@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.
Files changed (208) hide show
  1. package/README.md +6 -6
  2. package/dist/index.d.ts +0 -2
  3. package/dist/index.js +7902 -8
  4. package/package.json +13 -7
  5. package/dist/__tests__/cli.test.d.ts +0 -2
  6. package/dist/__tests__/cli.test.d.ts.map +0 -1
  7. package/dist/__tests__/cli.test.js +0 -243
  8. package/dist/__tests__/fixtures/safe-app/app/api/users/route.js +0 -36
  9. package/dist/__tests__/fixtures/vulnerable-app/app/api/users/route.js +0 -28
  10. package/dist/__tests__/fixtures/vulnerable-app/lib/config.d.ts +0 -4
  11. package/dist/__tests__/fixtures/vulnerable-app/lib/config.d.ts.map +0 -1
  12. package/dist/__tests__/fixtures/vulnerable-app/lib/config.js +0 -6
  13. package/dist/__tests__/scanners/env-config.test.d.ts +0 -2
  14. package/dist/__tests__/scanners/env-config.test.d.ts.map +0 -1
  15. package/dist/__tests__/scanners/env-config.test.js +0 -142
  16. package/dist/__tests__/scanners/nextjs-middleware.test.d.ts +0 -2
  17. package/dist/__tests__/scanners/nextjs-middleware.test.d.ts.map +0 -1
  18. package/dist/__tests__/scanners/nextjs-middleware.test.js +0 -193
  19. package/dist/__tests__/scanners/scanner-packs.test.d.ts +0 -2
  20. package/dist/__tests__/scanners/scanner-packs.test.d.ts.map +0 -1
  21. package/dist/__tests__/scanners/scanner-packs.test.js +0 -126
  22. package/dist/__tests__/scanners/unused-security-imports.test.d.ts +0 -2
  23. package/dist/__tests__/scanners/unused-security-imports.test.d.ts.map +0 -1
  24. package/dist/__tests__/scanners/unused-security-imports.test.js +0 -145
  25. package/dist/commands/demo-artifact.d.ts +0 -7
  26. package/dist/commands/demo-artifact.d.ts.map +0 -1
  27. package/dist/commands/demo-artifact.js +0 -322
  28. package/dist/commands/evaluate.d.ts +0 -30
  29. package/dist/commands/evaluate.d.ts.map +0 -1
  30. package/dist/commands/evaluate.js +0 -258
  31. package/dist/commands/explain.d.ts +0 -12
  32. package/dist/commands/explain.d.ts.map +0 -1
  33. package/dist/commands/explain.js +0 -214
  34. package/dist/commands/index.d.ts +0 -7
  35. package/dist/commands/index.d.ts.map +0 -1
  36. package/dist/commands/index.js +0 -6
  37. package/dist/commands/intent.d.ts +0 -21
  38. package/dist/commands/intent.d.ts.map +0 -1
  39. package/dist/commands/intent.js +0 -192
  40. package/dist/commands/scan.d.ts +0 -44
  41. package/dist/commands/scan.d.ts.map +0 -1
  42. package/dist/commands/scan.js +0 -497
  43. package/dist/commands/waivers.d.ts +0 -30
  44. package/dist/commands/waivers.d.ts.map +0 -1
  45. package/dist/commands/waivers.js +0 -249
  46. package/dist/index.d.ts.map +0 -1
  47. package/dist/phase3/index.d.ts +0 -11
  48. package/dist/phase3/index.d.ts.map +0 -1
  49. package/dist/phase3/index.js +0 -12
  50. package/dist/phase3/intent-miner.d.ts +0 -32
  51. package/dist/phase3/intent-miner.d.ts.map +0 -1
  52. package/dist/phase3/intent-miner.js +0 -323
  53. package/dist/phase3/proof-trace-builder.d.ts +0 -42
  54. package/dist/phase3/proof-trace-builder.d.ts.map +0 -1
  55. package/dist/phase3/proof-trace-builder.js +0 -441
  56. package/dist/phase3/scanners/auth-by-ui-server-gap.d.ts +0 -15
  57. package/dist/phase3/scanners/auth-by-ui-server-gap.d.ts.map +0 -1
  58. package/dist/phase3/scanners/auth-by-ui-server-gap.js +0 -237
  59. package/dist/phase3/scanners/comment-claim-unproven.d.ts +0 -14
  60. package/dist/phase3/scanners/comment-claim-unproven.d.ts.map +0 -1
  61. package/dist/phase3/scanners/comment-claim-unproven.js +0 -161
  62. package/dist/phase3/scanners/index.d.ts +0 -31
  63. package/dist/phase3/scanners/index.d.ts.map +0 -1
  64. package/dist/phase3/scanners/index.js +0 -40
  65. package/dist/phase3/scanners/middleware-assumed-not-matching.d.ts +0 -14
  66. package/dist/phase3/scanners/middleware-assumed-not-matching.d.ts.map +0 -1
  67. package/dist/phase3/scanners/middleware-assumed-not-matching.js +0 -172
  68. package/dist/phase3/scanners/validation-claimed-missing.d.ts +0 -15
  69. package/dist/phase3/scanners/validation-claimed-missing.d.ts.map +0 -1
  70. package/dist/phase3/scanners/validation-claimed-missing.js +0 -204
  71. package/dist/scanners/abuse/compute-abuse.d.ts +0 -20
  72. package/dist/scanners/abuse/compute-abuse.d.ts.map +0 -1
  73. package/dist/scanners/abuse/compute-abuse.js +0 -509
  74. package/dist/scanners/abuse/index.d.ts +0 -12
  75. package/dist/scanners/abuse/index.d.ts.map +0 -1
  76. package/dist/scanners/abuse/index.js +0 -15
  77. package/dist/scanners/auth/index.d.ts +0 -5
  78. package/dist/scanners/auth/index.d.ts.map +0 -1
  79. package/dist/scanners/auth/index.js +0 -10
  80. package/dist/scanners/auth/middleware-gap.d.ts +0 -22
  81. package/dist/scanners/auth/middleware-gap.d.ts.map +0 -1
  82. package/dist/scanners/auth/middleware-gap.js +0 -203
  83. package/dist/scanners/auth/unprotected-api-route.d.ts +0 -12
  84. package/dist/scanners/auth/unprotected-api-route.d.ts.map +0 -1
  85. package/dist/scanners/auth/unprotected-api-route.js +0 -126
  86. package/dist/scanners/config/index.d.ts +0 -5
  87. package/dist/scanners/config/index.d.ts.map +0 -1
  88. package/dist/scanners/config/index.js +0 -10
  89. package/dist/scanners/config/insecure-defaults.d.ts +0 -12
  90. package/dist/scanners/config/insecure-defaults.d.ts.map +0 -1
  91. package/dist/scanners/config/insecure-defaults.js +0 -77
  92. package/dist/scanners/config/undocumented-env.d.ts +0 -24
  93. package/dist/scanners/config/undocumented-env.d.ts.map +0 -1
  94. package/dist/scanners/config/undocumented-env.js +0 -159
  95. package/dist/scanners/crypto/index.d.ts +0 -6
  96. package/dist/scanners/crypto/index.d.ts.map +0 -1
  97. package/dist/scanners/crypto/index.js +0 -11
  98. package/dist/scanners/crypto/jwt-decode-unverified.d.ts +0 -14
  99. package/dist/scanners/crypto/jwt-decode-unverified.d.ts.map +0 -1
  100. package/dist/scanners/crypto/jwt-decode-unverified.js +0 -87
  101. package/dist/scanners/crypto/math-random-tokens.d.ts +0 -13
  102. package/dist/scanners/crypto/math-random-tokens.d.ts.map +0 -1
  103. package/dist/scanners/crypto/math-random-tokens.js +0 -80
  104. package/dist/scanners/crypto/weak-hashing.d.ts +0 -11
  105. package/dist/scanners/crypto/weak-hashing.d.ts.map +0 -1
  106. package/dist/scanners/crypto/weak-hashing.js +0 -95
  107. package/dist/scanners/env-config.d.ts +0 -24
  108. package/dist/scanners/env-config.d.ts.map +0 -1
  109. package/dist/scanners/env-config.js +0 -164
  110. package/dist/scanners/hallucinations/index.d.ts +0 -4
  111. package/dist/scanners/hallucinations/index.d.ts.map +0 -1
  112. package/dist/scanners/hallucinations/index.js +0 -8
  113. package/dist/scanners/hallucinations/unused-security-imports.d.ts +0 -36
  114. package/dist/scanners/hallucinations/unused-security-imports.d.ts.map +0 -1
  115. package/dist/scanners/hallucinations/unused-security-imports.js +0 -309
  116. package/dist/scanners/helpers/ast-helpers.d.ts +0 -6
  117. package/dist/scanners/helpers/ast-helpers.d.ts.map +0 -1
  118. package/dist/scanners/helpers/ast-helpers.js +0 -945
  119. package/dist/scanners/helpers/context-builder.d.ts +0 -17
  120. package/dist/scanners/helpers/context-builder.d.ts.map +0 -1
  121. package/dist/scanners/helpers/context-builder.js +0 -148
  122. package/dist/scanners/helpers/index.d.ts +0 -3
  123. package/dist/scanners/helpers/index.d.ts.map +0 -1
  124. package/dist/scanners/helpers/index.js +0 -2
  125. package/dist/scanners/index.d.ts +0 -30
  126. package/dist/scanners/index.d.ts.map +0 -1
  127. package/dist/scanners/index.js +0 -102
  128. package/dist/scanners/middleware/index.d.ts +0 -4
  129. package/dist/scanners/middleware/index.d.ts.map +0 -1
  130. package/dist/scanners/middleware/index.js +0 -7
  131. package/dist/scanners/middleware/missing-rate-limit.d.ts +0 -13
  132. package/dist/scanners/middleware/missing-rate-limit.d.ts.map +0 -1
  133. package/dist/scanners/middleware/missing-rate-limit.js +0 -140
  134. package/dist/scanners/network/cors-misconfiguration.d.ts +0 -14
  135. package/dist/scanners/network/cors-misconfiguration.d.ts.map +0 -1
  136. package/dist/scanners/network/cors-misconfiguration.js +0 -89
  137. package/dist/scanners/network/index.d.ts +0 -7
  138. package/dist/scanners/network/index.d.ts.map +0 -1
  139. package/dist/scanners/network/index.js +0 -18
  140. package/dist/scanners/network/missing-timeout.d.ts +0 -15
  141. package/dist/scanners/network/missing-timeout.d.ts.map +0 -1
  142. package/dist/scanners/network/missing-timeout.js +0 -93
  143. package/dist/scanners/network/open-redirect.d.ts +0 -15
  144. package/dist/scanners/network/open-redirect.d.ts.map +0 -1
  145. package/dist/scanners/network/open-redirect.js +0 -88
  146. package/dist/scanners/network/ssrf-prone-fetch.d.ts +0 -12
  147. package/dist/scanners/network/ssrf-prone-fetch.d.ts.map +0 -1
  148. package/dist/scanners/network/ssrf-prone-fetch.js +0 -90
  149. package/dist/scanners/nextjs-middleware.d.ts +0 -26
  150. package/dist/scanners/nextjs-middleware.d.ts.map +0 -1
  151. package/dist/scanners/nextjs-middleware.js +0 -246
  152. package/dist/scanners/privacy/debug-flags.d.ts +0 -13
  153. package/dist/scanners/privacy/debug-flags.d.ts.map +0 -1
  154. package/dist/scanners/privacy/debug-flags.js +0 -124
  155. package/dist/scanners/privacy/index.d.ts +0 -6
  156. package/dist/scanners/privacy/index.d.ts.map +0 -1
  157. package/dist/scanners/privacy/index.js +0 -11
  158. package/dist/scanners/privacy/over-broad-response.d.ts +0 -15
  159. package/dist/scanners/privacy/over-broad-response.d.ts.map +0 -1
  160. package/dist/scanners/privacy/over-broad-response.js +0 -109
  161. package/dist/scanners/privacy/sensitive-logging.d.ts +0 -11
  162. package/dist/scanners/privacy/sensitive-logging.d.ts.map +0 -1
  163. package/dist/scanners/privacy/sensitive-logging.js +0 -78
  164. package/dist/scanners/types.d.ts +0 -456
  165. package/dist/scanners/types.d.ts.map +0 -1
  166. package/dist/scanners/types.js +0 -16
  167. package/dist/scanners/unused-security-imports.d.ts +0 -34
  168. package/dist/scanners/unused-security-imports.d.ts.map +0 -1
  169. package/dist/scanners/unused-security-imports.js +0 -206
  170. package/dist/scanners/uploads/index.d.ts +0 -5
  171. package/dist/scanners/uploads/index.d.ts.map +0 -1
  172. package/dist/scanners/uploads/index.js +0 -9
  173. package/dist/scanners/uploads/missing-constraints.d.ts +0 -15
  174. package/dist/scanners/uploads/missing-constraints.d.ts.map +0 -1
  175. package/dist/scanners/uploads/missing-constraints.js +0 -109
  176. package/dist/scanners/uploads/public-path.d.ts +0 -11
  177. package/dist/scanners/uploads/public-path.d.ts.map +0 -1
  178. package/dist/scanners/uploads/public-path.js +0 -87
  179. package/dist/scanners/validation/client-side-only.d.ts +0 -14
  180. package/dist/scanners/validation/client-side-only.d.ts.map +0 -1
  181. package/dist/scanners/validation/client-side-only.js +0 -140
  182. package/dist/scanners/validation/ignored-validation.d.ts +0 -12
  183. package/dist/scanners/validation/ignored-validation.d.ts.map +0 -1
  184. package/dist/scanners/validation/ignored-validation.js +0 -119
  185. package/dist/scanners/validation/index.d.ts +0 -5
  186. package/dist/scanners/validation/index.d.ts.map +0 -1
  187. package/dist/scanners/validation/index.js +0 -9
  188. package/dist/utils/exclude-patterns.d.ts +0 -35
  189. package/dist/utils/exclude-patterns.d.ts.map +0 -1
  190. package/dist/utils/exclude-patterns.js +0 -78
  191. package/dist/utils/file-utils.d.ts +0 -37
  192. package/dist/utils/file-utils.d.ts.map +0 -1
  193. package/dist/utils/file-utils.js +0 -77
  194. package/dist/utils/fingerprint.d.ts +0 -25
  195. package/dist/utils/fingerprint.d.ts.map +0 -1
  196. package/dist/utils/fingerprint.js +0 -28
  197. package/dist/utils/git-info.d.ts +0 -14
  198. package/dist/utils/git-info.d.ts.map +0 -1
  199. package/dist/utils/git-info.js +0 -55
  200. package/dist/utils/index.d.ts +0 -4
  201. package/dist/utils/index.d.ts.map +0 -1
  202. package/dist/utils/index.js +0 -3
  203. package/dist/utils/progress.d.ts +0 -42
  204. package/dist/utils/progress.d.ts.map +0 -1
  205. package/dist/utils/progress.js +0 -165
  206. package/dist/utils/sarif-formatter.d.ts +0 -92
  207. package/dist/utils/sarif-formatter.d.ts.map +0 -1
  208. 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"}