@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,193 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { parseMatcherConfig, matcherCoversApi, scanNextjsMiddleware, } from "../../scanners/nextjs-middleware.js";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import fs from "node:fs";
|
|
5
|
-
import os from "node:os";
|
|
6
|
-
describe("parseMatcherConfig", () => {
|
|
7
|
-
it("parses single string matcher", () => {
|
|
8
|
-
const content = `
|
|
9
|
-
export const config = {
|
|
10
|
-
matcher: '/dashboard/:path*'
|
|
11
|
-
};
|
|
12
|
-
`;
|
|
13
|
-
const matchers = parseMatcherConfig(content);
|
|
14
|
-
expect(matchers).toEqual(["/dashboard/:path*"]);
|
|
15
|
-
});
|
|
16
|
-
it("parses array matcher", () => {
|
|
17
|
-
const content = `
|
|
18
|
-
export const config = {
|
|
19
|
-
matcher: ['/dashboard/:path*', '/admin/:path*']
|
|
20
|
-
};
|
|
21
|
-
`;
|
|
22
|
-
const matchers = parseMatcherConfig(content);
|
|
23
|
-
expect(matchers).toEqual(["/dashboard/:path*", "/admin/:path*"]);
|
|
24
|
-
});
|
|
25
|
-
it("returns null when no config export", () => {
|
|
26
|
-
const content = `
|
|
27
|
-
export function middleware(request) {
|
|
28
|
-
return NextResponse.next();
|
|
29
|
-
}
|
|
30
|
-
`;
|
|
31
|
-
const matchers = parseMatcherConfig(content);
|
|
32
|
-
expect(matchers).toBeNull();
|
|
33
|
-
});
|
|
34
|
-
it("parses complex negation pattern", () => {
|
|
35
|
-
const content = `
|
|
36
|
-
export const config = {
|
|
37
|
-
matcher: '/((?!_next/static|_next/image|favicon.ico).*)'
|
|
38
|
-
};
|
|
39
|
-
`;
|
|
40
|
-
const matchers = parseMatcherConfig(content);
|
|
41
|
-
expect(matchers).toEqual(["/((?!_next/static|_next/image|favicon.ico).*)"]);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
describe("matcherCoversApi", () => {
|
|
45
|
-
it("returns true for direct /api match", () => {
|
|
46
|
-
expect(matcherCoversApi(["/api"])).toBe(true);
|
|
47
|
-
expect(matcherCoversApi(["/api/:path*"])).toBe(true);
|
|
48
|
-
});
|
|
49
|
-
it("returns true for patterns starting with /api", () => {
|
|
50
|
-
expect(matcherCoversApi(["/api/users/:path*"])).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
it("returns true for catch-all patterns", () => {
|
|
53
|
-
expect(matcherCoversApi(["/:path*"])).toBe(true);
|
|
54
|
-
});
|
|
55
|
-
it("returns true for negation patterns not excluding api", () => {
|
|
56
|
-
expect(matcherCoversApi(["/((?!_next/static|_next/image|favicon.ico).*)"])).toBe(true);
|
|
57
|
-
});
|
|
58
|
-
it("returns false for dashboard-only patterns", () => {
|
|
59
|
-
expect(matcherCoversApi(["/dashboard/:path*"])).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
it("returns false for unrelated patterns", () => {
|
|
62
|
-
expect(matcherCoversApi(["/admin", "/profile"])).toBe(false);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
describe("scanNextjsMiddleware", () => {
|
|
66
|
-
function createNextProject(tmpDir, options = {}) {
|
|
67
|
-
// Create package.json
|
|
68
|
-
const pkg = {
|
|
69
|
-
name: "test-app",
|
|
70
|
-
dependencies: {
|
|
71
|
-
next: "14.0.0",
|
|
72
|
-
...(options.hasNextAuth && { "next-auth": "4.0.0" }),
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
fs.writeFileSync(path.join(tmpDir, "package.json"), JSON.stringify(pkg));
|
|
76
|
-
// Create middleware if specified
|
|
77
|
-
if (options.hasMiddleware && options.middlewareContent) {
|
|
78
|
-
fs.writeFileSync(path.join(tmpDir, "middleware.ts"), options.middlewareContent);
|
|
79
|
-
}
|
|
80
|
-
// Create API routes if specified
|
|
81
|
-
if (options.hasApiRoutes) {
|
|
82
|
-
const apiDir = path.join(tmpDir, "app", "api", "users");
|
|
83
|
-
fs.mkdirSync(apiDir, { recursive: true });
|
|
84
|
-
fs.writeFileSync(path.join(apiDir, "route.ts"), `export async function GET() { return Response.json({}); }`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
it("returns no findings for non-Next.js project", async () => {
|
|
88
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
89
|
-
try {
|
|
90
|
-
fs.writeFileSync(path.join(tmpDir, "package.json"), JSON.stringify({ name: "test" }));
|
|
91
|
-
const context = {
|
|
92
|
-
targetDir: tmpDir,
|
|
93
|
-
sourceFiles: [],
|
|
94
|
-
};
|
|
95
|
-
const findings = await scanNextjsMiddleware(context);
|
|
96
|
-
expect(findings).toHaveLength(0);
|
|
97
|
-
}
|
|
98
|
-
finally {
|
|
99
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
it("returns no findings when no API routes exist", async () => {
|
|
103
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
104
|
-
try {
|
|
105
|
-
createNextProject(tmpDir, { hasApiRoutes: false });
|
|
106
|
-
const context = {
|
|
107
|
-
targetDir: tmpDir,
|
|
108
|
-
sourceFiles: [],
|
|
109
|
-
};
|
|
110
|
-
const findings = await scanNextjsMiddleware(context);
|
|
111
|
-
expect(findings).toHaveLength(0);
|
|
112
|
-
}
|
|
113
|
-
finally {
|
|
114
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
it("finds missing middleware with next-auth", async () => {
|
|
118
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
119
|
-
try {
|
|
120
|
-
createNextProject(tmpDir, {
|
|
121
|
-
hasNextAuth: true,
|
|
122
|
-
hasApiRoutes: true,
|
|
123
|
-
});
|
|
124
|
-
const context = {
|
|
125
|
-
targetDir: tmpDir,
|
|
126
|
-
sourceFiles: [],
|
|
127
|
-
};
|
|
128
|
-
const findings = await scanNextjsMiddleware(context);
|
|
129
|
-
expect(findings).toHaveLength(1);
|
|
130
|
-
expect(findings[0].ruleId).toBe("VC-AUTH-INFO-001");
|
|
131
|
-
expect(findings[0].severity).toBe("medium");
|
|
132
|
-
}
|
|
133
|
-
finally {
|
|
134
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
it("finds middleware not covering API routes", async () => {
|
|
138
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
139
|
-
try {
|
|
140
|
-
createNextProject(tmpDir, {
|
|
141
|
-
hasMiddleware: true,
|
|
142
|
-
middlewareContent: `
|
|
143
|
-
export function middleware(request) {
|
|
144
|
-
return NextResponse.next();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export const config = {
|
|
148
|
-
matcher: '/dashboard/:path*'
|
|
149
|
-
};
|
|
150
|
-
`,
|
|
151
|
-
hasApiRoutes: true,
|
|
152
|
-
});
|
|
153
|
-
const context = {
|
|
154
|
-
targetDir: tmpDir,
|
|
155
|
-
sourceFiles: [],
|
|
156
|
-
};
|
|
157
|
-
const findings = await scanNextjsMiddleware(context);
|
|
158
|
-
expect(findings).toHaveLength(1);
|
|
159
|
-
expect(findings[0].ruleId).toBe("VC-MW-001");
|
|
160
|
-
expect(findings[0].severity).toBe("high");
|
|
161
|
-
}
|
|
162
|
-
finally {
|
|
163
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
it("returns no findings when middleware covers API", async () => {
|
|
167
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
168
|
-
try {
|
|
169
|
-
createNextProject(tmpDir, {
|
|
170
|
-
hasMiddleware: true,
|
|
171
|
-
middlewareContent: `
|
|
172
|
-
export function middleware(request) {
|
|
173
|
-
return NextResponse.next();
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export const config = {
|
|
177
|
-
matcher: ['/dashboard/:path*', '/api/:path*']
|
|
178
|
-
};
|
|
179
|
-
`,
|
|
180
|
-
hasApiRoutes: true,
|
|
181
|
-
});
|
|
182
|
-
const context = {
|
|
183
|
-
targetDir: tmpDir,
|
|
184
|
-
sourceFiles: [],
|
|
185
|
-
};
|
|
186
|
-
const findings = await scanNextjsMiddleware(context);
|
|
187
|
-
expect(findings).toHaveLength(0);
|
|
188
|
-
}
|
|
189
|
-
finally {
|
|
190
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scanner-packs.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/scanners/scanner-packs.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll } from "vitest";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { buildScanContext } from "../../scanners/index.js";
|
|
4
|
-
import { scanUnprotectedApiRoutes } from "../../scanners/auth/unprotected-api-route.js";
|
|
5
|
-
import { parseMatcherConfig, matcherCoversApi } from "../../scanners/auth/middleware-gap.js";
|
|
6
|
-
import { scanInsecureDefaults } from "../../scanners/config/insecure-defaults.js";
|
|
7
|
-
import { findSecurityImports, checkIdentifierUsage, } from "../../scanners/hallucinations/unused-security-imports.js";
|
|
8
|
-
const FIXTURES_DIR = path.resolve(__dirname, "../fixtures");
|
|
9
|
-
const VULNERABLE_APP = path.join(FIXTURES_DIR, "vulnerable-app");
|
|
10
|
-
const SAFE_APP = path.join(FIXTURES_DIR, "safe-app");
|
|
11
|
-
describe("Scanner Packs", () => {
|
|
12
|
-
let vulnerableContext;
|
|
13
|
-
let safeContext;
|
|
14
|
-
beforeAll(async () => {
|
|
15
|
-
vulnerableContext = await buildScanContext(VULNERABLE_APP);
|
|
16
|
-
safeContext = await buildScanContext(SAFE_APP);
|
|
17
|
-
});
|
|
18
|
-
describe("Auth Pack", () => {
|
|
19
|
-
describe("VC-AUTH-001: Unprotected API Routes", () => {
|
|
20
|
-
it("should detect unprotected POST handler with database writes", async () => {
|
|
21
|
-
const findings = await scanUnprotectedApiRoutes(vulnerableContext);
|
|
22
|
-
const postFinding = findings.find((f) => f.ruleId === "VC-AUTH-001" && f.title.includes("POST"));
|
|
23
|
-
expect(postFinding).toBeDefined();
|
|
24
|
-
expect(postFinding?.severity).toBe("high");
|
|
25
|
-
expect(postFinding?.category).toBe("auth");
|
|
26
|
-
});
|
|
27
|
-
it("should detect unprotected DELETE handler as critical", async () => {
|
|
28
|
-
const findings = await scanUnprotectedApiRoutes(vulnerableContext);
|
|
29
|
-
const deleteFinding = findings.find((f) => f.ruleId === "VC-AUTH-001" && f.title.includes("DELETE"));
|
|
30
|
-
expect(deleteFinding).toBeDefined();
|
|
31
|
-
expect(deleteFinding?.severity).toBe("critical");
|
|
32
|
-
});
|
|
33
|
-
it("should not flag protected routes", async () => {
|
|
34
|
-
const findings = await scanUnprotectedApiRoutes(safeContext);
|
|
35
|
-
expect(findings).toHaveLength(0);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
describe("Middleware Gap Detection", () => {
|
|
39
|
-
it("should parse single string matcher", () => {
|
|
40
|
-
const content = `export const config = { matcher: '/api/:path*' }`;
|
|
41
|
-
const matchers = parseMatcherConfig(content);
|
|
42
|
-
expect(matchers).toEqual(["/api/:path*"]);
|
|
43
|
-
});
|
|
44
|
-
it("should parse array matcher", () => {
|
|
45
|
-
const content = `export const config = { matcher: ['/api/:path*', '/admin/:path*'] }`;
|
|
46
|
-
const matchers = parseMatcherConfig(content);
|
|
47
|
-
expect(matchers).toEqual(["/api/:path*", "/admin/:path*"]);
|
|
48
|
-
});
|
|
49
|
-
it("should detect when matcher covers api", () => {
|
|
50
|
-
expect(matcherCoversApi(["/api/:path*"])).toBe(true);
|
|
51
|
-
expect(matcherCoversApi(["/api/users"])).toBe(true);
|
|
52
|
-
expect(matcherCoversApi(["/:path*"])).toBe(true);
|
|
53
|
-
});
|
|
54
|
-
it("should detect when matcher does not cover api", () => {
|
|
55
|
-
expect(matcherCoversApi(["/dashboard/:path*"])).toBe(false);
|
|
56
|
-
expect(matcherCoversApi(["/admin/:path*"])).toBe(false);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
describe("Config Pack", () => {
|
|
61
|
-
describe("VC-CONFIG-002: Insecure Defaults", () => {
|
|
62
|
-
it("should detect insecure default for JWT_SECRET", async () => {
|
|
63
|
-
const findings = await scanInsecureDefaults(vulnerableContext);
|
|
64
|
-
const jwtFinding = findings.find((f) => f.ruleId === "VC-CONFIG-002" && f.title.includes("JWT_SECRET"));
|
|
65
|
-
expect(jwtFinding).toBeDefined();
|
|
66
|
-
expect(jwtFinding?.severity).toBe("critical");
|
|
67
|
-
});
|
|
68
|
-
it("should detect insecure default for SESSION_SECRET", async () => {
|
|
69
|
-
const findings = await scanInsecureDefaults(vulnerableContext);
|
|
70
|
-
const sessionFinding = findings.find((f) => f.ruleId === "VC-CONFIG-002" && f.title.includes("SESSION_SECRET"));
|
|
71
|
-
expect(sessionFinding).toBeDefined();
|
|
72
|
-
expect(sessionFinding?.severity).toBe("critical");
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
describe("Hallucinations Pack", () => {
|
|
77
|
-
describe("findSecurityImports", () => {
|
|
78
|
-
it("should find default imports", () => {
|
|
79
|
-
const content = `import helmet from "helmet";`;
|
|
80
|
-
const imports = findSecurityImports(content, ["helmet"]);
|
|
81
|
-
expect(imports).toHaveLength(1);
|
|
82
|
-
expect(imports[0].library).toBe("helmet");
|
|
83
|
-
expect(imports[0].importedNames).toEqual(["helmet"]);
|
|
84
|
-
expect(imports[0].isDefaultImport).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
it("should find named imports", () => {
|
|
87
|
-
const content = `import { z, ZodError } from "zod";`;
|
|
88
|
-
const imports = findSecurityImports(content, ["zod"]);
|
|
89
|
-
expect(imports).toHaveLength(1);
|
|
90
|
-
expect(imports[0].library).toBe("zod");
|
|
91
|
-
expect(imports[0].importedNames).toContain("z");
|
|
92
|
-
expect(imports[0].importedNames).toContain("ZodError");
|
|
93
|
-
});
|
|
94
|
-
it("should find namespace imports", () => {
|
|
95
|
-
const content = `import * as yup from "yup";`;
|
|
96
|
-
const imports = findSecurityImports(content, ["yup"]);
|
|
97
|
-
expect(imports).toHaveLength(1);
|
|
98
|
-
expect(imports[0].library).toBe("yup");
|
|
99
|
-
expect(imports[0].isNamespaceImport).toBe(true);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
describe("checkIdentifierUsage", () => {
|
|
103
|
-
it("should detect used identifiers", () => {
|
|
104
|
-
const content = `import helmet from "helmet";
|
|
105
|
-
|
|
106
|
-
app.use(helmet());`;
|
|
107
|
-
const usage = checkIdentifierUsage(content, 1, ["helmet"], false);
|
|
108
|
-
expect(usage[0].used).toBe(true);
|
|
109
|
-
});
|
|
110
|
-
it("should detect unused identifiers", () => {
|
|
111
|
-
const content = `import helmet from "helmet";
|
|
112
|
-
|
|
113
|
-
app.use(cors());`;
|
|
114
|
-
const usage = checkIdentifierUsage(content, 1, ["helmet"], false);
|
|
115
|
-
expect(usage[0].used).toBe(false);
|
|
116
|
-
});
|
|
117
|
-
it("should detect namespace usage", () => {
|
|
118
|
-
const content = `import * as yup from "yup";
|
|
119
|
-
|
|
120
|
-
const schema = yup.object();`;
|
|
121
|
-
const usage = checkIdentifierUsage(content, 1, ["yup"], true);
|
|
122
|
-
expect(usage[0].used).toBe(true);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"unused-security-imports.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/scanners/unused-security-imports.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { findSecurityImports, checkIdentifierUsage, scanUnusedSecurityImports, } from "../../scanners/unused-security-imports.js";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import fs from "node:fs";
|
|
5
|
-
import os from "node:os";
|
|
6
|
-
describe("findSecurityImports", () => {
|
|
7
|
-
it("finds default imports", () => {
|
|
8
|
-
const content = `import zod from "zod";`;
|
|
9
|
-
const imports = findSecurityImports(content, ["zod"]);
|
|
10
|
-
expect(imports).toHaveLength(1);
|
|
11
|
-
expect(imports[0].library).toBe("zod");
|
|
12
|
-
expect(imports[0].importedNames).toContain("zod");
|
|
13
|
-
expect(imports[0].isDefaultImport).toBe(true);
|
|
14
|
-
});
|
|
15
|
-
it("finds named imports", () => {
|
|
16
|
-
const content = `import { z, ZodError } from "zod";`;
|
|
17
|
-
const imports = findSecurityImports(content, ["zod"]);
|
|
18
|
-
expect(imports).toHaveLength(1);
|
|
19
|
-
expect(imports[0].importedNames).toContain("z");
|
|
20
|
-
expect(imports[0].importedNames).toContain("ZodError");
|
|
21
|
-
});
|
|
22
|
-
it("finds namespace imports", () => {
|
|
23
|
-
const content = `import * as yup from "yup";`;
|
|
24
|
-
const imports = findSecurityImports(content, ["yup"]);
|
|
25
|
-
expect(imports).toHaveLength(1);
|
|
26
|
-
expect(imports[0].isNamespaceImport).toBe(true);
|
|
27
|
-
expect(imports[0].importedNames).toContain("yup");
|
|
28
|
-
});
|
|
29
|
-
it("finds helmet import", () => {
|
|
30
|
-
const content = `import helmet from "helmet";`;
|
|
31
|
-
const imports = findSecurityImports(content, ["helmet"]);
|
|
32
|
-
expect(imports).toHaveLength(1);
|
|
33
|
-
expect(imports[0].library).toBe("helmet");
|
|
34
|
-
});
|
|
35
|
-
it("returns correct line numbers", () => {
|
|
36
|
-
const content = `// line 1
|
|
37
|
-
// line 2
|
|
38
|
-
import { z } from "zod";
|
|
39
|
-
`;
|
|
40
|
-
const imports = findSecurityImports(content, ["zod"]);
|
|
41
|
-
expect(imports[0].line).toBe(3);
|
|
42
|
-
});
|
|
43
|
-
it("ignores non-security imports", () => {
|
|
44
|
-
const content = `
|
|
45
|
-
import express from "express";
|
|
46
|
-
import { z } from "zod";
|
|
47
|
-
`;
|
|
48
|
-
const imports = findSecurityImports(content, ["zod", "helmet"]);
|
|
49
|
-
expect(imports).toHaveLength(1);
|
|
50
|
-
expect(imports[0].library).toBe("zod");
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
describe("checkIdentifierUsage", () => {
|
|
54
|
-
it("detects used identifiers", () => {
|
|
55
|
-
const content = `
|
|
56
|
-
import { z } from "zod";
|
|
57
|
-
const schema = z.string();
|
|
58
|
-
`;
|
|
59
|
-
const result = checkIdentifierUsage(content, 2, ["z"], false);
|
|
60
|
-
expect(result[0].used).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
it("detects unused identifiers", () => {
|
|
63
|
-
const content = `
|
|
64
|
-
import { z, ZodError } from "zod";
|
|
65
|
-
const schema = z.string();
|
|
66
|
-
`;
|
|
67
|
-
const result = checkIdentifierUsage(content, 2, ["z", "ZodError"], false);
|
|
68
|
-
expect(result.find((r) => r.identifier === "z")?.used).toBe(true);
|
|
69
|
-
expect(result.find((r) => r.identifier === "ZodError")?.used).toBe(false);
|
|
70
|
-
});
|
|
71
|
-
it("handles namespace imports", () => {
|
|
72
|
-
const content = `
|
|
73
|
-
import * as yup from "yup";
|
|
74
|
-
const schema = yup.string();
|
|
75
|
-
`;
|
|
76
|
-
const result = checkIdentifierUsage(content, 2, ["yup"], true);
|
|
77
|
-
expect(result[0].used).toBe(true);
|
|
78
|
-
});
|
|
79
|
-
it("detects unused namespace imports", () => {
|
|
80
|
-
const content = `
|
|
81
|
-
import * as yup from "yup";
|
|
82
|
-
// not used
|
|
83
|
-
`;
|
|
84
|
-
const result = checkIdentifierUsage(content, 2, ["yup"], true);
|
|
85
|
-
expect(result[0].used).toBe(false);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
describe("scanUnusedSecurityImports", () => {
|
|
89
|
-
it("finds unused zod import", async () => {
|
|
90
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
91
|
-
fs.writeFileSync(path.join(tmpDir, "app.ts"), `import { z } from "zod";
|
|
92
|
-
// import is never used below
|
|
93
|
-
const x = 1;
|
|
94
|
-
`);
|
|
95
|
-
try {
|
|
96
|
-
const context = {
|
|
97
|
-
targetDir: tmpDir,
|
|
98
|
-
sourceFiles: ["app.ts"],
|
|
99
|
-
};
|
|
100
|
-
const findings = await scanUnusedSecurityImports(context);
|
|
101
|
-
expect(findings).toHaveLength(1);
|
|
102
|
-
expect(findings[0].ruleId).toBe("VC-HALL-001");
|
|
103
|
-
expect(findings[0].category).toBe("validation");
|
|
104
|
-
}
|
|
105
|
-
finally {
|
|
106
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
it("does not flag used imports", async () => {
|
|
110
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
111
|
-
fs.writeFileSync(path.join(tmpDir, "app.ts"), `import { z } from "zod";
|
|
112
|
-
const schema = z.string();
|
|
113
|
-
`);
|
|
114
|
-
try {
|
|
115
|
-
const context = {
|
|
116
|
-
targetDir: tmpDir,
|
|
117
|
-
sourceFiles: ["app.ts"],
|
|
118
|
-
};
|
|
119
|
-
const findings = await scanUnusedSecurityImports(context);
|
|
120
|
-
expect(findings).toHaveLength(0);
|
|
121
|
-
}
|
|
122
|
-
finally {
|
|
123
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
it("finds unused helmet import", async () => {
|
|
127
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
128
|
-
fs.writeFileSync(path.join(tmpDir, "server.ts"), `import helmet from "helmet";
|
|
129
|
-
// import is never used below
|
|
130
|
-
const app = express();
|
|
131
|
-
`);
|
|
132
|
-
try {
|
|
133
|
-
const context = {
|
|
134
|
-
targetDir: tmpDir,
|
|
135
|
-
sourceFiles: ["server.ts"],
|
|
136
|
-
};
|
|
137
|
-
const findings = await scanUnusedSecurityImports(context);
|
|
138
|
-
expect(findings).toHaveLength(1);
|
|
139
|
-
expect(findings[0].category).toBe("middleware");
|
|
140
|
-
}
|
|
141
|
-
finally {
|
|
142
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
});
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
export interface DemoArtifactOptions {
|
|
3
|
-
out?: string;
|
|
4
|
-
}
|
|
5
|
-
export declare function executeDemoArtifact(options: DemoArtifactOptions): void;
|
|
6
|
-
export declare function registerDemoArtifactCommand(program: Command): void;
|
|
7
|
-
//# sourceMappingURL=demo-artifact.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"demo-artifact.d.ts","sourceRoot":"","sources":["../../src/commands/demo-artifact.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA4SD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CA6BtE;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAQlE"}
|