@empline/preflight 1.1.32 → 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/dist/checks/cart/cart-guest-merge.d.ts +14 -0
- package/dist/checks/cart/cart-guest-merge.d.ts.map +1 -0
- package/dist/checks/cart/cart-guest-merge.js +217 -0
- package/dist/checks/cart/cart-guest-merge.js.map +1 -0
- package/dist/checks/cart/cart-quantity-validation.d.ts +14 -0
- package/dist/checks/cart/cart-quantity-validation.d.ts.map +1 -0
- package/dist/checks/cart/cart-quantity-validation.js +211 -0
- package/dist/checks/cart/cart-quantity-validation.js.map +1 -0
- package/dist/checks/cart/cart-stock-validation.d.ts +14 -0
- package/dist/checks/cart/cart-stock-validation.d.ts.map +1 -0
- package/dist/checks/cart/cart-stock-validation.js +211 -0
- package/dist/checks/cart/cart-stock-validation.js.map +1 -0
- package/dist/checks/checkout/checkout-multi-store.d.ts +14 -0
- package/dist/checks/checkout/checkout-multi-store.d.ts.map +1 -0
- package/dist/checks/checkout/checkout-multi-store.js +209 -0
- package/dist/checks/checkout/checkout-multi-store.js.map +1 -0
- package/dist/checks/checkout/checkout-tax-consistency.d.ts +14 -0
- package/dist/checks/checkout/checkout-tax-consistency.d.ts.map +1 -0
- package/dist/checks/checkout/checkout-tax-consistency.js +212 -0
- package/dist/checks/checkout/checkout-tax-consistency.js.map +1 -0
- package/dist/checks/order/order-inventory-guard.d.ts +14 -0
- package/dist/checks/order/order-inventory-guard.d.ts.map +1 -0
- package/dist/checks/order/order-inventory-guard.js +212 -0
- package/dist/checks/order/order-inventory-guard.js.map +1 -0
- package/dist/checks/order/order-status-validation.d.ts +14 -0
- package/dist/checks/order/order-status-validation.d.ts.map +1 -0
- package/dist/checks/order/order-status-validation.js +218 -0
- package/dist/checks/order/order-status-validation.js.map +1 -0
- package/dist/checks/order/shipping-address-validation.d.ts +14 -0
- package/dist/checks/order/shipping-address-validation.d.ts.map +1 -0
- package/dist/checks/order/shipping-address-validation.js +213 -0
- package/dist/checks/order/shipping-address-validation.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"}
|