@quantracode/vibecheck 0.0.1 → 0.0.2
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/README.md +6 -6
- package/dist/index.d.ts +0 -2
- package/dist/index.js +7902 -8
- package/package.json +13 -7
- package/dist/__tests__/cli.test.d.ts +0 -2
- package/dist/__tests__/cli.test.d.ts.map +0 -1
- package/dist/__tests__/cli.test.js +0 -243
- package/dist/__tests__/fixtures/safe-app/app/api/users/route.js +0 -36
- package/dist/__tests__/fixtures/vulnerable-app/app/api/users/route.js +0 -28
- package/dist/__tests__/fixtures/vulnerable-app/lib/config.d.ts +0 -4
- package/dist/__tests__/fixtures/vulnerable-app/lib/config.d.ts.map +0 -1
- package/dist/__tests__/fixtures/vulnerable-app/lib/config.js +0 -6
- package/dist/__tests__/scanners/env-config.test.d.ts +0 -2
- package/dist/__tests__/scanners/env-config.test.d.ts.map +0 -1
- package/dist/__tests__/scanners/env-config.test.js +0 -142
- package/dist/__tests__/scanners/nextjs-middleware.test.d.ts +0 -2
- package/dist/__tests__/scanners/nextjs-middleware.test.d.ts.map +0 -1
- package/dist/__tests__/scanners/nextjs-middleware.test.js +0 -193
- package/dist/__tests__/scanners/scanner-packs.test.d.ts +0 -2
- package/dist/__tests__/scanners/scanner-packs.test.d.ts.map +0 -1
- package/dist/__tests__/scanners/scanner-packs.test.js +0 -126
- package/dist/__tests__/scanners/unused-security-imports.test.d.ts +0 -2
- package/dist/__tests__/scanners/unused-security-imports.test.d.ts.map +0 -1
- package/dist/__tests__/scanners/unused-security-imports.test.js +0 -145
- package/dist/commands/demo-artifact.d.ts +0 -7
- package/dist/commands/demo-artifact.d.ts.map +0 -1
- package/dist/commands/demo-artifact.js +0 -322
- package/dist/commands/evaluate.d.ts +0 -30
- package/dist/commands/evaluate.d.ts.map +0 -1
- package/dist/commands/evaluate.js +0 -258
- package/dist/commands/explain.d.ts +0 -12
- package/dist/commands/explain.d.ts.map +0 -1
- package/dist/commands/explain.js +0 -214
- package/dist/commands/index.d.ts +0 -7
- package/dist/commands/index.d.ts.map +0 -1
- package/dist/commands/index.js +0 -6
- package/dist/commands/intent.d.ts +0 -21
- package/dist/commands/intent.d.ts.map +0 -1
- package/dist/commands/intent.js +0 -192
- package/dist/commands/scan.d.ts +0 -44
- package/dist/commands/scan.d.ts.map +0 -1
- package/dist/commands/scan.js +0 -497
- package/dist/commands/waivers.d.ts +0 -30
- package/dist/commands/waivers.d.ts.map +0 -1
- package/dist/commands/waivers.js +0 -249
- package/dist/index.d.ts.map +0 -1
- package/dist/phase3/index.d.ts +0 -11
- package/dist/phase3/index.d.ts.map +0 -1
- package/dist/phase3/index.js +0 -12
- package/dist/phase3/intent-miner.d.ts +0 -32
- package/dist/phase3/intent-miner.d.ts.map +0 -1
- package/dist/phase3/intent-miner.js +0 -323
- package/dist/phase3/proof-trace-builder.d.ts +0 -42
- package/dist/phase3/proof-trace-builder.d.ts.map +0 -1
- package/dist/phase3/proof-trace-builder.js +0 -441
- package/dist/phase3/scanners/auth-by-ui-server-gap.d.ts +0 -15
- package/dist/phase3/scanners/auth-by-ui-server-gap.d.ts.map +0 -1
- package/dist/phase3/scanners/auth-by-ui-server-gap.js +0 -237
- package/dist/phase3/scanners/comment-claim-unproven.d.ts +0 -14
- package/dist/phase3/scanners/comment-claim-unproven.d.ts.map +0 -1
- package/dist/phase3/scanners/comment-claim-unproven.js +0 -161
- package/dist/phase3/scanners/index.d.ts +0 -31
- package/dist/phase3/scanners/index.d.ts.map +0 -1
- package/dist/phase3/scanners/index.js +0 -40
- package/dist/phase3/scanners/middleware-assumed-not-matching.d.ts +0 -14
- package/dist/phase3/scanners/middleware-assumed-not-matching.d.ts.map +0 -1
- package/dist/phase3/scanners/middleware-assumed-not-matching.js +0 -172
- package/dist/phase3/scanners/validation-claimed-missing.d.ts +0 -15
- package/dist/phase3/scanners/validation-claimed-missing.d.ts.map +0 -1
- package/dist/phase3/scanners/validation-claimed-missing.js +0 -204
- package/dist/scanners/abuse/compute-abuse.d.ts +0 -20
- package/dist/scanners/abuse/compute-abuse.d.ts.map +0 -1
- package/dist/scanners/abuse/compute-abuse.js +0 -509
- package/dist/scanners/abuse/index.d.ts +0 -12
- package/dist/scanners/abuse/index.d.ts.map +0 -1
- package/dist/scanners/abuse/index.js +0 -15
- package/dist/scanners/auth/index.d.ts +0 -5
- package/dist/scanners/auth/index.d.ts.map +0 -1
- package/dist/scanners/auth/index.js +0 -10
- package/dist/scanners/auth/middleware-gap.d.ts +0 -22
- package/dist/scanners/auth/middleware-gap.d.ts.map +0 -1
- package/dist/scanners/auth/middleware-gap.js +0 -203
- package/dist/scanners/auth/unprotected-api-route.d.ts +0 -12
- package/dist/scanners/auth/unprotected-api-route.d.ts.map +0 -1
- package/dist/scanners/auth/unprotected-api-route.js +0 -126
- package/dist/scanners/config/index.d.ts +0 -5
- package/dist/scanners/config/index.d.ts.map +0 -1
- package/dist/scanners/config/index.js +0 -10
- package/dist/scanners/config/insecure-defaults.d.ts +0 -12
- package/dist/scanners/config/insecure-defaults.d.ts.map +0 -1
- package/dist/scanners/config/insecure-defaults.js +0 -77
- package/dist/scanners/config/undocumented-env.d.ts +0 -24
- package/dist/scanners/config/undocumented-env.d.ts.map +0 -1
- package/dist/scanners/config/undocumented-env.js +0 -159
- package/dist/scanners/crypto/index.d.ts +0 -6
- package/dist/scanners/crypto/index.d.ts.map +0 -1
- package/dist/scanners/crypto/index.js +0 -11
- package/dist/scanners/crypto/jwt-decode-unverified.d.ts +0 -14
- package/dist/scanners/crypto/jwt-decode-unverified.d.ts.map +0 -1
- package/dist/scanners/crypto/jwt-decode-unverified.js +0 -87
- package/dist/scanners/crypto/math-random-tokens.d.ts +0 -13
- package/dist/scanners/crypto/math-random-tokens.d.ts.map +0 -1
- package/dist/scanners/crypto/math-random-tokens.js +0 -80
- package/dist/scanners/crypto/weak-hashing.d.ts +0 -11
- package/dist/scanners/crypto/weak-hashing.d.ts.map +0 -1
- package/dist/scanners/crypto/weak-hashing.js +0 -95
- package/dist/scanners/env-config.d.ts +0 -24
- package/dist/scanners/env-config.d.ts.map +0 -1
- package/dist/scanners/env-config.js +0 -164
- package/dist/scanners/hallucinations/index.d.ts +0 -4
- package/dist/scanners/hallucinations/index.d.ts.map +0 -1
- package/dist/scanners/hallucinations/index.js +0 -8
- package/dist/scanners/hallucinations/unused-security-imports.d.ts +0 -36
- package/dist/scanners/hallucinations/unused-security-imports.d.ts.map +0 -1
- package/dist/scanners/hallucinations/unused-security-imports.js +0 -309
- package/dist/scanners/helpers/ast-helpers.d.ts +0 -6
- package/dist/scanners/helpers/ast-helpers.d.ts.map +0 -1
- package/dist/scanners/helpers/ast-helpers.js +0 -945
- package/dist/scanners/helpers/context-builder.d.ts +0 -17
- package/dist/scanners/helpers/context-builder.d.ts.map +0 -1
- package/dist/scanners/helpers/context-builder.js +0 -148
- package/dist/scanners/helpers/index.d.ts +0 -3
- package/dist/scanners/helpers/index.d.ts.map +0 -1
- package/dist/scanners/helpers/index.js +0 -2
- package/dist/scanners/index.d.ts +0 -30
- package/dist/scanners/index.d.ts.map +0 -1
- package/dist/scanners/index.js +0 -102
- package/dist/scanners/middleware/index.d.ts +0 -4
- package/dist/scanners/middleware/index.d.ts.map +0 -1
- package/dist/scanners/middleware/index.js +0 -7
- package/dist/scanners/middleware/missing-rate-limit.d.ts +0 -13
- package/dist/scanners/middleware/missing-rate-limit.d.ts.map +0 -1
- package/dist/scanners/middleware/missing-rate-limit.js +0 -140
- package/dist/scanners/network/cors-misconfiguration.d.ts +0 -14
- package/dist/scanners/network/cors-misconfiguration.d.ts.map +0 -1
- package/dist/scanners/network/cors-misconfiguration.js +0 -89
- package/dist/scanners/network/index.d.ts +0 -7
- package/dist/scanners/network/index.d.ts.map +0 -1
- package/dist/scanners/network/index.js +0 -18
- package/dist/scanners/network/missing-timeout.d.ts +0 -15
- package/dist/scanners/network/missing-timeout.d.ts.map +0 -1
- package/dist/scanners/network/missing-timeout.js +0 -93
- package/dist/scanners/network/open-redirect.d.ts +0 -15
- package/dist/scanners/network/open-redirect.d.ts.map +0 -1
- package/dist/scanners/network/open-redirect.js +0 -88
- package/dist/scanners/network/ssrf-prone-fetch.d.ts +0 -12
- package/dist/scanners/network/ssrf-prone-fetch.d.ts.map +0 -1
- package/dist/scanners/network/ssrf-prone-fetch.js +0 -90
- package/dist/scanners/nextjs-middleware.d.ts +0 -26
- package/dist/scanners/nextjs-middleware.d.ts.map +0 -1
- package/dist/scanners/nextjs-middleware.js +0 -246
- package/dist/scanners/privacy/debug-flags.d.ts +0 -13
- package/dist/scanners/privacy/debug-flags.d.ts.map +0 -1
- package/dist/scanners/privacy/debug-flags.js +0 -124
- package/dist/scanners/privacy/index.d.ts +0 -6
- package/dist/scanners/privacy/index.d.ts.map +0 -1
- package/dist/scanners/privacy/index.js +0 -11
- package/dist/scanners/privacy/over-broad-response.d.ts +0 -15
- package/dist/scanners/privacy/over-broad-response.d.ts.map +0 -1
- package/dist/scanners/privacy/over-broad-response.js +0 -109
- package/dist/scanners/privacy/sensitive-logging.d.ts +0 -11
- package/dist/scanners/privacy/sensitive-logging.d.ts.map +0 -1
- package/dist/scanners/privacy/sensitive-logging.js +0 -78
- package/dist/scanners/types.d.ts +0 -456
- package/dist/scanners/types.d.ts.map +0 -1
- package/dist/scanners/types.js +0 -16
- package/dist/scanners/unused-security-imports.d.ts +0 -34
- package/dist/scanners/unused-security-imports.d.ts.map +0 -1
- package/dist/scanners/unused-security-imports.js +0 -206
- package/dist/scanners/uploads/index.d.ts +0 -5
- package/dist/scanners/uploads/index.d.ts.map +0 -1
- package/dist/scanners/uploads/index.js +0 -9
- package/dist/scanners/uploads/missing-constraints.d.ts +0 -15
- package/dist/scanners/uploads/missing-constraints.d.ts.map +0 -1
- package/dist/scanners/uploads/missing-constraints.js +0 -109
- package/dist/scanners/uploads/public-path.d.ts +0 -11
- package/dist/scanners/uploads/public-path.d.ts.map +0 -1
- package/dist/scanners/uploads/public-path.js +0 -87
- package/dist/scanners/validation/client-side-only.d.ts +0 -14
- package/dist/scanners/validation/client-side-only.d.ts.map +0 -1
- package/dist/scanners/validation/client-side-only.js +0 -140
- package/dist/scanners/validation/ignored-validation.d.ts +0 -12
- package/dist/scanners/validation/ignored-validation.d.ts.map +0 -1
- package/dist/scanners/validation/ignored-validation.js +0 -119
- package/dist/scanners/validation/index.d.ts +0 -5
- package/dist/scanners/validation/index.d.ts.map +0 -1
- package/dist/scanners/validation/index.js +0 -9
- package/dist/utils/exclude-patterns.d.ts +0 -35
- package/dist/utils/exclude-patterns.d.ts.map +0 -1
- package/dist/utils/exclude-patterns.js +0 -78
- package/dist/utils/file-utils.d.ts +0 -37
- package/dist/utils/file-utils.d.ts.map +0 -1
- package/dist/utils/file-utils.js +0 -77
- package/dist/utils/fingerprint.d.ts +0 -25
- package/dist/utils/fingerprint.d.ts.map +0 -1
- package/dist/utils/fingerprint.js +0 -28
- package/dist/utils/git-info.d.ts +0 -14
- package/dist/utils/git-info.d.ts.map +0 -1
- package/dist/utils/git-info.js +0 -55
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -3
- package/dist/utils/progress.d.ts +0 -42
- package/dist/utils/progress.d.ts.map +0 -1
- package/dist/utils/progress.js +0 -165
- package/dist/utils/sarif-formatter.d.ts +0 -92
- package/dist/utils/sarif-formatter.d.ts.map +0 -1
- package/dist/utils/sarif-formatter.js +0 -172
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { resolvePath } from "../../utils/file-utils.js";
|
|
2
|
-
import { generateFingerprint, generateFindingId } from "../../utils/fingerprint.js";
|
|
3
|
-
const RULE_ID = "VC-NET-004";
|
|
4
|
-
/**
|
|
5
|
-
* VC-NET-004: Missing request timeout on outbound calls
|
|
6
|
-
*
|
|
7
|
-
* Detects axios/fetch usage without explicit timeout:
|
|
8
|
-
* - axios(...) without timeout option
|
|
9
|
-
* - fetch(...) without AbortController / timeout wrapper
|
|
10
|
-
*
|
|
11
|
-
* Precision rule:
|
|
12
|
-
* - Only flag when outbound call is inside a route handler (app/api/**) AND
|
|
13
|
-
* - Call appears to hit non-localhost URL (string literal starting http)
|
|
14
|
-
*/
|
|
15
|
-
export async function scanMissingTimeout(context) {
|
|
16
|
-
const { repoRoot, fileIndex, helpers } = context;
|
|
17
|
-
const findings = [];
|
|
18
|
-
// Only scan API route files
|
|
19
|
-
for (const relPath of fileIndex.apiRouteFiles) {
|
|
20
|
-
const absPath = resolvePath(repoRoot, relPath);
|
|
21
|
-
const sourceFile = helpers.parseFile(absPath);
|
|
22
|
-
if (!sourceFile)
|
|
23
|
-
continue;
|
|
24
|
-
const handlers = helpers.findRouteHandlers(sourceFile);
|
|
25
|
-
for (const handler of handlers) {
|
|
26
|
-
const outboundCalls = helpers.findOutboundCalls(handler.functionNode);
|
|
27
|
-
for (const call of outboundCalls) {
|
|
28
|
-
// Only flag external URLs without timeout
|
|
29
|
-
if (!call.isExternalUrl || call.hasTimeout)
|
|
30
|
-
continue;
|
|
31
|
-
const evidence = [
|
|
32
|
-
{
|
|
33
|
-
file: relPath,
|
|
34
|
-
startLine: call.line,
|
|
35
|
-
endLine: call.line,
|
|
36
|
-
snippet: call.snippet,
|
|
37
|
-
label: `${call.method} call to external URL without timeout`,
|
|
38
|
-
},
|
|
39
|
-
];
|
|
40
|
-
const fingerprint = generateFingerprint({
|
|
41
|
-
ruleId: RULE_ID,
|
|
42
|
-
file: relPath,
|
|
43
|
-
symbol: call.method,
|
|
44
|
-
startLine: call.line,
|
|
45
|
-
});
|
|
46
|
-
findings.push({
|
|
47
|
-
id: generateFindingId({
|
|
48
|
-
ruleId: RULE_ID,
|
|
49
|
-
file: relPath,
|
|
50
|
-
symbol: call.method,
|
|
51
|
-
startLine: call.line,
|
|
52
|
-
}),
|
|
53
|
-
ruleId: RULE_ID,
|
|
54
|
-
title: `Missing timeout on ${call.method} call`,
|
|
55
|
-
description: `The outbound ${call.method} call to an external URL lacks a timeout configuration. If the external service is slow or unresponsive, this could cause your API handler to hang indefinitely, consuming server resources and potentially causing denial of service.`,
|
|
56
|
-
severity: "low",
|
|
57
|
-
confidence: 0.75,
|
|
58
|
-
category: "network",
|
|
59
|
-
evidence,
|
|
60
|
-
remediation: {
|
|
61
|
-
recommendedFix: `Add a timeout to prevent indefinite hangs. For fetch, use AbortController; for axios, use the timeout option.`,
|
|
62
|
-
patch: `// For fetch, use AbortController:
|
|
63
|
-
const controller = new AbortController();
|
|
64
|
-
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
const response = await fetch(url, {
|
|
68
|
-
signal: controller.signal,
|
|
69
|
-
});
|
|
70
|
-
clearTimeout(timeoutId);
|
|
71
|
-
return response;
|
|
72
|
-
} catch (error) {
|
|
73
|
-
if (error.name === 'AbortError') {
|
|
74
|
-
throw new Error('Request timed out');
|
|
75
|
-
}
|
|
76
|
-
throw error;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// For axios:
|
|
80
|
-
const response = await axios.get(url, {
|
|
81
|
-
timeout: 5000,
|
|
82
|
-
});`,
|
|
83
|
-
},
|
|
84
|
-
links: {
|
|
85
|
-
cwe: "https://cwe.mitre.org/data/definitions/400.html",
|
|
86
|
-
},
|
|
87
|
-
fingerprint,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return findings;
|
|
93
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { Finding } from "@vibecheck/schema";
|
|
2
|
-
import type { ScanContext } from "../types.js";
|
|
3
|
-
/**
|
|
4
|
-
* VC-NET-002: Open Redirect
|
|
5
|
-
*
|
|
6
|
-
* Detects server-side redirects where user-controlled input determines destination:
|
|
7
|
-
* - NextResponse.redirect(userValue)
|
|
8
|
-
* - res.redirect(userValue)
|
|
9
|
-
* - redirect(userValue) (next/navigation on server)
|
|
10
|
-
*
|
|
11
|
-
* Two-signal requirement:
|
|
12
|
-
* - Must identify user-controlled source AND redirect call uses that value
|
|
13
|
-
*/
|
|
14
|
-
export declare function scanOpenRedirect(context: ScanContext): Promise<Finding[]>;
|
|
15
|
-
//# sourceMappingURL=open-redirect.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"open-redirect.d.ts","sourceRoot":"","sources":["../../../src/scanners/network/open-redirect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAiF/E"}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { resolvePath } from "../../utils/file-utils.js";
|
|
2
|
-
import { generateFingerprint, generateFindingId } from "../../utils/fingerprint.js";
|
|
3
|
-
const RULE_ID = "VC-NET-002";
|
|
4
|
-
/**
|
|
5
|
-
* VC-NET-002: Open Redirect
|
|
6
|
-
*
|
|
7
|
-
* Detects server-side redirects where user-controlled input determines destination:
|
|
8
|
-
* - NextResponse.redirect(userValue)
|
|
9
|
-
* - res.redirect(userValue)
|
|
10
|
-
* - redirect(userValue) (next/navigation on server)
|
|
11
|
-
*
|
|
12
|
-
* Two-signal requirement:
|
|
13
|
-
* - Must identify user-controlled source AND redirect call uses that value
|
|
14
|
-
*/
|
|
15
|
-
export async function scanOpenRedirect(context) {
|
|
16
|
-
const { repoRoot, fileIndex, helpers } = context;
|
|
17
|
-
const findings = [];
|
|
18
|
-
// Scan API route files and server components
|
|
19
|
-
const filesToScan = [...fileIndex.apiRouteFiles, ...fileIndex.routeFiles];
|
|
20
|
-
for (const relPath of filesToScan) {
|
|
21
|
-
const absPath = resolvePath(repoRoot, relPath);
|
|
22
|
-
const sourceFile = helpers.parseFile(absPath);
|
|
23
|
-
if (!sourceFile)
|
|
24
|
-
continue;
|
|
25
|
-
const handlers = helpers.findRouteHandlers(sourceFile);
|
|
26
|
-
for (const handler of handlers) {
|
|
27
|
-
const redirectCalls = helpers.findRedirectCalls(handler.functionNode);
|
|
28
|
-
for (const redirect of redirectCalls) {
|
|
29
|
-
if (!redirect.isUserControlled)
|
|
30
|
-
continue;
|
|
31
|
-
const evidence = [
|
|
32
|
-
{
|
|
33
|
-
file: relPath,
|
|
34
|
-
startLine: redirect.line,
|
|
35
|
-
endLine: redirect.line,
|
|
36
|
-
snippet: redirect.snippet,
|
|
37
|
-
label: `User-controlled redirect via ${redirect.userControlledSource}`,
|
|
38
|
-
},
|
|
39
|
-
];
|
|
40
|
-
const fingerprint = generateFingerprint({
|
|
41
|
-
ruleId: RULE_ID,
|
|
42
|
-
file: relPath,
|
|
43
|
-
symbol: redirect.method,
|
|
44
|
-
startLine: redirect.line,
|
|
45
|
-
});
|
|
46
|
-
findings.push({
|
|
47
|
-
id: generateFindingId({
|
|
48
|
-
ruleId: RULE_ID,
|
|
49
|
-
file: relPath,
|
|
50
|
-
symbol: redirect.method,
|
|
51
|
-
startLine: redirect.line,
|
|
52
|
-
}),
|
|
53
|
-
ruleId: RULE_ID,
|
|
54
|
-
title: `Open redirect via ${redirect.method}`,
|
|
55
|
-
description: `The ${redirect.method} call uses user-controlled input (${redirect.userControlledSource}) to determine the redirect destination. An attacker could craft a malicious link that redirects users to a phishing site or malicious domain, abusing the trust users place in your domain.`,
|
|
56
|
-
severity: "high",
|
|
57
|
-
confidence: 0.85,
|
|
58
|
-
category: "network",
|
|
59
|
-
evidence,
|
|
60
|
-
remediation: {
|
|
61
|
-
recommendedFix: `Validate the redirect URL against an allowlist of permitted paths or domains. Never redirect to arbitrary user-provided URLs without validation.`,
|
|
62
|
-
patch: `// Validate redirect URL before use
|
|
63
|
-
const allowedPaths = ['/dashboard', '/profile', '/settings'];
|
|
64
|
-
const redirectUrl = searchParams.get('next') || '/';
|
|
65
|
-
|
|
66
|
-
// Option 1: Only allow relative paths
|
|
67
|
-
if (!redirectUrl.startsWith('/') || redirectUrl.startsWith('//')) {
|
|
68
|
-
return NextResponse.redirect(new URL('/', request.url));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Option 2: Allowlist check
|
|
72
|
-
if (!allowedPaths.some(path => redirectUrl.startsWith(path))) {
|
|
73
|
-
return NextResponse.redirect(new URL('/', request.url));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return NextResponse.redirect(new URL(redirectUrl, request.url));`,
|
|
77
|
-
},
|
|
78
|
-
links: {
|
|
79
|
-
cwe: "https://cwe.mitre.org/data/definitions/601.html",
|
|
80
|
-
owasp: "https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html",
|
|
81
|
-
},
|
|
82
|
-
fingerprint,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return findings;
|
|
88
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { Finding } from "@vibecheck/schema";
|
|
2
|
-
import type { ScanContext } from "../types.js";
|
|
3
|
-
/**
|
|
4
|
-
* VC-NET-001: SSRF-prone fetch
|
|
5
|
-
*
|
|
6
|
-
* Detects fetch/axios calls using direct user-controlled input:
|
|
7
|
-
* - fetch(body.url) / fetch(query.url) / axios.get(body.url)
|
|
8
|
-
*
|
|
9
|
-
* Only flags if the argument is a property named url/uri/link coming from req.json()/req.query
|
|
10
|
-
*/
|
|
11
|
-
export declare function scanSsrfProneFetch(context: ScanContext): Promise<Finding[]>;
|
|
12
|
-
//# sourceMappingURL=ssrf-prone-fetch.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ssrf-prone-fetch.d.ts","sourceRoot":"","sources":["../../../src/scanners/network/ssrf-prone-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAqFjF"}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { resolvePath } from "../../utils/file-utils.js";
|
|
2
|
-
import { generateFingerprint, generateFindingId } from "../../utils/fingerprint.js";
|
|
3
|
-
const RULE_ID = "VC-NET-001";
|
|
4
|
-
/**
|
|
5
|
-
* VC-NET-001: SSRF-prone fetch
|
|
6
|
-
*
|
|
7
|
-
* Detects fetch/axios calls using direct user-controlled input:
|
|
8
|
-
* - fetch(body.url) / fetch(query.url) / axios.get(body.url)
|
|
9
|
-
*
|
|
10
|
-
* Only flags if the argument is a property named url/uri/link coming from req.json()/req.query
|
|
11
|
-
*/
|
|
12
|
-
export async function scanSsrfProneFetch(context) {
|
|
13
|
-
const { repoRoot, fileIndex, helpers } = context;
|
|
14
|
-
const findings = [];
|
|
15
|
-
// Scan API route files (most likely to have SSRF issues)
|
|
16
|
-
for (const relPath of fileIndex.apiRouteFiles) {
|
|
17
|
-
const absPath = resolvePath(repoRoot, relPath);
|
|
18
|
-
const sourceFile = helpers.parseFile(absPath);
|
|
19
|
-
if (!sourceFile)
|
|
20
|
-
continue;
|
|
21
|
-
const handlers = helpers.findRouteHandlers(sourceFile);
|
|
22
|
-
for (const handler of handlers) {
|
|
23
|
-
const ssrfCalls = helpers.findSsrfProneFetch(handler.functionNode);
|
|
24
|
-
for (const ssrf of ssrfCalls) {
|
|
25
|
-
const evidence = [
|
|
26
|
-
{
|
|
27
|
-
file: relPath,
|
|
28
|
-
startLine: ssrf.line,
|
|
29
|
-
endLine: ssrf.line,
|
|
30
|
-
snippet: ssrf.snippet,
|
|
31
|
-
label: `User-controlled URL passed to ${ssrf.fetchMethod}`,
|
|
32
|
-
},
|
|
33
|
-
];
|
|
34
|
-
const fingerprint = generateFingerprint({
|
|
35
|
-
ruleId: RULE_ID,
|
|
36
|
-
file: relPath,
|
|
37
|
-
symbol: ssrf.fetchMethod,
|
|
38
|
-
startLine: ssrf.line,
|
|
39
|
-
});
|
|
40
|
-
findings.push({
|
|
41
|
-
id: generateFindingId({
|
|
42
|
-
ruleId: RULE_ID,
|
|
43
|
-
file: relPath,
|
|
44
|
-
symbol: ssrf.fetchMethod,
|
|
45
|
-
startLine: ssrf.line,
|
|
46
|
-
}),
|
|
47
|
-
ruleId: RULE_ID,
|
|
48
|
-
title: `SSRF-prone ${ssrf.fetchMethod} call`,
|
|
49
|
-
description: `The ${ssrf.fetchMethod} call uses user-controlled input (${ssrf.userInputSource}) as the URL. An attacker could manipulate this to make requests to internal services, cloud metadata endpoints, or other sensitive destinations.`,
|
|
50
|
-
severity: "high",
|
|
51
|
-
confidence: 0.85,
|
|
52
|
-
category: "network",
|
|
53
|
-
evidence,
|
|
54
|
-
remediation: {
|
|
55
|
-
recommendedFix: `Validate and sanitize the URL before use. Use an allowlist of permitted domains or URL patterns. Never allow requests to internal networks, localhost, or cloud metadata endpoints.`,
|
|
56
|
-
patch: `// Validate URL before fetching
|
|
57
|
-
const url = new URL(userProvidedUrl);
|
|
58
|
-
|
|
59
|
-
// Check against allowlist
|
|
60
|
-
const allowedHosts = ["api.example.com", "cdn.example.com"];
|
|
61
|
-
if (!allowedHosts.includes(url.hostname)) {
|
|
62
|
-
throw new Error("URL not allowed");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Block internal addresses
|
|
66
|
-
const blockedPatterns = [
|
|
67
|
-
/^localhost$/i,
|
|
68
|
-
/^127\\.\\d+\\.\\d+\\.\\d+$/,
|
|
69
|
-
/^10\\.\\d+\\.\\d+\\.\\d+$/,
|
|
70
|
-
/^172\\.(1[6-9]|2[0-9]|3[0-1])\\.\\d+\\.\\d+$/,
|
|
71
|
-
/^192\\.168\\.\\d+\\.\\d+$/,
|
|
72
|
-
/^169\\.254\\.169\\.254$/, // AWS metadata
|
|
73
|
-
];
|
|
74
|
-
if (blockedPatterns.some(p => p.test(url.hostname))) {
|
|
75
|
-
throw new Error("URL not allowed");
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const response = await fetch(url.toString());`,
|
|
79
|
-
},
|
|
80
|
-
links: {
|
|
81
|
-
cwe: "https://cwe.mitre.org/data/definitions/918.html",
|
|
82
|
-
owasp: "https://owasp.org/Top10/A10_2021-Server-Side_Request_Forgery_%28SSRF%29/",
|
|
83
|
-
},
|
|
84
|
-
fingerprint,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return findings;
|
|
90
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { Finding } from "@vibecheck/schema";
|
|
2
|
-
import type { ScanContext } from "./types.js";
|
|
3
|
-
/**
|
|
4
|
-
* Parse matcher config from middleware file
|
|
5
|
-
*
|
|
6
|
-
* Looks for patterns like:
|
|
7
|
-
* - export const config = { matcher: '/api/:path*' }
|
|
8
|
-
* - export const config = { matcher: ['/api/:path*', '/admin/:path*'] }
|
|
9
|
-
*
|
|
10
|
-
* Limitations:
|
|
11
|
-
* - Uses regex, may have false positives/negatives
|
|
12
|
-
* - Does not handle dynamic matcher generation
|
|
13
|
-
* - Does not parse complex expressions
|
|
14
|
-
*/
|
|
15
|
-
export declare function parseMatcherConfig(content: string): string[] | null;
|
|
16
|
-
/**
|
|
17
|
-
* Check if any matcher pattern covers /api routes
|
|
18
|
-
*/
|
|
19
|
-
export declare function matcherCoversApi(matchers: string[]): boolean;
|
|
20
|
-
/**
|
|
21
|
-
* Next.js Middleware Gap Scanner
|
|
22
|
-
*
|
|
23
|
-
* Checks if middleware properly covers API routes
|
|
24
|
-
*/
|
|
25
|
-
export declare function scanNextjsMiddleware(context: ScanContext): Promise<Finding[]>;
|
|
26
|
-
//# sourceMappingURL=nextjs-middleware.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"nextjs-middleware.d.ts","sourceRoot":"","sources":["../../src/scanners/nextjs-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAS/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAwD9C;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAkCnE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAyB5D;AAkBD;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CA8InF"}
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import { readFileSync, fileExists, resolvePath, readJsonSync, findFiles, } from "../utils/file-utils.js";
|
|
2
|
-
import { generateFingerprint, generateFindingId } from "../utils/fingerprint.js";
|
|
3
|
-
const RULE_MW_001 = "VC-MW-001";
|
|
4
|
-
const RULE_AUTH_INFO_001 = "VC-AUTH-INFO-001";
|
|
5
|
-
/**
|
|
6
|
-
* Check if project has next-auth dependency
|
|
7
|
-
*/
|
|
8
|
-
function hasNextAuth(targetDir) {
|
|
9
|
-
const pkgPath = resolvePath(targetDir, "package.json");
|
|
10
|
-
const pkg = readJsonSync(pkgPath);
|
|
11
|
-
if (!pkg)
|
|
12
|
-
return false;
|
|
13
|
-
return Boolean(pkg.dependencies?.["next-auth"] || pkg.devDependencies?.["next-auth"]);
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Check if project is a Next.js project
|
|
17
|
-
*/
|
|
18
|
-
function isNextProject(targetDir) {
|
|
19
|
-
const pkgPath = resolvePath(targetDir, "package.json");
|
|
20
|
-
const pkg = readJsonSync(pkgPath);
|
|
21
|
-
if (!pkg)
|
|
22
|
-
return false;
|
|
23
|
-
return Boolean(pkg.dependencies?.["next"] || pkg.devDependencies?.["next"]);
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Find middleware file in Next.js project
|
|
27
|
-
* Can be at root or in src directory
|
|
28
|
-
*/
|
|
29
|
-
function findMiddlewarePath(targetDir) {
|
|
30
|
-
const candidates = [
|
|
31
|
-
"middleware.ts",
|
|
32
|
-
"middleware.js",
|
|
33
|
-
"src/middleware.ts",
|
|
34
|
-
"src/middleware.js",
|
|
35
|
-
];
|
|
36
|
-
for (const candidate of candidates) {
|
|
37
|
-
const fullPath = resolvePath(targetDir, candidate);
|
|
38
|
-
if (fileExists(fullPath)) {
|
|
39
|
-
return candidate;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Parse matcher config from middleware file
|
|
46
|
-
*
|
|
47
|
-
* Looks for patterns like:
|
|
48
|
-
* - export const config = { matcher: '/api/:path*' }
|
|
49
|
-
* - export const config = { matcher: ['/api/:path*', '/admin/:path*'] }
|
|
50
|
-
*
|
|
51
|
-
* Limitations:
|
|
52
|
-
* - Uses regex, may have false positives/negatives
|
|
53
|
-
* - Does not handle dynamic matcher generation
|
|
54
|
-
* - Does not parse complex expressions
|
|
55
|
-
*/
|
|
56
|
-
export function parseMatcherConfig(content) {
|
|
57
|
-
// Look for config export with matcher
|
|
58
|
-
const configMatch = content.match(/export\s+const\s+config\s*=\s*\{[^}]*matcher\s*:\s*([^}]+)\}/s);
|
|
59
|
-
if (!configMatch) {
|
|
60
|
-
return null; // No config export found
|
|
61
|
-
}
|
|
62
|
-
const matcherPart = configMatch[1];
|
|
63
|
-
// Check for array matcher: ['/path1', '/path2']
|
|
64
|
-
const arrayMatch = matcherPart.match(/\[([^\]]+)\]/);
|
|
65
|
-
if (arrayMatch) {
|
|
66
|
-
const items = arrayMatch[1];
|
|
67
|
-
const paths = [];
|
|
68
|
-
// Extract string literals
|
|
69
|
-
const stringMatches = items.matchAll(/['"]([^'"]+)['"]/g);
|
|
70
|
-
for (const m of stringMatches) {
|
|
71
|
-
paths.push(m[1]);
|
|
72
|
-
}
|
|
73
|
-
return paths;
|
|
74
|
-
}
|
|
75
|
-
// Check for single string matcher: '/path'
|
|
76
|
-
const stringMatch = matcherPart.match(/['"]([^'"]+)['"]/);
|
|
77
|
-
if (stringMatch) {
|
|
78
|
-
return [stringMatch[1]];
|
|
79
|
-
}
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Check if any matcher pattern covers /api routes
|
|
84
|
-
*/
|
|
85
|
-
export function matcherCoversApi(matchers) {
|
|
86
|
-
for (const matcher of matchers) {
|
|
87
|
-
// Direct /api match
|
|
88
|
-
if (matcher === "/api" || matcher === "/api/:path*") {
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
// Pattern starts with /api
|
|
92
|
-
if (matcher.startsWith("/api/") || matcher.startsWith("/api:")) {
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
// Catch-all that would include /api
|
|
96
|
-
if (matcher === "/:path*" || matcher === "/(.*)" || matcher === "/(.*)") {
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
// Negation pattern - check if it's excluding something else but including api
|
|
100
|
-
// e.g., '/((?!_next/static|_next/image|favicon.ico).*)'
|
|
101
|
-
if (matcher.includes("(?!") && !matcher.includes("api")) {
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Find API routes in Next.js app directory
|
|
109
|
-
*/
|
|
110
|
-
async function findApiRoutes(targetDir) {
|
|
111
|
-
// Look for API routes in app directory (App Router)
|
|
112
|
-
const appApiRoutes = await findFiles(["app/**/route.{ts,js}", "src/app/**/route.{ts,js}"], { cwd: targetDir });
|
|
113
|
-
// Filter to only /api routes
|
|
114
|
-
return appApiRoutes.filter((r) => r.includes("/api/") || r.includes("\\api\\"));
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Next.js Middleware Gap Scanner
|
|
118
|
-
*
|
|
119
|
-
* Checks if middleware properly covers API routes
|
|
120
|
-
*/
|
|
121
|
-
export async function scanNextjsMiddleware(context) {
|
|
122
|
-
const { targetDir } = context;
|
|
123
|
-
const findings = [];
|
|
124
|
-
// Only run for Next.js projects
|
|
125
|
-
if (!isNextProject(targetDir)) {
|
|
126
|
-
return findings;
|
|
127
|
-
}
|
|
128
|
-
// Find API routes
|
|
129
|
-
const apiRoutes = await findApiRoutes(targetDir);
|
|
130
|
-
const hasApiRoutes = apiRoutes.length > 0;
|
|
131
|
-
if (!hasApiRoutes) {
|
|
132
|
-
return findings; // No API routes to protect
|
|
133
|
-
}
|
|
134
|
-
// Check for middleware
|
|
135
|
-
const middlewarePath = findMiddlewarePath(targetDir);
|
|
136
|
-
if (!middlewarePath) {
|
|
137
|
-
// No middleware file exists
|
|
138
|
-
if (hasNextAuth(targetDir)) {
|
|
139
|
-
// Has next-auth but no middleware - likely missing protection
|
|
140
|
-
const evidence = [
|
|
141
|
-
{
|
|
142
|
-
file: "package.json",
|
|
143
|
-
startLine: 1,
|
|
144
|
-
endLine: 1,
|
|
145
|
-
label: "next-auth dependency present",
|
|
146
|
-
},
|
|
147
|
-
...apiRoutes.slice(0, 3).map((route) => ({
|
|
148
|
-
file: route,
|
|
149
|
-
startLine: 1,
|
|
150
|
-
endLine: 1,
|
|
151
|
-
label: "API route without middleware protection",
|
|
152
|
-
})),
|
|
153
|
-
];
|
|
154
|
-
const fingerprint = generateFingerprint({
|
|
155
|
-
ruleId: RULE_AUTH_INFO_001,
|
|
156
|
-
file: "middleware.ts",
|
|
157
|
-
symbol: "missing",
|
|
158
|
-
});
|
|
159
|
-
findings.push({
|
|
160
|
-
id: generateFindingId({
|
|
161
|
-
ruleId: RULE_AUTH_INFO_001,
|
|
162
|
-
file: "middleware.ts",
|
|
163
|
-
symbol: "missing",
|
|
164
|
-
}),
|
|
165
|
-
severity: "medium",
|
|
166
|
-
confidence: 0.7,
|
|
167
|
-
category: "auth",
|
|
168
|
-
ruleId: RULE_AUTH_INFO_001,
|
|
169
|
-
title: "Next.js middleware missing with next-auth dependency",
|
|
170
|
-
description: `This Next.js project uses next-auth but has no middleware.ts file. API routes (${apiRoutes.length} found) may lack server-side authentication enforcement. While next-auth provides session management, middleware is recommended for edge-level protection.`,
|
|
171
|
-
evidence,
|
|
172
|
-
remediation: {
|
|
173
|
-
recommendedFix: "Create a middleware.ts file that checks authentication for protected routes. See: https://next-auth.js.org/configuration/nextjs#middleware",
|
|
174
|
-
},
|
|
175
|
-
links: {
|
|
176
|
-
owasp: "https://owasp.org/Top10/A01_2021-Broken_Access_Control/",
|
|
177
|
-
},
|
|
178
|
-
fingerprint,
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
return findings;
|
|
182
|
-
}
|
|
183
|
-
// Middleware exists - check if it covers API routes
|
|
184
|
-
const middlewareContent = readFileSync(resolvePath(targetDir, middlewarePath));
|
|
185
|
-
if (!middlewareContent) {
|
|
186
|
-
return findings;
|
|
187
|
-
}
|
|
188
|
-
const matchers = parseMatcherConfig(middlewareContent);
|
|
189
|
-
// If no matcher config, middleware applies to all routes (except static)
|
|
190
|
-
if (matchers === null) {
|
|
191
|
-
return findings; // Likely covers everything
|
|
192
|
-
}
|
|
193
|
-
// Check if matchers cover /api
|
|
194
|
-
if (!matcherCoversApi(matchers)) {
|
|
195
|
-
const lines = middlewareContent.split("\n");
|
|
196
|
-
let configLine = 1;
|
|
197
|
-
for (let i = 0; i < lines.length; i++) {
|
|
198
|
-
if (lines[i].includes("config") && lines[i].includes("matcher")) {
|
|
199
|
-
configLine = i + 1;
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
const evidence = [
|
|
204
|
-
{
|
|
205
|
-
file: middlewarePath,
|
|
206
|
-
startLine: configLine,
|
|
207
|
-
endLine: configLine,
|
|
208
|
-
snippet: `matcher: ${JSON.stringify(matchers)}`,
|
|
209
|
-
label: "Middleware matcher does not include /api routes",
|
|
210
|
-
},
|
|
211
|
-
...apiRoutes.slice(0, 2).map((route) => ({
|
|
212
|
-
file: route,
|
|
213
|
-
startLine: 1,
|
|
214
|
-
endLine: 1,
|
|
215
|
-
label: "API route not covered by middleware",
|
|
216
|
-
})),
|
|
217
|
-
];
|
|
218
|
-
const fingerprint = generateFingerprint({
|
|
219
|
-
ruleId: RULE_MW_001,
|
|
220
|
-
file: middlewarePath,
|
|
221
|
-
symbol: "matcher",
|
|
222
|
-
});
|
|
223
|
-
findings.push({
|
|
224
|
-
id: generateFindingId({
|
|
225
|
-
ruleId: RULE_MW_001,
|
|
226
|
-
file: middlewarePath,
|
|
227
|
-
symbol: "matcher",
|
|
228
|
-
}),
|
|
229
|
-
severity: "high",
|
|
230
|
-
confidence: 0.85,
|
|
231
|
-
category: "middleware",
|
|
232
|
-
ruleId: RULE_MW_001,
|
|
233
|
-
title: "Next.js middleware matcher does not cover API routes",
|
|
234
|
-
description: `The middleware.ts file has a matcher configuration that does not include /api routes. Found ${apiRoutes.length} API route(s) that are not protected by middleware. Current matcher: ${JSON.stringify(matchers)}`,
|
|
235
|
-
evidence,
|
|
236
|
-
remediation: {
|
|
237
|
-
recommendedFix: `Update the middleware matcher to include API routes. Example: matcher: ['/((?!_next/static|_next/image|favicon.ico).*)', '/api/:path*']`,
|
|
238
|
-
},
|
|
239
|
-
links: {
|
|
240
|
-
owasp: "https://owasp.org/Top10/A01_2021-Broken_Access_Control/",
|
|
241
|
-
},
|
|
242
|
-
fingerprint,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
return findings;
|
|
246
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { Finding } from "@vibecheck/schema";
|
|
2
|
-
import type { ScanContext } from "../types.js";
|
|
3
|
-
/**
|
|
4
|
-
* VC-PRIV-003: Debug flags enabled in production-ish config
|
|
5
|
-
*
|
|
6
|
-
* Detect:
|
|
7
|
-
* - next.config.* or server config with `dev: true`, `debug: true`, or logging level "debug"
|
|
8
|
-
*
|
|
9
|
-
* Precision:
|
|
10
|
-
* - Only if NODE_ENV is checked or project appears production-ready
|
|
11
|
-
*/
|
|
12
|
-
export declare function scanDebugFlags(context: ScanContext): Promise<Finding[]>;
|
|
13
|
-
//# sourceMappingURL=debug-flags.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"debug-flags.d.ts","sourceRoot":"","sources":["../../../src/scanners/privacy/debug-flags.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CA0H7E"}
|