@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,206 +0,0 @@
|
|
|
1
|
-
import { readFileSync, resolvePath } from "../utils/file-utils.js";
|
|
2
|
-
import { generateFingerprint, generateFindingId } from "../utils/fingerprint.js";
|
|
3
|
-
const RULE_ID = "VC-HALL-001";
|
|
4
|
-
const SECURITY_LIBS = [
|
|
5
|
-
{
|
|
6
|
-
name: "zod",
|
|
7
|
-
category: "validation",
|
|
8
|
-
severity: "medium",
|
|
9
|
-
description: "Schema validation library",
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
name: "yup",
|
|
13
|
-
category: "validation",
|
|
14
|
-
severity: "medium",
|
|
15
|
-
description: "Schema validation library",
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
name: "joi",
|
|
19
|
-
category: "validation",
|
|
20
|
-
severity: "medium",
|
|
21
|
-
description: "Schema validation library",
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
name: "helmet",
|
|
25
|
-
category: "middleware",
|
|
26
|
-
severity: "medium",
|
|
27
|
-
description: "Security headers middleware",
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: "cors",
|
|
31
|
-
category: "middleware",
|
|
32
|
-
severity: "low",
|
|
33
|
-
description: "CORS middleware",
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: "csurf",
|
|
37
|
-
category: "middleware",
|
|
38
|
-
severity: "medium",
|
|
39
|
-
description: "CSRF protection middleware",
|
|
40
|
-
},
|
|
41
|
-
];
|
|
42
|
-
/**
|
|
43
|
-
* Find imports of security libraries in a file
|
|
44
|
-
*
|
|
45
|
-
* Limitations:
|
|
46
|
-
* - Uses regex, may match imports in comments
|
|
47
|
-
* - Does not handle dynamic imports: import('zod')
|
|
48
|
-
* - Does not track re-exports
|
|
49
|
-
*/
|
|
50
|
-
export function findSecurityImports(content, libraries) {
|
|
51
|
-
const imports = [];
|
|
52
|
-
const lines = content.split("\n");
|
|
53
|
-
const libPattern = libraries.join("|");
|
|
54
|
-
// Match: import X from 'lib' / import { x, y } from 'lib' / import * as X from 'lib'
|
|
55
|
-
const importRegex = new RegExp(`^\\s*import\\s+(?:([\\w]+)|\\{([^}]+)\\}|\\*\\s+as\\s+([\\w]+))\\s+from\\s+['"](?:${libPattern})['"]`, "gm");
|
|
56
|
-
for (let i = 0; i < lines.length; i++) {
|
|
57
|
-
const line = lines[i];
|
|
58
|
-
importRegex.lastIndex = 0;
|
|
59
|
-
for (const lib of libraries) {
|
|
60
|
-
// Check each library pattern
|
|
61
|
-
const patterns = [
|
|
62
|
-
// import X from 'lib'
|
|
63
|
-
new RegExp(`^\\s*import\\s+(\\w+)\\s+from\\s+['"]${lib}['"]`),
|
|
64
|
-
// import { x, y } from 'lib'
|
|
65
|
-
new RegExp(`^\\s*import\\s+\\{([^}]+)\\}\\s+from\\s+['"]${lib}['"]`),
|
|
66
|
-
// import * as X from 'lib'
|
|
67
|
-
new RegExp(`^\\s*import\\s+\\*\\s+as\\s+(\\w+)\\s+from\\s+['"]${lib}['"]`),
|
|
68
|
-
// import X, { y } from 'lib'
|
|
69
|
-
new RegExp(`^\\s*import\\s+(\\w+)\\s*,\\s*\\{([^}]+)\\}\\s+from\\s+['"]${lib}['"]`),
|
|
70
|
-
];
|
|
71
|
-
for (let p = 0; p < patterns.length; p++) {
|
|
72
|
-
const match = line.match(patterns[p]);
|
|
73
|
-
if (match) {
|
|
74
|
-
const importedNames = [];
|
|
75
|
-
let isDefaultImport = false;
|
|
76
|
-
let isNamespaceImport = false;
|
|
77
|
-
if (p === 0) {
|
|
78
|
-
// Default import
|
|
79
|
-
importedNames.push(match[1]);
|
|
80
|
-
isDefaultImport = true;
|
|
81
|
-
}
|
|
82
|
-
else if (p === 1) {
|
|
83
|
-
// Named imports
|
|
84
|
-
const names = match[1].split(",").map((n) => n.trim().split(/\s+as\s+/)[0].trim());
|
|
85
|
-
importedNames.push(...names.filter(Boolean));
|
|
86
|
-
}
|
|
87
|
-
else if (p === 2) {
|
|
88
|
-
// Namespace import
|
|
89
|
-
importedNames.push(match[1]);
|
|
90
|
-
isNamespaceImport = true;
|
|
91
|
-
}
|
|
92
|
-
else if (p === 3) {
|
|
93
|
-
// Default + named
|
|
94
|
-
importedNames.push(match[1]);
|
|
95
|
-
isDefaultImport = true;
|
|
96
|
-
const names = match[2].split(",").map((n) => n.trim().split(/\s+as\s+/)[0].trim());
|
|
97
|
-
importedNames.push(...names.filter(Boolean));
|
|
98
|
-
}
|
|
99
|
-
imports.push({
|
|
100
|
-
library: lib,
|
|
101
|
-
importedNames,
|
|
102
|
-
line: i + 1,
|
|
103
|
-
snippet: line.trim(),
|
|
104
|
-
isDefaultImport,
|
|
105
|
-
isNamespaceImport,
|
|
106
|
-
});
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return imports;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Check if any imported identifiers are used after the import line
|
|
116
|
-
*/
|
|
117
|
-
export function checkIdentifierUsage(content, importLine, identifiers, isNamespaceImport) {
|
|
118
|
-
const lines = content.split("\n");
|
|
119
|
-
const afterImport = lines.slice(importLine).join("\n");
|
|
120
|
-
return identifiers.map((id) => {
|
|
121
|
-
// For namespace imports, check for namespace.something
|
|
122
|
-
if (isNamespaceImport) {
|
|
123
|
-
const namespaceRegex = new RegExp(`\\b${id}\\s*\\.`, "g");
|
|
124
|
-
return { identifier: id, used: namespaceRegex.test(afterImport) };
|
|
125
|
-
}
|
|
126
|
-
// For regular imports, check for identifier usage
|
|
127
|
-
// Match the identifier as a word boundary, not just as part of another word
|
|
128
|
-
const usageRegex = new RegExp(`\\b${id}\\b`, "g");
|
|
129
|
-
const matches = afterImport.match(usageRegex);
|
|
130
|
-
// If found at least once, it's used (the import line itself is excluded)
|
|
131
|
-
return { identifier: id, used: matches !== null && matches.length > 0 };
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Unused Security Imports Scanner
|
|
136
|
-
*
|
|
137
|
-
* Detects when security libraries are imported but not used
|
|
138
|
-
*/
|
|
139
|
-
export async function scanUnusedSecurityImports(context) {
|
|
140
|
-
const { targetDir, sourceFiles } = context;
|
|
141
|
-
const findings = [];
|
|
142
|
-
const libNames = SECURITY_LIBS.map((l) => l.name);
|
|
143
|
-
for (const relFile of sourceFiles) {
|
|
144
|
-
const absPath = resolvePath(targetDir, relFile);
|
|
145
|
-
const content = readFileSync(absPath);
|
|
146
|
-
if (!content)
|
|
147
|
-
continue;
|
|
148
|
-
const imports = findSecurityImports(content, libNames);
|
|
149
|
-
for (const imp of imports) {
|
|
150
|
-
const libInfo = SECURITY_LIBS.find((l) => l.name === imp.library);
|
|
151
|
-
if (!libInfo)
|
|
152
|
-
continue;
|
|
153
|
-
const usageResults = checkIdentifierUsage(content, imp.line, imp.importedNames, imp.isNamespaceImport);
|
|
154
|
-
const unusedIdentifiers = usageResults
|
|
155
|
-
.filter((r) => !r.used)
|
|
156
|
-
.map((r) => r.identifier);
|
|
157
|
-
if (unusedIdentifiers.length === 0)
|
|
158
|
-
continue;
|
|
159
|
-
// All imported identifiers are unused
|
|
160
|
-
const allUnused = unusedIdentifiers.length === imp.importedNames.length;
|
|
161
|
-
const evidence = [
|
|
162
|
-
{
|
|
163
|
-
file: relFile,
|
|
164
|
-
startLine: imp.line,
|
|
165
|
-
endLine: imp.line,
|
|
166
|
-
snippet: imp.snippet,
|
|
167
|
-
label: allUnused
|
|
168
|
-
? `Unused import of ${imp.library}`
|
|
169
|
-
: `Partially unused import: ${unusedIdentifiers.join(", ")}`,
|
|
170
|
-
},
|
|
171
|
-
];
|
|
172
|
-
const fingerprint = generateFingerprint({
|
|
173
|
-
ruleId: RULE_ID,
|
|
174
|
-
file: relFile,
|
|
175
|
-
symbol: imp.library,
|
|
176
|
-
startLine: imp.line,
|
|
177
|
-
});
|
|
178
|
-
findings.push({
|
|
179
|
-
id: generateFindingId({
|
|
180
|
-
ruleId: RULE_ID,
|
|
181
|
-
file: relFile,
|
|
182
|
-
symbol: imp.library,
|
|
183
|
-
startLine: imp.line,
|
|
184
|
-
}),
|
|
185
|
-
severity: libInfo.severity,
|
|
186
|
-
confidence: allUnused ? 0.9 : 0.75,
|
|
187
|
-
category: libInfo.category,
|
|
188
|
-
ruleId: RULE_ID,
|
|
189
|
-
title: allUnused
|
|
190
|
-
? `Imported ${libInfo.description.toLowerCase()} "${imp.library}" is not used`
|
|
191
|
-
: `Partially unused import from "${imp.library}"`,
|
|
192
|
-
description: allUnused
|
|
193
|
-
? `The security library "${imp.library}" is imported but none of its exports are used. This may indicate incomplete implementation of ${libInfo.category} functionality.`
|
|
194
|
-
: `Some exports from "${imp.library}" (${unusedIdentifiers.join(", ")}) are imported but not used. This may indicate incomplete implementation.`,
|
|
195
|
-
evidence,
|
|
196
|
-
remediation: {
|
|
197
|
-
recommendedFix: allUnused
|
|
198
|
-
? `Either implement ${libInfo.category} using "${imp.library}" or remove the unused import.`
|
|
199
|
-
: `Remove unused imports (${unusedIdentifiers.join(", ")}) or implement their usage.`,
|
|
200
|
-
},
|
|
201
|
-
fingerprint,
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return findings;
|
|
206
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/scanners/uploads/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,eAAO,MAAM,WAAW,EAAE,WAIzB,CAAC;AAEF,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { scanMissingUploadConstraints } from "./missing-constraints.js";
|
|
2
|
-
import { scanPublicUploadPath } from "./public-path.js";
|
|
3
|
-
export const uploadsPack = {
|
|
4
|
-
id: "uploads",
|
|
5
|
-
name: "File Upload Security",
|
|
6
|
-
scanners: [scanMissingUploadConstraints, scanPublicUploadPath],
|
|
7
|
-
};
|
|
8
|
-
export { scanMissingUploadConstraints } from "./missing-constraints.js";
|
|
9
|
-
export { scanPublicUploadPath } from "./public-path.js";
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { Finding } from "@vibecheck/schema";
|
|
2
|
-
import type { ScanContext } from "../types.js";
|
|
3
|
-
/**
|
|
4
|
-
* VC-UP-001: File upload without size/type constraints
|
|
5
|
-
*
|
|
6
|
-
* Targets:
|
|
7
|
-
* - multer usage without limits/fileFilter
|
|
8
|
-
* - formidable/busboy without size checks
|
|
9
|
-
* - Next.js route handlers reading formData and accepting file without checking type/size
|
|
10
|
-
*
|
|
11
|
-
* Precision:
|
|
12
|
-
* - Only flag if a file is accepted AND there is no check of file.size, file.type, or limits
|
|
13
|
-
*/
|
|
14
|
-
export declare function scanMissingUploadConstraints(context: ScanContext): Promise<Finding[]>;
|
|
15
|
-
//# sourceMappingURL=missing-constraints.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"missing-constraints.d.ts","sourceRoot":"","sources":["../../../src/scanners/uploads/missing-constraints.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,4BAA4B,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAoG3F"}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { resolvePath } from "../../utils/file-utils.js";
|
|
2
|
-
import { generateFingerprint, generateFindingId } from "../../utils/fingerprint.js";
|
|
3
|
-
const RULE_ID = "VC-UP-001";
|
|
4
|
-
/**
|
|
5
|
-
* VC-UP-001: File upload without size/type constraints
|
|
6
|
-
*
|
|
7
|
-
* Targets:
|
|
8
|
-
* - multer usage without limits/fileFilter
|
|
9
|
-
* - formidable/busboy without size checks
|
|
10
|
-
* - Next.js route handlers reading formData and accepting file without checking type/size
|
|
11
|
-
*
|
|
12
|
-
* Precision:
|
|
13
|
-
* - Only flag if a file is accepted AND there is no check of file.size, file.type, or limits
|
|
14
|
-
*/
|
|
15
|
-
export async function scanMissingUploadConstraints(context) {
|
|
16
|
-
const { repoRoot, fileIndex, helpers } = context;
|
|
17
|
-
const findings = [];
|
|
18
|
-
// 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 uploadHandlers = helpers.findFileUploadHandlers(handler.functionNode);
|
|
27
|
-
for (const upload of uploadHandlers) {
|
|
28
|
-
// Only flag if missing both size and type checks
|
|
29
|
-
if (upload.hasSizeCheck && upload.hasTypeCheck)
|
|
30
|
-
continue;
|
|
31
|
-
const missingChecks = [];
|
|
32
|
-
if (!upload.hasSizeCheck)
|
|
33
|
-
missingChecks.push("size limit");
|
|
34
|
-
if (!upload.hasTypeCheck)
|
|
35
|
-
missingChecks.push("type validation");
|
|
36
|
-
const evidence = [
|
|
37
|
-
{
|
|
38
|
-
file: relPath,
|
|
39
|
-
startLine: upload.line,
|
|
40
|
-
endLine: upload.line,
|
|
41
|
-
snippet: upload.snippet,
|
|
42
|
-
label: `File upload via ${upload.uploadMethod} without ${missingChecks.join(" or ")}`,
|
|
43
|
-
},
|
|
44
|
-
];
|
|
45
|
-
const fingerprint = generateFingerprint({
|
|
46
|
-
ruleId: RULE_ID,
|
|
47
|
-
file: relPath,
|
|
48
|
-
symbol: upload.uploadMethod,
|
|
49
|
-
startLine: upload.line,
|
|
50
|
-
});
|
|
51
|
-
findings.push({
|
|
52
|
-
id: generateFindingId({
|
|
53
|
-
ruleId: RULE_ID,
|
|
54
|
-
file: relPath,
|
|
55
|
-
symbol: upload.uploadMethod,
|
|
56
|
-
startLine: upload.line,
|
|
57
|
-
}),
|
|
58
|
-
ruleId: RULE_ID,
|
|
59
|
-
title: `File upload missing ${missingChecks.join(" and ")}`,
|
|
60
|
-
description: `This file upload handler using ${upload.uploadMethod} lacks ${missingChecks.join(" and ")}. Without size limits, attackers can upload extremely large files causing denial of service. Without type validation, attackers may upload malicious executables, scripts, or unexpected content types.`,
|
|
61
|
-
severity: "high",
|
|
62
|
-
confidence: 0.8,
|
|
63
|
-
category: "uploads",
|
|
64
|
-
evidence,
|
|
65
|
-
remediation: {
|
|
66
|
-
recommendedFix: `Add both size limits and file type validation to your upload handler.`,
|
|
67
|
-
patch: upload.uploadMethod === "multer"
|
|
68
|
-
? `// For multer, add limits and fileFilter:
|
|
69
|
-
const upload = multer({
|
|
70
|
-
limits: {
|
|
71
|
-
fileSize: 5 * 1024 * 1024, // 5MB limit
|
|
72
|
-
files: 1, // Single file
|
|
73
|
-
},
|
|
74
|
-
fileFilter: (req, file, cb) => {
|
|
75
|
-
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
|
|
76
|
-
if (allowedTypes.includes(file.mimetype)) {
|
|
77
|
-
cb(null, true);
|
|
78
|
-
} else {
|
|
79
|
-
cb(new Error('Invalid file type'));
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
});`
|
|
83
|
-
: `// For Next.js formData, validate the file:
|
|
84
|
-
const formData = await request.formData();
|
|
85
|
-
const file = formData.get('file') as File;
|
|
86
|
-
|
|
87
|
-
// Check file size
|
|
88
|
-
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
|
|
89
|
-
if (file.size > MAX_SIZE) {
|
|
90
|
-
return Response.json({ error: 'File too large' }, { status: 400 });
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Check file type
|
|
94
|
-
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
|
|
95
|
-
if (!allowedTypes.includes(file.type)) {
|
|
96
|
-
return Response.json({ error: 'Invalid file type' }, { status: 400 });
|
|
97
|
-
}`,
|
|
98
|
-
},
|
|
99
|
-
links: {
|
|
100
|
-
cwe: "https://cwe.mitre.org/data/definitions/434.html",
|
|
101
|
-
owasp: "https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html",
|
|
102
|
-
},
|
|
103
|
-
fingerprint,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return findings;
|
|
109
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Finding } from "@vibecheck/schema";
|
|
2
|
-
import type { ScanContext } from "../types.js";
|
|
3
|
-
/**
|
|
4
|
-
* VC-UP-002: Upload served from public path
|
|
5
|
-
*
|
|
6
|
-
* Detect:
|
|
7
|
-
* - Writing uploaded file to `public/` or `static/` directories
|
|
8
|
-
* - Or referencing user-supplied filenames in public URL generation
|
|
9
|
-
*/
|
|
10
|
-
export declare function scanPublicUploadPath(context: ScanContext): Promise<Finding[]>;
|
|
11
|
-
//# sourceMappingURL=public-path.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"public-path.d.ts","sourceRoot":"","sources":["../../../src/scanners/uploads/public-path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAmFnF"}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { resolvePath } from "../../utils/file-utils.js";
|
|
2
|
-
import { generateFingerprint, generateFindingId } from "../../utils/fingerprint.js";
|
|
3
|
-
const RULE_ID = "VC-UP-002";
|
|
4
|
-
/**
|
|
5
|
-
* VC-UP-002: Upload served from public path
|
|
6
|
-
*
|
|
7
|
-
* Detect:
|
|
8
|
-
* - Writing uploaded file to `public/` or `static/` directories
|
|
9
|
-
* - Or referencing user-supplied filenames in public URL generation
|
|
10
|
-
*/
|
|
11
|
-
export async function scanPublicUploadPath(context) {
|
|
12
|
-
const { repoRoot, fileIndex, helpers } = context;
|
|
13
|
-
const findings = [];
|
|
14
|
-
for (const relPath of fileIndex.allSourceFiles) {
|
|
15
|
-
const absPath = resolvePath(repoRoot, relPath);
|
|
16
|
-
const sourceFile = helpers.parseFile(absPath);
|
|
17
|
-
if (!sourceFile)
|
|
18
|
-
continue;
|
|
19
|
-
const publicWrites = helpers.findPublicFileWrites(sourceFile);
|
|
20
|
-
for (const write of publicWrites) {
|
|
21
|
-
if (!write.isPublicDir)
|
|
22
|
-
continue;
|
|
23
|
-
const evidence = [
|
|
24
|
-
{
|
|
25
|
-
file: relPath,
|
|
26
|
-
startLine: write.line,
|
|
27
|
-
endLine: write.line,
|
|
28
|
-
snippet: write.snippet,
|
|
29
|
-
label: `File written to public path${write.usesUserFilename ? " with user-supplied filename" : ""}`,
|
|
30
|
-
},
|
|
31
|
-
];
|
|
32
|
-
const fingerprint = generateFingerprint({
|
|
33
|
-
ruleId: RULE_ID,
|
|
34
|
-
file: relPath,
|
|
35
|
-
symbol: "public-write",
|
|
36
|
-
startLine: write.line,
|
|
37
|
-
});
|
|
38
|
-
const severity = write.usesUserFilename ? "high" : "medium";
|
|
39
|
-
findings.push({
|
|
40
|
-
id: generateFindingId({
|
|
41
|
-
ruleId: RULE_ID,
|
|
42
|
-
file: relPath,
|
|
43
|
-
symbol: "public-write",
|
|
44
|
-
startLine: write.line,
|
|
45
|
-
}),
|
|
46
|
-
ruleId: RULE_ID,
|
|
47
|
-
title: `Uploaded file written to public directory${write.usesUserFilename ? " with user filename" : ""}`,
|
|
48
|
-
description: `Files are being written to a publicly accessible directory (${write.writePath}).${write.usesUserFilename ? " The filename appears to come from user input, which could allow path traversal attacks or filename-based exploits." : ""} Publicly accessible uploads can be directly accessed by anyone and may be executed by the server if not properly configured.`,
|
|
49
|
-
severity,
|
|
50
|
-
confidence: 0.85,
|
|
51
|
-
category: "uploads",
|
|
52
|
-
evidence,
|
|
53
|
-
remediation: {
|
|
54
|
-
recommendedFix: `Store uploads outside the public directory and serve them through a controlled API endpoint. Always sanitize and generate safe filenames.`,
|
|
55
|
-
patch: `// DON'T: Write directly to public folder
|
|
56
|
-
// fs.writeFile(path.join('public', filename), buffer);
|
|
57
|
-
|
|
58
|
-
// DO: Store outside public with generated names
|
|
59
|
-
import { randomUUID } from 'crypto';
|
|
60
|
-
import path from 'path';
|
|
61
|
-
|
|
62
|
-
// Generate safe filename
|
|
63
|
-
const ext = path.extname(originalFilename).toLowerCase();
|
|
64
|
-
const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif'];
|
|
65
|
-
if (!allowedExtensions.includes(ext)) {
|
|
66
|
-
throw new Error('Invalid file extension');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const safeFilename = \`\${randomUUID()}\${ext}\`;
|
|
70
|
-
const uploadPath = path.join(process.cwd(), 'uploads', safeFilename);
|
|
71
|
-
|
|
72
|
-
// Write to non-public directory
|
|
73
|
-
await fs.writeFile(uploadPath, buffer);
|
|
74
|
-
|
|
75
|
-
// Serve through API route:
|
|
76
|
-
// GET /api/files/[id] -> Stream file from uploads directory`,
|
|
77
|
-
},
|
|
78
|
-
links: {
|
|
79
|
-
cwe: "https://cwe.mitre.org/data/definitions/434.html",
|
|
80
|
-
owasp: "https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html",
|
|
81
|
-
},
|
|
82
|
-
fingerprint,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return findings;
|
|
87
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { Finding } from "@vibecheck/schema";
|
|
2
|
-
import type { ScanContext } from "../types.js";
|
|
3
|
-
/**
|
|
4
|
-
* VC-VAL-002: Client-side-only validation
|
|
5
|
-
*
|
|
6
|
-
* Detects if validation libraries (zod/yup/joi) are used in frontend components
|
|
7
|
-
* but API route handlers accept raw input without validation.
|
|
8
|
-
*
|
|
9
|
-
* Precision rule:
|
|
10
|
-
* - Only flag if there exists at least one validation schema used in frontend
|
|
11
|
-
* - AND at least one API route file exists that uses req.json without validation
|
|
12
|
-
*/
|
|
13
|
-
export declare function scanClientSideOnlyValidation(context: ScanContext): Promise<Finding[]>;
|
|
14
|
-
//# sourceMappingURL=client-side-only.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client-side-only.d.ts","sourceRoot":"","sources":["../../../src/scanners/validation/client-side-only.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C;;;;;;;;;GASG;AACH,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAyI3F"}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { resolvePath } from "../../utils/file-utils.js";
|
|
2
|
-
import { generateFingerprint, generateFindingId } from "../../utils/fingerprint.js";
|
|
3
|
-
const RULE_ID = "VC-VAL-002";
|
|
4
|
-
/**
|
|
5
|
-
* VC-VAL-002: Client-side-only validation
|
|
6
|
-
*
|
|
7
|
-
* Detects if validation libraries (zod/yup/joi) are used in frontend components
|
|
8
|
-
* but API route handlers accept raw input without validation.
|
|
9
|
-
*
|
|
10
|
-
* Precision rule:
|
|
11
|
-
* - Only flag if there exists at least one validation schema used in frontend
|
|
12
|
-
* - AND at least one API route file exists that uses req.json without validation
|
|
13
|
-
*/
|
|
14
|
-
export async function scanClientSideOnlyValidation(context) {
|
|
15
|
-
const { repoRoot, fileIndex, helpers } = context;
|
|
16
|
-
const findings = [];
|
|
17
|
-
// Find frontend files with validation
|
|
18
|
-
const frontendFilesWithValidation = [];
|
|
19
|
-
for (const relPath of fileIndex.allSourceFiles) {
|
|
20
|
-
// Check if it's a frontend file (components, pages, but NOT api routes)
|
|
21
|
-
const isFrontend = (relPath.includes("/components/") ||
|
|
22
|
-
relPath.includes("\\components\\") ||
|
|
23
|
-
relPath.includes("/app/") ||
|
|
24
|
-
relPath.includes("\\app\\")) &&
|
|
25
|
-
!relPath.includes("/api/") &&
|
|
26
|
-
!relPath.includes("\\api\\") &&
|
|
27
|
-
(relPath.endsWith(".tsx") || relPath.endsWith(".jsx"));
|
|
28
|
-
if (!isFrontend)
|
|
29
|
-
continue;
|
|
30
|
-
const absPath = resolvePath(repoRoot, relPath);
|
|
31
|
-
const sourceFile = helpers.parseFile(absPath);
|
|
32
|
-
if (!sourceFile)
|
|
33
|
-
continue;
|
|
34
|
-
if (helpers.hasValidationSchemas(sourceFile)) {
|
|
35
|
-
frontendFilesWithValidation.push(relPath);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// If no frontend validation found, skip
|
|
39
|
-
if (frontendFilesWithValidation.length === 0) {
|
|
40
|
-
return findings;
|
|
41
|
-
}
|
|
42
|
-
// Check API routes for missing server-side validation
|
|
43
|
-
for (const relPath of fileIndex.apiRouteFiles) {
|
|
44
|
-
const absPath = resolvePath(repoRoot, relPath);
|
|
45
|
-
const sourceFile = helpers.parseFile(absPath);
|
|
46
|
-
if (!sourceFile)
|
|
47
|
-
continue;
|
|
48
|
-
const handlers = helpers.findRouteHandlers(sourceFile);
|
|
49
|
-
const fileHasValidation = helpers.hasValidationSchemas(sourceFile);
|
|
50
|
-
for (const handler of handlers) {
|
|
51
|
-
// Only check POST/PUT/PATCH handlers that accept body
|
|
52
|
-
if (!["POST", "PUT", "PATCH"].includes(handler.method))
|
|
53
|
-
continue;
|
|
54
|
-
const handlerText = helpers.getNodeText(handler.functionNode);
|
|
55
|
-
// Check if handler reads request body
|
|
56
|
-
const readsBody = /\.json\s*\(\s*\)|\.body\b|await\s+request\.json\s*\(\s*\)/.test(handlerText);
|
|
57
|
-
if (!readsBody)
|
|
58
|
-
continue;
|
|
59
|
-
// Check if handler has validation
|
|
60
|
-
const validationUsages = helpers.findValidationUsage(handler.functionNode);
|
|
61
|
-
const hasValidation = validationUsages.length > 0 || fileHasValidation;
|
|
62
|
-
if (hasValidation)
|
|
63
|
-
continue;
|
|
64
|
-
const evidence = [
|
|
65
|
-
{
|
|
66
|
-
file: relPath,
|
|
67
|
-
startLine: handler.startLine,
|
|
68
|
-
endLine: handler.endLine,
|
|
69
|
-
snippet: handlerText.slice(0, 200) + "...",
|
|
70
|
-
label: `${handler.method} handler accepts request body without server-side validation`,
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
file: frontendFilesWithValidation[0],
|
|
74
|
-
startLine: 1,
|
|
75
|
-
endLine: 1,
|
|
76
|
-
snippet: `Validation schema found in frontend component`,
|
|
77
|
-
label: "Frontend validation exists but server validation missing",
|
|
78
|
-
},
|
|
79
|
-
];
|
|
80
|
-
const fingerprint = generateFingerprint({
|
|
81
|
-
ruleId: RULE_ID,
|
|
82
|
-
file: relPath,
|
|
83
|
-
symbol: handler.method,
|
|
84
|
-
startLine: handler.startLine,
|
|
85
|
-
});
|
|
86
|
-
findings.push({
|
|
87
|
-
id: generateFindingId({
|
|
88
|
-
ruleId: RULE_ID,
|
|
89
|
-
file: relPath,
|
|
90
|
-
symbol: handler.method,
|
|
91
|
-
startLine: handler.startLine,
|
|
92
|
-
}),
|
|
93
|
-
ruleId: RULE_ID,
|
|
94
|
-
title: `Client-side-only validation on ${handler.method} endpoint`,
|
|
95
|
-
description: `This API endpoint accepts request body data without server-side validation, while validation schemas exist in frontend components. Client-side validation can be easily bypassed - attackers can send requests directly to your API. Always validate input on the server.`,
|
|
96
|
-
severity: "medium",
|
|
97
|
-
confidence: 0.7,
|
|
98
|
-
category: "validation",
|
|
99
|
-
evidence,
|
|
100
|
-
remediation: {
|
|
101
|
-
recommendedFix: `Add server-side validation using the same schema. Consider sharing schemas between frontend and backend.`,
|
|
102
|
-
patch: `// Share your Zod schema between frontend and backend:
|
|
103
|
-
// lib/schemas/user.ts
|
|
104
|
-
import { z } from "zod";
|
|
105
|
-
|
|
106
|
-
export const createUserSchema = z.object({
|
|
107
|
-
name: z.string().min(1),
|
|
108
|
-
email: z.string().email(),
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// In your API route:
|
|
112
|
-
import { createUserSchema } from "@/lib/schemas/user";
|
|
113
|
-
|
|
114
|
-
export async function POST(request: Request) {
|
|
115
|
-
const body = await request.json();
|
|
116
|
-
|
|
117
|
-
// Server-side validation
|
|
118
|
-
const result = createUserSchema.safeParse(body);
|
|
119
|
-
if (!result.success) {
|
|
120
|
-
return Response.json(
|
|
121
|
-
{ error: result.error.flatten() },
|
|
122
|
-
{ status: 400 }
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Use validated data
|
|
127
|
-
const { name, email } = result.data;
|
|
128
|
-
// ...
|
|
129
|
-
}`,
|
|
130
|
-
},
|
|
131
|
-
links: {
|
|
132
|
-
cwe: "https://cwe.mitre.org/data/definitions/20.html",
|
|
133
|
-
owasp: "https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html",
|
|
134
|
-
},
|
|
135
|
-
fingerprint,
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return findings;
|
|
140
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { Finding } from "@vibecheck/schema";
|
|
2
|
-
import type { ScanContext } from "../types.js";
|
|
3
|
-
/**
|
|
4
|
-
* VC-VAL-001: Validation defined but output ignored
|
|
5
|
-
*
|
|
6
|
-
* Detects when zod/yup/joi validation is performed but:
|
|
7
|
-
* - The validated result is not assigned to a variable
|
|
8
|
-
* - The validated result is assigned but never referenced
|
|
9
|
-
* - Raw request body is used after validation
|
|
10
|
-
*/
|
|
11
|
-
export declare function scanIgnoredValidation(context: ScanContext): Promise<Finding[]>;
|
|
12
|
-
//# sourceMappingURL=ignored-validation.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ignored-validation.d.ts","sourceRoot":"","sources":["../../../src/scanners/validation/ignored-validation.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,qBAAqB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAqHpF"}
|