@empline/preflight 1.1.33 → 1.1.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/checks/auth/client-only-protected-pages.d.ts +14 -0
- package/dist/checks/auth/client-only-protected-pages.d.ts.map +1 -0
- package/dist/checks/auth/client-only-protected-pages.js +182 -0
- package/dist/checks/auth/client-only-protected-pages.js.map +1 -0
- package/dist/checks/auth/empty-data-instead-of-redirect.d.ts +14 -0
- package/dist/checks/auth/empty-data-instead-of-redirect.d.ts.map +1 -0
- package/dist/checks/auth/empty-data-instead-of-redirect.js +200 -0
- package/dist/checks/auth/empty-data-instead-of-redirect.js.map +1 -0
- package/dist/checks/auth/store-id-fallback.d.ts +14 -0
- package/dist/checks/auth/store-id-fallback.d.ts.map +1 -0
- package/dist/checks/auth/store-id-fallback.js +217 -0
- package/dist/checks/auth/store-id-fallback.js.map +1 -0
- package/dist/checks/auth/store-page-auth-guard.d.ts +14 -0
- package/dist/checks/auth/store-page-auth-guard.d.ts.map +1 -0
- package/dist/checks/auth/store-page-auth-guard.js +207 -0
- package/dist/checks/auth/store-page-auth-guard.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export declare const id = "auth/client-only-protected-pages";
|
|
3
|
+
export declare const name = "Client Only Protected Pages";
|
|
4
|
+
export declare const description = "Detects protected pages marked 'use client' bypassing server auth";
|
|
5
|
+
export declare const category = "auth";
|
|
6
|
+
export declare const blocking = true;
|
|
7
|
+
export declare const tags: string[];
|
|
8
|
+
export declare const requires: string[];
|
|
9
|
+
export declare function run(): Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
errors: number;
|
|
12
|
+
warnings: number;
|
|
13
|
+
}>;
|
|
14
|
+
//# sourceMappingURL=client-only-protected-pages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-only-protected-pages.d.ts","sourceRoot":"","sources":["../../../src/checks/auth/client-only-protected-pages.ts"],"names":[],"mappings":";AA2BA,eAAO,MAAM,EAAE,qCAAqC,CAAC;AACrD,eAAO,MAAM,IAAI,gCAAgC,CAAC;AAClD,eAAO,MAAM,WAAW,sEAAsE,CAAC;AAC/F,eAAO,MAAM,QAAQ,SAAS,CAAC;AAC/B,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,IAAI,UAA8D,CAAC;AAChF,eAAO,MAAM,QAAQ,UAA0B,CAAC;AAgDhD,wBAAsB,GAAG,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmH3F"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.requires = exports.tags = exports.blocking = exports.category = exports.description = exports.name = exports.id = void 0;
|
|
8
|
+
exports.run = run;
|
|
9
|
+
/**
|
|
10
|
+
* Client Only Protected Pages Preflight
|
|
11
|
+
*
|
|
12
|
+
* Detects when pages in protected routes are marked "use client" which
|
|
13
|
+
* bypasses server-side authentication. This is a CRITICAL security issue.
|
|
14
|
+
*
|
|
15
|
+
* The problem:
|
|
16
|
+
* - Server Components can check auth before rendering
|
|
17
|
+
* - Client Components render first, THEN check auth
|
|
18
|
+
* - This creates a flash of protected content before redirect
|
|
19
|
+
* - Attacker can intercept the initial HTML with sensitive data
|
|
20
|
+
*
|
|
21
|
+
* DANGEROUS: 'use client' in app/store/[storeId]/page.tsx
|
|
22
|
+
* DANGEROUS: 'use client' in app/admin/page.tsx
|
|
23
|
+
* DANGEROUS: 'use client' in app/(protected)/page.tsx
|
|
24
|
+
*
|
|
25
|
+
* SAFE: Server Component that checks auth, then renders client child
|
|
26
|
+
* SAFE: Layout does auth check, page can be client component
|
|
27
|
+
*/
|
|
28
|
+
const fs_1 = __importDefault(require("fs"));
|
|
29
|
+
const path_1 = __importDefault(require("path"));
|
|
30
|
+
const glob_1 = require("glob");
|
|
31
|
+
const console_chars_1 = require("../../utils/console-chars");
|
|
32
|
+
// METADATA - Required for plugin loader discovery
|
|
33
|
+
exports.id = "auth/client-only-protected-pages";
|
|
34
|
+
exports.name = "Client Only Protected Pages";
|
|
35
|
+
exports.description = "Detects protected pages marked 'use client' bypassing server auth";
|
|
36
|
+
exports.category = "auth";
|
|
37
|
+
exports.blocking = true; // Critical security check
|
|
38
|
+
exports.tags = ["auth", "security", "client", "server", "rsc", "critical"];
|
|
39
|
+
exports.requires = ["trading-card-system"];
|
|
40
|
+
/**
|
|
41
|
+
* Protected route patterns that MUST have server-side auth
|
|
42
|
+
*/
|
|
43
|
+
const PROTECTED_ROUTE_PATTERNS = [
|
|
44
|
+
"app/store/**/page.tsx",
|
|
45
|
+
"app/admin/**/page.tsx",
|
|
46
|
+
"app/(protected)/**/page.tsx",
|
|
47
|
+
"app/(authenticated)/**/page.tsx",
|
|
48
|
+
"app/dashboard/**/page.tsx",
|
|
49
|
+
"app/settings/**/page.tsx",
|
|
50
|
+
"app/account/**/page.tsx",
|
|
51
|
+
"app/seller/**/page.tsx",
|
|
52
|
+
];
|
|
53
|
+
/**
|
|
54
|
+
* These files MUST check auth server-side in the route
|
|
55
|
+
*/
|
|
56
|
+
const LAYOUT_FILES = [
|
|
57
|
+
"app/store/layout.tsx",
|
|
58
|
+
"app/store/[storeId]/layout.tsx",
|
|
59
|
+
"app/admin/layout.tsx",
|
|
60
|
+
"app/(protected)/layout.tsx",
|
|
61
|
+
"app/dashboard/layout.tsx",
|
|
62
|
+
];
|
|
63
|
+
function hasUseClientDirective(content) {
|
|
64
|
+
// Check first 500 chars for 'use client' directive
|
|
65
|
+
const header = content.substring(0, 500);
|
|
66
|
+
return /['"]use client['"]/.test(header);
|
|
67
|
+
}
|
|
68
|
+
function hasServerAuthCheck(content) {
|
|
69
|
+
// Check for server-side auth patterns
|
|
70
|
+
return (/getServerSession|auth\s*\(\s*\)|requireAuth/.test(content) &&
|
|
71
|
+
/redirect|notFound|throw/.test(content));
|
|
72
|
+
}
|
|
73
|
+
async function run() {
|
|
74
|
+
console.log(`\n${console_chars_1.emoji.shield} CLIENT ONLY PROTECTED PAGES CHECK`);
|
|
75
|
+
console.log((0, console_chars_1.createDivider)(65, "heavy"));
|
|
76
|
+
const issues = [];
|
|
77
|
+
const filesChecked = [];
|
|
78
|
+
// First, check if protected route layouts have server auth
|
|
79
|
+
console.log(`\n${console_chars_1.emoji.search} Checking protected route layouts...`);
|
|
80
|
+
const layoutsWithAuth = new Set();
|
|
81
|
+
for (const layoutPattern of LAYOUT_FILES) {
|
|
82
|
+
const matches = await (0, glob_1.glob)(layoutPattern, { cwd: process.cwd() });
|
|
83
|
+
for (const relativePath of matches) {
|
|
84
|
+
const filePath = path_1.default.join(process.cwd(), relativePath);
|
|
85
|
+
if (!fs_1.default.existsSync(filePath))
|
|
86
|
+
continue;
|
|
87
|
+
const content = fs_1.default.readFileSync(filePath, "utf-8");
|
|
88
|
+
if (hasServerAuthCheck(content)) {
|
|
89
|
+
layoutsWithAuth.add(path_1.default.dirname(relativePath));
|
|
90
|
+
console.log(` ${console_chars_1.emoji.success} ${relativePath} has server-side auth`);
|
|
91
|
+
}
|
|
92
|
+
else if (!hasUseClientDirective(content)) {
|
|
93
|
+
// Server component layout without auth check
|
|
94
|
+
console.log(` ${console_chars_1.emoji.warning} ${relativePath} is server component but no auth check found`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Now check protected pages
|
|
99
|
+
console.log(`\n${console_chars_1.emoji.search} Checking protected pages...`);
|
|
100
|
+
const allFiles = [];
|
|
101
|
+
for (const pattern of PROTECTED_ROUTE_PATTERNS) {
|
|
102
|
+
const matches = await (0, glob_1.glob)(pattern, { cwd: process.cwd() });
|
|
103
|
+
allFiles.push(...matches);
|
|
104
|
+
}
|
|
105
|
+
const uniqueFiles = [...new Set(allFiles)];
|
|
106
|
+
for (const relativePath of uniqueFiles) {
|
|
107
|
+
const filePath = path_1.default.join(process.cwd(), relativePath);
|
|
108
|
+
if (!fs_1.default.existsSync(filePath))
|
|
109
|
+
continue;
|
|
110
|
+
const content = fs_1.default.readFileSync(filePath, "utf-8");
|
|
111
|
+
filesChecked.push(relativePath);
|
|
112
|
+
const isClientComponent = hasUseClientDirective(content);
|
|
113
|
+
const pageDir = path_1.default.dirname(relativePath);
|
|
114
|
+
// Check if this page's parent layout has auth
|
|
115
|
+
const hasParentLayoutAuth = [...layoutsWithAuth].some((layoutDir) => pageDir.startsWith(layoutDir));
|
|
116
|
+
if (isClientComponent) {
|
|
117
|
+
if (!hasParentLayoutAuth) {
|
|
118
|
+
issues.push({
|
|
119
|
+
file: relativePath,
|
|
120
|
+
type: "client-protected-page",
|
|
121
|
+
description: `Protected page uses 'use client' without server auth in parent layout`,
|
|
122
|
+
critical: true,
|
|
123
|
+
});
|
|
124
|
+
console.log(` ${console_chars_1.emoji.error} ${relativePath}: Client component in protected route without parent layout auth`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
console.log(` ${console_chars_1.emoji.success} ${relativePath}: Client component but parent layout has auth`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// Server component - check if it has its own auth
|
|
132
|
+
if (hasServerAuthCheck(content)) {
|
|
133
|
+
console.log(` ${console_chars_1.emoji.success} ${relativePath}: Server component with auth check`);
|
|
134
|
+
}
|
|
135
|
+
else if (hasParentLayoutAuth) {
|
|
136
|
+
console.log(` ${console_chars_1.emoji.success} ${relativePath}: Server component, parent layout has auth`);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
console.log(` ${console_chars_1.emoji.warning} ${relativePath}: Server component but no visible auth check`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Summary
|
|
144
|
+
const criticalIssues = issues.filter((i) => i.critical);
|
|
145
|
+
console.log(`\n${console_chars_1.emoji.chart} Summary:`);
|
|
146
|
+
console.log(` Protected pages checked: ${filesChecked.length}`);
|
|
147
|
+
console.log(` Layouts with server auth: ${layoutsWithAuth.size}`);
|
|
148
|
+
console.log(` Security issues: ${criticalIssues.length}`);
|
|
149
|
+
if (issues.length === 0) {
|
|
150
|
+
console.log(`\n${console_chars_1.emoji.success} CLIENT ONLY PROTECTED PAGES CHECK PASSED`);
|
|
151
|
+
console.log(`\nAll protected pages have proper server-side authentication.`);
|
|
152
|
+
return { success: true, errors: 0, warnings: 0 };
|
|
153
|
+
}
|
|
154
|
+
console.log(`\n${console_chars_1.emoji.error} Security issues found:`);
|
|
155
|
+
for (const issue of issues) {
|
|
156
|
+
console.log(` ${issue.file}: ${issue.description}`);
|
|
157
|
+
}
|
|
158
|
+
console.log(`\n${console_chars_1.emoji.info} To fix client-only protected pages:`);
|
|
159
|
+
console.log(` Option 1: Remove 'use client' and do auth in the page itself`);
|
|
160
|
+
console.log(` Option 2: Add auth check to parent layout.tsx (recommended)`);
|
|
161
|
+
console.log(` Option 3: Create a Server Component wrapper that checks auth`);
|
|
162
|
+
console.log(`\n Layout auth pattern:`);
|
|
163
|
+
console.log(` export default async function Layout({children}) {`);
|
|
164
|
+
console.log(` const session = await auth();`);
|
|
165
|
+
console.log(` if (!session) redirect('/login');`);
|
|
166
|
+
console.log(` return children;`);
|
|
167
|
+
console.log(` }`);
|
|
168
|
+
console.log(`\n${console_chars_1.emoji.error} CLIENT ONLY PROTECTED PAGES CHECK FAILED`);
|
|
169
|
+
return { success: false, errors: criticalIssues.length, warnings: 0 };
|
|
170
|
+
}
|
|
171
|
+
// Allow direct execution
|
|
172
|
+
if (require.main === module) {
|
|
173
|
+
run()
|
|
174
|
+
.then((result) => {
|
|
175
|
+
process.exit(result.success ? 0 : 1);
|
|
176
|
+
})
|
|
177
|
+
.catch((err) => {
|
|
178
|
+
console.error(`${console_chars_1.emoji.error} Preflight failed:`, err);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=client-only-protected-pages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-only-protected-pages.js","sourceRoot":"","sources":["../../../src/checks/auth/client-only-protected-pages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export declare const id = "auth/empty-data-instead-of-redirect";
|
|
3
|
+
export declare const name = "Empty Data Instead of Redirect";
|
|
4
|
+
export declare const description = "Detects pages returning empty data instead of redirecting on access failure";
|
|
5
|
+
export declare const category = "auth";
|
|
6
|
+
export declare const blocking = true;
|
|
7
|
+
export declare const tags: string[];
|
|
8
|
+
export declare const requires: string[];
|
|
9
|
+
export declare function run(): Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
errors: number;
|
|
12
|
+
warnings: number;
|
|
13
|
+
}>;
|
|
14
|
+
//# sourceMappingURL=empty-data-instead-of-redirect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"empty-data-instead-of-redirect.d.ts","sourceRoot":"","sources":["../../../src/checks/auth/empty-data-instead-of-redirect.ts"],"names":[],"mappings":";AA4BA,eAAO,MAAM,EAAE,wCAAwC,CAAC;AACxD,eAAO,MAAM,IAAI,mCAAmC,CAAC;AACrD,eAAO,MAAM,WAAW,gFAAgF,CAAC;AACzG,eAAO,MAAM,QAAQ,SAAS,CAAC;AAC/B,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,IAAI,UAAqD,CAAC;AACvE,eAAO,MAAM,QAAQ,UAA0B,CAAC;AAiFhD,wBAAsB,GAAG,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAuG3F"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.requires = exports.tags = exports.blocking = exports.category = exports.description = exports.name = exports.id = void 0;
|
|
8
|
+
exports.run = run;
|
|
9
|
+
/**
|
|
10
|
+
* Empty Data Instead of Redirect Preflight
|
|
11
|
+
*
|
|
12
|
+
* Detects when pages return empty arrays/objects instead of redirecting
|
|
13
|
+
* on access failure. This is a security anti-pattern because:
|
|
14
|
+
*
|
|
15
|
+
* BAD: if (!hasAccess) return { data: [] } // Hides access failure
|
|
16
|
+
* BAD: if (!session) return [] // Silent auth failure
|
|
17
|
+
* BAD: if (!store) return null // Doesn't tell user they need to login
|
|
18
|
+
*
|
|
19
|
+
* GOOD: if (!hasAccess) redirect('/unauthorized')
|
|
20
|
+
* GOOD: if (!session) redirect('/login')
|
|
21
|
+
* GOOD: if (!store) notFound()
|
|
22
|
+
*
|
|
23
|
+
* Problems with returning empty data:
|
|
24
|
+
* - User sees blank page with no explanation
|
|
25
|
+
* - Masks authorization failures
|
|
26
|
+
* - Makes debugging harder
|
|
27
|
+
* - May expose that resource exists (information leak)
|
|
28
|
+
*/
|
|
29
|
+
const fs_1 = __importDefault(require("fs"));
|
|
30
|
+
const path_1 = __importDefault(require("path"));
|
|
31
|
+
const glob_1 = require("glob");
|
|
32
|
+
const console_chars_1 = require("../../utils/console-chars");
|
|
33
|
+
// METADATA - Required for plugin loader discovery
|
|
34
|
+
exports.id = "auth/empty-data-instead-of-redirect";
|
|
35
|
+
exports.name = "Empty Data Instead of Redirect";
|
|
36
|
+
exports.description = "Detects pages returning empty data instead of redirecting on access failure";
|
|
37
|
+
exports.category = "auth";
|
|
38
|
+
exports.blocking = true;
|
|
39
|
+
exports.tags = ["auth", "security", "redirect", "ux", "critical"];
|
|
40
|
+
exports.requires = ["trading-card-system"];
|
|
41
|
+
/**
|
|
42
|
+
* File patterns to check
|
|
43
|
+
*/
|
|
44
|
+
const FILE_PATTERNS = [
|
|
45
|
+
"app/**/page.tsx",
|
|
46
|
+
"app/**/layout.tsx",
|
|
47
|
+
"app/api/**/route.ts",
|
|
48
|
+
];
|
|
49
|
+
/**
|
|
50
|
+
* Dangerous patterns - returning empty on auth/access failure
|
|
51
|
+
*/
|
|
52
|
+
const DANGEROUS_PATTERNS = {
|
|
53
|
+
// Return empty array on !session
|
|
54
|
+
emptyArrayNoSession: {
|
|
55
|
+
pattern: /if\s*\(\s*!session\s*\)[^{]*return\s*(?:\[\s*\]|\{\s*\}|null|undefined)/,
|
|
56
|
+
description: "Returns empty data when session missing (should redirect to login)",
|
|
57
|
+
critical: true,
|
|
58
|
+
},
|
|
59
|
+
// Return empty array on !hasAccess
|
|
60
|
+
emptyArrayNoAccess: {
|
|
61
|
+
pattern: /if\s*\(\s*!(?:hasAccess|canAccess|isAuthorized|hasPermission)\s*\)[^{]*return\s*(?:\[\s*\]|\{\s*\}|null)/,
|
|
62
|
+
description: "Returns empty data on access failure (should redirect or show error)",
|
|
63
|
+
critical: true,
|
|
64
|
+
},
|
|
65
|
+
// Return empty on !store
|
|
66
|
+
emptyArrayNoStore: {
|
|
67
|
+
pattern: /if\s*\(\s*!store\s*\)[^{]*return\s*(?:\[\s*\]|\{\s*\}|null)/,
|
|
68
|
+
description: "Returns empty data when store not found (should use notFound())",
|
|
69
|
+
critical: true,
|
|
70
|
+
},
|
|
71
|
+
// Return empty data prop
|
|
72
|
+
emptyDataProp: {
|
|
73
|
+
pattern: /return\s*\{[^}]*(?:data|items|results):\s*\[\s*\][^}]*\}(?:(?!redirect|notFound).)*$/m,
|
|
74
|
+
description: "Returns object with empty data array (may mask access failure)",
|
|
75
|
+
critical: false,
|
|
76
|
+
},
|
|
77
|
+
// Early return empty in RSC
|
|
78
|
+
earlyReturnEmpty: {
|
|
79
|
+
pattern: /(?:async\s+)?function\s+(?:Page|Layout)\s*\([^)]*\)[^{]*\{[^}]*if\s*\([^)]*\)\s*return\s*(?:null|\[\]|\{\})/s,
|
|
80
|
+
description: "Page/Layout returns empty early (should redirect instead)",
|
|
81
|
+
critical: false,
|
|
82
|
+
},
|
|
83
|
+
// Return NextResponse with empty body on auth failure
|
|
84
|
+
nextResponseEmpty: {
|
|
85
|
+
pattern: /if\s*\(\s*!(?:session|auth|user)\s*\)[^{]*return\s*NextResponse\.json\s*\(\s*(?:\[\]|\{\}|null)\s*\)/,
|
|
86
|
+
description: "API returns empty response on auth failure (should return 401)",
|
|
87
|
+
critical: true,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Safe patterns that indicate proper handling
|
|
92
|
+
*/
|
|
93
|
+
const SAFE_PATTERNS = [
|
|
94
|
+
// Redirect on failure
|
|
95
|
+
/if\s*\(\s*!session\s*\)[^{]*redirect/,
|
|
96
|
+
/if\s*\(\s*!hasAccess\s*\)[^{]*redirect/,
|
|
97
|
+
// notFound() call
|
|
98
|
+
/if\s*\(\s*!(?:store|data|item)\s*\)[^{]*notFound\s*\(\)/,
|
|
99
|
+
// Proper error response
|
|
100
|
+
/if\s*\(\s*!session\s*\)[^{]*return\s*(?:NextResponse\.json\s*\([^)]*,\s*\{\s*status:\s*401|new\s*Response\s*\([^)]*,\s*\{\s*status:\s*401)/,
|
|
101
|
+
// Throw error
|
|
102
|
+
/if\s*\(\s*!(?:session|hasAccess)\s*\)[^{]*throw/,
|
|
103
|
+
];
|
|
104
|
+
function getLineNumber(content, position) {
|
|
105
|
+
return content.substring(0, position).split("\n").length;
|
|
106
|
+
}
|
|
107
|
+
async function run() {
|
|
108
|
+
console.log(`\n${console_chars_1.emoji.shield} EMPTY DATA INSTEAD OF REDIRECT CHECK`);
|
|
109
|
+
console.log((0, console_chars_1.createDivider)(65, "heavy"));
|
|
110
|
+
const issues = [];
|
|
111
|
+
const filesChecked = [];
|
|
112
|
+
// Find files
|
|
113
|
+
const allFiles = [];
|
|
114
|
+
for (const pattern of FILE_PATTERNS) {
|
|
115
|
+
const matches = await (0, glob_1.glob)(pattern, { cwd: process.cwd() });
|
|
116
|
+
allFiles.push(...matches);
|
|
117
|
+
}
|
|
118
|
+
const uniqueFiles = [...new Set(allFiles)];
|
|
119
|
+
console.log(`\n${console_chars_1.emoji.search} Scanning ${uniqueFiles.length} page/route files...`);
|
|
120
|
+
for (const relativePath of uniqueFiles) {
|
|
121
|
+
const filePath = path_1.default.join(process.cwd(), relativePath);
|
|
122
|
+
if (!fs_1.default.existsSync(filePath))
|
|
123
|
+
continue;
|
|
124
|
+
const content = fs_1.default.readFileSync(filePath, "utf-8");
|
|
125
|
+
filesChecked.push(relativePath);
|
|
126
|
+
// Skip if file has proper redirect/notFound handling
|
|
127
|
+
const hasSafePattern = SAFE_PATTERNS.some((pattern) => pattern.test(content));
|
|
128
|
+
// Check for dangerous patterns
|
|
129
|
+
for (const [name, check] of Object.entries(DANGEROUS_PATTERNS)) {
|
|
130
|
+
const regex = new RegExp(check.pattern.source, "g");
|
|
131
|
+
let match;
|
|
132
|
+
while ((match = regex.exec(content)) !== null) {
|
|
133
|
+
// Check if there's a safe pattern nearby
|
|
134
|
+
const nearbyCode = content.substring(Math.max(0, match.index - 100), Math.min(content.length, match.index + 300));
|
|
135
|
+
if (SAFE_PATTERNS.some((pattern) => pattern.test(nearbyCode))) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const lineNum = getLineNumber(content, match.index);
|
|
139
|
+
issues.push({
|
|
140
|
+
file: relativePath,
|
|
141
|
+
type: "empty-data-return",
|
|
142
|
+
pattern: name,
|
|
143
|
+
description: check.description,
|
|
144
|
+
critical: check.critical,
|
|
145
|
+
line: lineNum,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Summary
|
|
151
|
+
const criticalIssues = issues.filter((i) => i.critical);
|
|
152
|
+
const warningIssues = issues.filter((i) => !i.critical);
|
|
153
|
+
console.log(`\n${console_chars_1.emoji.chart} Summary:`);
|
|
154
|
+
console.log(` Files checked: ${filesChecked.length}`);
|
|
155
|
+
console.log(` Critical issues: ${criticalIssues.length}`);
|
|
156
|
+
console.log(` Warning issues: ${warningIssues.length}`);
|
|
157
|
+
if (issues.length === 0) {
|
|
158
|
+
console.log(`\n${console_chars_1.emoji.success} EMPTY DATA INSTEAD OF REDIRECT CHECK PASSED`);
|
|
159
|
+
console.log(`\nAll pages properly redirect or show errors on access failure.`);
|
|
160
|
+
return { success: true, errors: 0, warnings: 0 };
|
|
161
|
+
}
|
|
162
|
+
// Group by file
|
|
163
|
+
const issuesByFile = new Map();
|
|
164
|
+
for (const issue of issues) {
|
|
165
|
+
const existing = issuesByFile.get(issue.file) || [];
|
|
166
|
+
existing.push(issue);
|
|
167
|
+
issuesByFile.set(issue.file, existing);
|
|
168
|
+
}
|
|
169
|
+
console.log(`\n${console_chars_1.emoji.error} Issues found:`);
|
|
170
|
+
for (const [file, fileIssues] of issuesByFile) {
|
|
171
|
+
console.log(`\n ${file}:`);
|
|
172
|
+
for (const issue of fileIssues) {
|
|
173
|
+
const icon = issue.critical ? console_chars_1.emoji.error : console_chars_1.emoji.warning;
|
|
174
|
+
console.log(` ${icon} Line ${issue.line}: ${issue.description}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
console.log(`\n${console_chars_1.emoji.info} To fix empty data returns:`);
|
|
178
|
+
console.log(` 1. On missing session: redirect('/login') or redirect('/auth/signin')`);
|
|
179
|
+
console.log(` 2. On access denied: redirect('/unauthorized') or return 403`);
|
|
180
|
+
console.log(` 3. On missing resource: notFound() to show 404 page`);
|
|
181
|
+
console.log(` 4. API routes: return NextResponse.json({error}, {status: 401/403})`);
|
|
182
|
+
if (criticalIssues.length > 0) {
|
|
183
|
+
console.log(`\n${console_chars_1.emoji.error} EMPTY DATA INSTEAD OF REDIRECT CHECK FAILED`);
|
|
184
|
+
return { success: false, errors: criticalIssues.length, warnings: warningIssues.length };
|
|
185
|
+
}
|
|
186
|
+
console.log(`\n${console_chars_1.emoji.warning} EMPTY DATA INSTEAD OF REDIRECT CHECK PASSED WITH WARNINGS`);
|
|
187
|
+
return { success: true, errors: 0, warnings: warningIssues.length };
|
|
188
|
+
}
|
|
189
|
+
// Allow direct execution
|
|
190
|
+
if (require.main === module) {
|
|
191
|
+
run()
|
|
192
|
+
.then((result) => {
|
|
193
|
+
process.exit(result.success ? 0 : 1);
|
|
194
|
+
})
|
|
195
|
+
.catch((err) => {
|
|
196
|
+
console.error(`${console_chars_1.emoji.error} Preflight failed:`, err);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=empty-data-instead-of-redirect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"empty-data-instead-of-redirect.js","sourceRoot":"","sources":["../../../src/checks/auth/empty-data-instead-of-redirect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export declare const id = "auth/store-id-fallback";
|
|
3
|
+
export declare const name = "Store ID Fallback Detection";
|
|
4
|
+
export declare const description = "Detects risky patterns where storeId falls back to userId";
|
|
5
|
+
export declare const category = "auth";
|
|
6
|
+
export declare const blocking = true;
|
|
7
|
+
export declare const tags: string[];
|
|
8
|
+
export declare const requires: string[];
|
|
9
|
+
export declare function run(): Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
errors: number;
|
|
12
|
+
warnings: number;
|
|
13
|
+
}>;
|
|
14
|
+
//# sourceMappingURL=store-id-fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-id-fallback.d.ts","sourceRoot":"","sources":["../../../src/checks/auth/store-id-fallback.ts"],"names":[],"mappings":";AA4BA,eAAO,MAAM,EAAE,2BAA2B,CAAC;AAC3C,eAAO,MAAM,IAAI,gCAAgC,CAAC;AAClD,eAAO,MAAM,WAAW,8DAA8D,CAAC;AACvF,eAAO,MAAM,QAAQ,SAAS,CAAC;AAC/B,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,IAAI,UAAwD,CAAC;AAC1E,eAAO,MAAM,QAAQ,UAA0B,CAAC;AA4FhD,wBAAsB,GAAG,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgH3F"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.requires = exports.tags = exports.blocking = exports.category = exports.description = exports.name = exports.id = void 0;
|
|
8
|
+
exports.run = run;
|
|
9
|
+
/**
|
|
10
|
+
* Store ID Fallback Preflight
|
|
11
|
+
*
|
|
12
|
+
* Detects risky patterns where storeId falls back to userId or other values.
|
|
13
|
+
* This pattern can cause security issues and data leakage:
|
|
14
|
+
*
|
|
15
|
+
* DANGEROUS: storeId || userId - Falls back to userId if storeId missing
|
|
16
|
+
* DANGEROUS: storeId ?? session.user.id - Silently uses userId as storeId
|
|
17
|
+
* DANGEROUS: params.storeId || defaultStoreId - May expose wrong store
|
|
18
|
+
*
|
|
19
|
+
* These patterns often indicate:
|
|
20
|
+
* - Missing store context in the request
|
|
21
|
+
* - Assumption that userId === storeId (only true for store owners)
|
|
22
|
+
* - Logic errors that expose other users' stores
|
|
23
|
+
*
|
|
24
|
+
* Correct patterns:
|
|
25
|
+
* - Always require explicit storeId
|
|
26
|
+
* - Validate storeId against user's accessible stores
|
|
27
|
+
* - Return error if storeId is missing, don't fallback
|
|
28
|
+
*/
|
|
29
|
+
const fs_1 = __importDefault(require("fs"));
|
|
30
|
+
const path_1 = __importDefault(require("path"));
|
|
31
|
+
const glob_1 = require("glob");
|
|
32
|
+
const console_chars_1 = require("../../utils/console-chars");
|
|
33
|
+
// METADATA - Required for plugin loader discovery
|
|
34
|
+
exports.id = "auth/store-id-fallback";
|
|
35
|
+
exports.name = "Store ID Fallback Detection";
|
|
36
|
+
exports.description = "Detects risky patterns where storeId falls back to userId";
|
|
37
|
+
exports.category = "auth";
|
|
38
|
+
exports.blocking = true; // Critical security check
|
|
39
|
+
exports.tags = ["auth", "security", "store", "fallback", "critical"];
|
|
40
|
+
exports.requires = ["trading-card-system"];
|
|
41
|
+
/**
|
|
42
|
+
* File patterns to check
|
|
43
|
+
*/
|
|
44
|
+
const FILE_PATTERNS = [
|
|
45
|
+
"app/store/**/*.tsx",
|
|
46
|
+
"app/store/**/*.ts",
|
|
47
|
+
"app/api/store/**/*.ts",
|
|
48
|
+
"app/(store)/**/*.tsx",
|
|
49
|
+
"app/(store)/**/*.ts",
|
|
50
|
+
"lib/store*.ts",
|
|
51
|
+
"hooks/useStore*.ts",
|
|
52
|
+
"hooks/useStore*.tsx",
|
|
53
|
+
];
|
|
54
|
+
/**
|
|
55
|
+
* Dangerous fallback patterns
|
|
56
|
+
*/
|
|
57
|
+
const DANGEROUS_PATTERNS = {
|
|
58
|
+
// storeId || userId fallback
|
|
59
|
+
storeIdOrUserId: {
|
|
60
|
+
pattern: /storeId\s*\|\|\s*(?:userId|session\.user\.id|user\.id)/,
|
|
61
|
+
description: "storeId falls back to userId (dangerous - userId !== storeId for non-owners)",
|
|
62
|
+
critical: true,
|
|
63
|
+
},
|
|
64
|
+
// storeId ?? userId fallback
|
|
65
|
+
storeIdNullishUserId: {
|
|
66
|
+
pattern: /storeId\s*\?\?\s*(?:userId|session\.user\.id|user\.id)/,
|
|
67
|
+
description: "storeId nullish coalesces to userId",
|
|
68
|
+
critical: true,
|
|
69
|
+
},
|
|
70
|
+
// params.storeId || default
|
|
71
|
+
paramsStoreIdFallback: {
|
|
72
|
+
pattern: /params\.storeId\s*\|\|\s*(?!null|undefined|throw|redirect)/,
|
|
73
|
+
description: "params.storeId falls back to another value instead of erroring",
|
|
74
|
+
critical: true,
|
|
75
|
+
},
|
|
76
|
+
// Default storeId assignment
|
|
77
|
+
defaultStoreId: {
|
|
78
|
+
pattern: /storeId\s*=\s*storeId\s*\|\|\s*|storeId\s*\?\?=\s*(?!null)/,
|
|
79
|
+
description: "storeId assigned a default value instead of requiring explicit value",
|
|
80
|
+
critical: true,
|
|
81
|
+
},
|
|
82
|
+
// userId as storeId directly
|
|
83
|
+
userIdAsStoreId: {
|
|
84
|
+
pattern: /storeId:\s*(?:userId|session\.user\.id|user\.id)(?!\s*\|\||[^,}\s])/,
|
|
85
|
+
description: "userId used directly as storeId (only valid for store owners)",
|
|
86
|
+
critical: false, // Warning - might be intentional for owner operations
|
|
87
|
+
},
|
|
88
|
+
// Ternary fallback to userId
|
|
89
|
+
ternaryFallbackUserId: {
|
|
90
|
+
pattern: /storeId\s*\?\s*storeId\s*:\s*(?:userId|session\.user\.id)/,
|
|
91
|
+
description: "Ternary expression falls back to userId when storeId missing",
|
|
92
|
+
critical: true,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Acceptable patterns (used for context - don't flag these)
|
|
97
|
+
*/
|
|
98
|
+
const SAFE_PATTERNS = [
|
|
99
|
+
// Explicit error on missing storeId
|
|
100
|
+
/if\s*\(\s*!storeId\s*\)\s*(?:throw|return.*error|redirect)/,
|
|
101
|
+
// Required storeId validation
|
|
102
|
+
/storeId.*required|require.*storeId/i,
|
|
103
|
+
// Lookup user's default store (intentional)
|
|
104
|
+
/getUserDefaultStore|getDefaultStoreForUser/i,
|
|
105
|
+
];
|
|
106
|
+
function getLineNumber(content, position) {
|
|
107
|
+
return content.substring(0, position).split("\n").length;
|
|
108
|
+
}
|
|
109
|
+
function getCodeContext(content, position) {
|
|
110
|
+
const lines = content.split("\n");
|
|
111
|
+
const lineNum = getLineNumber(content, position);
|
|
112
|
+
const start = Math.max(0, lineNum - 2);
|
|
113
|
+
const end = Math.min(lines.length, lineNum + 1);
|
|
114
|
+
return lines.slice(start, end).join("\n").trim();
|
|
115
|
+
}
|
|
116
|
+
async function run() {
|
|
117
|
+
console.log(`\n${console_chars_1.emoji.shield} STORE ID FALLBACK DETECTION`);
|
|
118
|
+
console.log((0, console_chars_1.createDivider)(65, "heavy"));
|
|
119
|
+
const issues = [];
|
|
120
|
+
const filesChecked = [];
|
|
121
|
+
// Find files
|
|
122
|
+
const allFiles = [];
|
|
123
|
+
for (const pattern of FILE_PATTERNS) {
|
|
124
|
+
const matches = await (0, glob_1.glob)(pattern, { cwd: process.cwd() });
|
|
125
|
+
allFiles.push(...matches);
|
|
126
|
+
}
|
|
127
|
+
const uniqueFiles = [...new Set(allFiles)];
|
|
128
|
+
console.log(`\n${console_chars_1.emoji.search} Scanning ${uniqueFiles.length} store-related files...`);
|
|
129
|
+
if (uniqueFiles.length === 0) {
|
|
130
|
+
console.log(`\n${console_chars_1.emoji.warning} No store files found - skipping check`);
|
|
131
|
+
return { success: true, errors: 0, warnings: 1 };
|
|
132
|
+
}
|
|
133
|
+
for (const relativePath of uniqueFiles) {
|
|
134
|
+
const filePath = path_1.default.join(process.cwd(), relativePath);
|
|
135
|
+
if (!fs_1.default.existsSync(filePath))
|
|
136
|
+
continue;
|
|
137
|
+
const content = fs_1.default.readFileSync(filePath, "utf-8");
|
|
138
|
+
filesChecked.push(relativePath);
|
|
139
|
+
// Skip if file has safe patterns indicating proper handling
|
|
140
|
+
const hasSafePattern = SAFE_PATTERNS.some((pattern) => pattern.test(content));
|
|
141
|
+
// Check for dangerous patterns
|
|
142
|
+
for (const [name, check] of Object.entries(DANGEROUS_PATTERNS)) {
|
|
143
|
+
const regex = new RegExp(check.pattern.source, "g");
|
|
144
|
+
let match;
|
|
145
|
+
while ((match = regex.exec(content)) !== null) {
|
|
146
|
+
// Skip if nearby code has safe pattern
|
|
147
|
+
const nearbyCode = content.substring(Math.max(0, match.index - 200), Math.min(content.length, match.index + 200));
|
|
148
|
+
if (SAFE_PATTERNS.some((pattern) => pattern.test(nearbyCode))) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const lineNum = getLineNumber(content, match.index);
|
|
152
|
+
issues.push({
|
|
153
|
+
file: relativePath,
|
|
154
|
+
type: "dangerous-fallback",
|
|
155
|
+
pattern: name,
|
|
156
|
+
description: check.description,
|
|
157
|
+
critical: check.critical,
|
|
158
|
+
line: lineNum,
|
|
159
|
+
code: getCodeContext(content, match.index),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Summary
|
|
165
|
+
const criticalIssues = issues.filter((i) => i.critical);
|
|
166
|
+
const warningIssues = issues.filter((i) => !i.critical);
|
|
167
|
+
console.log(`\n${console_chars_1.emoji.chart} Summary:`);
|
|
168
|
+
console.log(` Files checked: ${filesChecked.length}`);
|
|
169
|
+
console.log(` Critical issues: ${criticalIssues.length}`);
|
|
170
|
+
console.log(` Warning issues: ${warningIssues.length}`);
|
|
171
|
+
if (issues.length === 0) {
|
|
172
|
+
console.log(`\n${console_chars_1.emoji.success} STORE ID FALLBACK DETECTION PASSED`);
|
|
173
|
+
console.log(`\nNo dangerous storeId fallback patterns found.`);
|
|
174
|
+
return { success: true, errors: 0, warnings: 0 };
|
|
175
|
+
}
|
|
176
|
+
// Display issues
|
|
177
|
+
if (criticalIssues.length > 0) {
|
|
178
|
+
console.log(`\n${console_chars_1.emoji.error} Critical issues:`);
|
|
179
|
+
for (const issue of criticalIssues) {
|
|
180
|
+
console.log(`\n ${issue.file}:${issue.line}`);
|
|
181
|
+
console.log(` ${issue.description}`);
|
|
182
|
+
if (issue.code) {
|
|
183
|
+
const indented = issue.code.split("\n").map((l) => ` ${l}`).join("\n");
|
|
184
|
+
console.log(indented);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (warningIssues.length > 0) {
|
|
189
|
+
console.log(`\n${console_chars_1.emoji.warning} Warning issues:`);
|
|
190
|
+
for (const issue of warningIssues) {
|
|
191
|
+
console.log(` ${issue.file}:${issue.line}: ${issue.description}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
console.log(`\n${console_chars_1.emoji.info} To fix storeId fallback issues:`);
|
|
195
|
+
console.log(` 1. Never use: storeId || userId (userId !== storeId for delegated users)`);
|
|
196
|
+
console.log(` 2. Always require explicit storeId from route params or context`);
|
|
197
|
+
console.log(` 3. Validate storeId against user's accessible stores`);
|
|
198
|
+
console.log(` 4. Return error/redirect if storeId is missing, don't fallback`);
|
|
199
|
+
if (criticalIssues.length > 0) {
|
|
200
|
+
console.log(`\n${console_chars_1.emoji.error} STORE ID FALLBACK DETECTION FAILED`);
|
|
201
|
+
return { success: false, errors: criticalIssues.length, warnings: warningIssues.length };
|
|
202
|
+
}
|
|
203
|
+
console.log(`\n${console_chars_1.emoji.warning} STORE ID FALLBACK DETECTION PASSED WITH WARNINGS`);
|
|
204
|
+
return { success: true, errors: 0, warnings: warningIssues.length };
|
|
205
|
+
}
|
|
206
|
+
// Allow direct execution
|
|
207
|
+
if (require.main === module) {
|
|
208
|
+
run()
|
|
209
|
+
.then((result) => {
|
|
210
|
+
process.exit(result.success ? 0 : 1);
|
|
211
|
+
})
|
|
212
|
+
.catch((err) => {
|
|
213
|
+
console.error(`${console_chars_1.emoji.error} Preflight failed:`, err);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=store-id-fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-id-fallback.js","sourceRoot":"","sources":["../../../src/checks/auth/store-id-fallback.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export declare const id = "auth/store-page-auth-guard";
|
|
3
|
+
export declare const name = "Store Page Auth Guard";
|
|
4
|
+
export declare const description = "Detects store pages missing auth or store access verification";
|
|
5
|
+
export declare const category = "auth";
|
|
6
|
+
export declare const blocking = true;
|
|
7
|
+
export declare const tags: string[];
|
|
8
|
+
export declare const requires: string[];
|
|
9
|
+
export declare function run(): Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
errors: number;
|
|
12
|
+
warnings: number;
|
|
13
|
+
}>;
|
|
14
|
+
//# sourceMappingURL=store-page-auth-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-page-auth-guard.d.ts","sourceRoot":"","sources":["../../../src/checks/auth/store-page-auth-guard.ts"],"names":[],"mappings":";AA2BA,eAAO,MAAM,EAAE,+BAA+B,CAAC;AAC/C,eAAO,MAAM,IAAI,0BAA0B,CAAC;AAC5C,eAAO,MAAM,WAAW,kEAAkE,CAAC;AAC3F,eAAO,MAAM,QAAQ,SAAS,CAAC;AAC/B,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,IAAI,UAA8D,CAAC;AAChF,eAAO,MAAM,QAAQ,UAA0B,CAAC;AAgFhD,wBAAsB,GAAG,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA0G3F"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.requires = exports.tags = exports.blocking = exports.category = exports.description = exports.name = exports.id = void 0;
|
|
8
|
+
exports.run = run;
|
|
9
|
+
/**
|
|
10
|
+
* Store Page Auth Guard Preflight
|
|
11
|
+
*
|
|
12
|
+
* Detects store pages that are missing authentication or store access verification.
|
|
13
|
+
* This is CRITICAL for security - store pages must verify:
|
|
14
|
+
* 1. User is authenticated (session exists)
|
|
15
|
+
* 2. User has access to the specific store (ownership or delegation)
|
|
16
|
+
* 3. Store exists and is not deleted
|
|
17
|
+
*
|
|
18
|
+
* Common vulnerable patterns:
|
|
19
|
+
* - Store pages without getServerSession or auth() call
|
|
20
|
+
* - Store API routes without checkPermission or store access check
|
|
21
|
+
* - Pages that fetch store data without verifying user access
|
|
22
|
+
*
|
|
23
|
+
* Prevents:
|
|
24
|
+
* - Unauthorized access to store data
|
|
25
|
+
* - Store data leakage between users
|
|
26
|
+
* - IDOR vulnerabilities
|
|
27
|
+
*/
|
|
28
|
+
const fs_1 = __importDefault(require("fs"));
|
|
29
|
+
const path_1 = __importDefault(require("path"));
|
|
30
|
+
const glob_1 = require("glob");
|
|
31
|
+
const console_chars_1 = require("../../utils/console-chars");
|
|
32
|
+
// METADATA - Required for plugin loader discovery
|
|
33
|
+
exports.id = "auth/store-page-auth-guard";
|
|
34
|
+
exports.name = "Store Page Auth Guard";
|
|
35
|
+
exports.description = "Detects store pages missing auth or store access verification";
|
|
36
|
+
exports.category = "auth";
|
|
37
|
+
exports.blocking = true; // Critical security check
|
|
38
|
+
exports.tags = ["auth", "security", "store", "access-control", "critical"];
|
|
39
|
+
exports.requires = ["trading-card-system"];
|
|
40
|
+
/**
|
|
41
|
+
* Store page patterns to check
|
|
42
|
+
*/
|
|
43
|
+
const STORE_PAGE_PATTERNS = [
|
|
44
|
+
"app/store/**/page.tsx",
|
|
45
|
+
"app/store/**/layout.tsx",
|
|
46
|
+
"app/api/store/**/route.ts",
|
|
47
|
+
"app/(store)/**/page.tsx",
|
|
48
|
+
"app/(store)/**/layout.tsx",
|
|
49
|
+
];
|
|
50
|
+
/**
|
|
51
|
+
* Required auth patterns for store pages
|
|
52
|
+
*/
|
|
53
|
+
const REQUIRED_AUTH_PATTERNS = {
|
|
54
|
+
// Session/auth check
|
|
55
|
+
sessionCheck: {
|
|
56
|
+
pattern: /getServerSession|auth\s*\(\s*\)|requireAuth|getSession/,
|
|
57
|
+
description: "Session/authentication check",
|
|
58
|
+
critical: true,
|
|
59
|
+
},
|
|
60
|
+
// Store access verification
|
|
61
|
+
storeAccessCheck: {
|
|
62
|
+
pattern: /checkPermission|getUserStoreAccess|verifyStoreAccess|storeId.*session|hasStoreAccess/i,
|
|
63
|
+
description: "Store access verification",
|
|
64
|
+
critical: true,
|
|
65
|
+
},
|
|
66
|
+
// Store ownership or delegation check
|
|
67
|
+
ownershipCheck: {
|
|
68
|
+
pattern: /store\.ownerId|ownerId.*===.*userId|userId.*===.*ownerId|delegatedAccess|storeAccess/i,
|
|
69
|
+
description: "Store ownership or delegation check",
|
|
70
|
+
critical: true,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Patterns that indicate store data access (requires auth)
|
|
75
|
+
*/
|
|
76
|
+
const STORE_DATA_PATTERNS = [
|
|
77
|
+
/findUnique.*store|findFirst.*store|prisma\.authStores/i,
|
|
78
|
+
/store\.listings|store\.orders|store\.products/i,
|
|
79
|
+
/getStoreData|fetchStore|loadStore/i,
|
|
80
|
+
/params\.storeId|storeId.*params/i,
|
|
81
|
+
];
|
|
82
|
+
/**
|
|
83
|
+
* Anti-patterns - dangerous code patterns
|
|
84
|
+
*/
|
|
85
|
+
const ANTI_PATTERNS = {
|
|
86
|
+
// Fetch store without auth
|
|
87
|
+
fetchStoreNoAuth: {
|
|
88
|
+
pattern: /(?:findUnique|findFirst)[\s\S]{0,100}authStores(?:(?!session|auth|checkPermission).)*\}/s,
|
|
89
|
+
description: "Store data fetched without authentication check nearby",
|
|
90
|
+
},
|
|
91
|
+
// Direct storeId from params without verification
|
|
92
|
+
directStoreIdUsage: {
|
|
93
|
+
pattern: /params\.storeId(?:(?!checkPermission|verifyAccess|hasAccess).){0,200}prisma/s,
|
|
94
|
+
description: "storeId from params used directly without access verification",
|
|
95
|
+
},
|
|
96
|
+
// Return store data without auth check
|
|
97
|
+
returnStoreDataNoAuth: {
|
|
98
|
+
pattern: /return\s*(?:NextResponse\.json|Response\.json)[\s\S]{0,50}store(?:(?!session|auth).){0,100}\}/s,
|
|
99
|
+
description: "Store data returned without visible auth check",
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
function hasStoreDataAccess(content) {
|
|
103
|
+
return STORE_DATA_PATTERNS.some((pattern) => pattern.test(content));
|
|
104
|
+
}
|
|
105
|
+
async function run() {
|
|
106
|
+
console.log(`\n${console_chars_1.emoji.shield} STORE PAGE AUTH GUARD CHECK`);
|
|
107
|
+
console.log((0, console_chars_1.createDivider)(65, "heavy"));
|
|
108
|
+
const issues = [];
|
|
109
|
+
const filesChecked = [];
|
|
110
|
+
// Find all store pages/routes
|
|
111
|
+
const allFiles = [];
|
|
112
|
+
for (const pattern of STORE_PAGE_PATTERNS) {
|
|
113
|
+
const matches = await (0, glob_1.glob)(pattern, { cwd: process.cwd() });
|
|
114
|
+
allFiles.push(...matches);
|
|
115
|
+
}
|
|
116
|
+
const uniqueFiles = [...new Set(allFiles)];
|
|
117
|
+
console.log(`\n${console_chars_1.emoji.search} Scanning ${uniqueFiles.length} store pages/routes...`);
|
|
118
|
+
if (uniqueFiles.length === 0) {
|
|
119
|
+
console.log(`\n${console_chars_1.emoji.warning} No store pages found - skipping check`);
|
|
120
|
+
return { success: true, errors: 0, warnings: 1 };
|
|
121
|
+
}
|
|
122
|
+
for (const relativePath of uniqueFiles) {
|
|
123
|
+
const filePath = path_1.default.join(process.cwd(), relativePath);
|
|
124
|
+
if (!fs_1.default.existsSync(filePath))
|
|
125
|
+
continue;
|
|
126
|
+
const content = fs_1.default.readFileSync(filePath, "utf-8");
|
|
127
|
+
filesChecked.push(relativePath);
|
|
128
|
+
// Skip if file doesn't access store data
|
|
129
|
+
if (!hasStoreDataAccess(content)) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
console.log(`\n${console_chars_1.emoji.file} ${relativePath} (accesses store data)`);
|
|
133
|
+
// Check for required auth patterns
|
|
134
|
+
const hasSessionCheck = REQUIRED_AUTH_PATTERNS.sessionCheck.pattern.test(content);
|
|
135
|
+
const hasStoreAccessCheck = REQUIRED_AUTH_PATTERNS.storeAccessCheck.pattern.test(content);
|
|
136
|
+
const hasOwnershipCheck = REQUIRED_AUTH_PATTERNS.ownershipCheck.pattern.test(content);
|
|
137
|
+
if (!hasSessionCheck) {
|
|
138
|
+
issues.push({
|
|
139
|
+
file: relativePath,
|
|
140
|
+
type: "missing-auth",
|
|
141
|
+
description: "Missing session/authentication check",
|
|
142
|
+
critical: true,
|
|
143
|
+
});
|
|
144
|
+
console.log(` ${console_chars_1.emoji.error} Missing: Session/authentication check`);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.log(` ${console_chars_1.emoji.success} Found: Session check`);
|
|
148
|
+
}
|
|
149
|
+
if (!hasStoreAccessCheck && !hasOwnershipCheck) {
|
|
150
|
+
issues.push({
|
|
151
|
+
file: relativePath,
|
|
152
|
+
type: "missing-store-access",
|
|
153
|
+
description: "Missing store access verification",
|
|
154
|
+
critical: true,
|
|
155
|
+
});
|
|
156
|
+
console.log(` ${console_chars_1.emoji.error} Missing: Store access verification`);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
console.log(` ${console_chars_1.emoji.success} Found: Store access check`);
|
|
160
|
+
}
|
|
161
|
+
// Check for anti-patterns
|
|
162
|
+
for (const [name, check] of Object.entries(ANTI_PATTERNS)) {
|
|
163
|
+
if (check.pattern.test(content)) {
|
|
164
|
+
issues.push({
|
|
165
|
+
file: relativePath,
|
|
166
|
+
type: "anti-pattern",
|
|
167
|
+
description: check.description,
|
|
168
|
+
critical: true,
|
|
169
|
+
});
|
|
170
|
+
console.log(` ${console_chars_1.emoji.error} Anti-pattern: ${check.description}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Summary
|
|
175
|
+
const criticalIssues = issues.filter((i) => i.critical);
|
|
176
|
+
console.log(`\n${console_chars_1.emoji.chart} Summary:`);
|
|
177
|
+
console.log(` Store pages checked: ${filesChecked.length}`);
|
|
178
|
+
console.log(` Security issues: ${criticalIssues.length}`);
|
|
179
|
+
if (issues.length === 0) {
|
|
180
|
+
console.log(`\n${console_chars_1.emoji.success} STORE PAGE AUTH GUARD CHECK PASSED`);
|
|
181
|
+
console.log(`\nAll store pages have proper authentication and access control.`);
|
|
182
|
+
return { success: true, errors: 0, warnings: 0 };
|
|
183
|
+
}
|
|
184
|
+
console.log(`\n${console_chars_1.emoji.error} Security issues found:`);
|
|
185
|
+
for (const issue of issues) {
|
|
186
|
+
console.log(` ${issue.file}: ${issue.description}`);
|
|
187
|
+
}
|
|
188
|
+
console.log(`\n${console_chars_1.emoji.info} Store page auth requirements:`);
|
|
189
|
+
console.log(` 1. Call getServerSession() or auth() to verify authentication`);
|
|
190
|
+
console.log(` 2. Call checkPermission() or getUserStoreAccess() to verify store access`);
|
|
191
|
+
console.log(` 3. Verify store ownership or delegation before returning data`);
|
|
192
|
+
console.log(` 4. Redirect or return 403 on access failure (don't return empty data)`);
|
|
193
|
+
console.log(`\n${console_chars_1.emoji.error} STORE PAGE AUTH GUARD CHECK FAILED`);
|
|
194
|
+
return { success: false, errors: criticalIssues.length, warnings: 0 };
|
|
195
|
+
}
|
|
196
|
+
// Allow direct execution
|
|
197
|
+
if (require.main === module) {
|
|
198
|
+
run()
|
|
199
|
+
.then((result) => {
|
|
200
|
+
process.exit(result.success ? 0 : 1);
|
|
201
|
+
})
|
|
202
|
+
.catch((err) => {
|
|
203
|
+
console.error(`${console_chars_1.emoji.error} Preflight failed:`, err);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=store-page-auth-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-page-auth-guard.js","sourceRoot":"","sources":["../../../src/checks/auth/store-page-auth-guard.ts"],"names":[],"mappings":"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"}
|