@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantracode/vibecheck",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Security scanner for modern web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -43,24 +43,30 @@
|
|
|
43
43
|
"node": ">=18.0.0"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
|
-
"build": "
|
|
46
|
+
"build": "tsup",
|
|
47
|
+
"build:tsc": "tsc -p tsconfig.build.json",
|
|
47
48
|
"typecheck": "tsc --noEmit",
|
|
48
|
-
"dev": "
|
|
49
|
+
"dev": "tsup --watch",
|
|
49
50
|
"test": "vitest run",
|
|
50
51
|
"test:watch": "vitest",
|
|
51
52
|
"vibecheck": "node ./dist/index.js",
|
|
52
53
|
"smoke": "node scripts/smoke-test.mjs",
|
|
53
|
-
"
|
|
54
|
+
"pack:check": "node scripts/pack-check.mjs",
|
|
55
|
+
"prepublishOnly": "pnpm run build && pnpm run pack:check"
|
|
54
56
|
},
|
|
55
57
|
"dependencies": {
|
|
56
|
-
"@vibecheck/policy": "workspace:*",
|
|
57
|
-
"@vibecheck/schema": "workspace:*",
|
|
58
58
|
"commander": "^12.1.0",
|
|
59
59
|
"fast-glob": "^3.3.2",
|
|
60
|
-
"
|
|
60
|
+
"micromatch": "^4.0.8",
|
|
61
|
+
"ts-morph": "^24.0.0",
|
|
62
|
+
"zod": "^3.24.1"
|
|
61
63
|
},
|
|
62
64
|
"devDependencies": {
|
|
65
|
+
"@types/micromatch": "^4.0.9",
|
|
63
66
|
"@types/node": "^22.10.2",
|
|
67
|
+
"@vibecheck/policy": "workspace:*",
|
|
68
|
+
"@vibecheck/schema": "workspace:*",
|
|
69
|
+
"tsup": "^8.5.1",
|
|
64
70
|
"vitest": "^2.1.8"
|
|
65
71
|
}
|
|
66
72
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cli.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { executeScan } from "../commands/scan.js";
|
|
3
|
-
import { executeExplain } from "../commands/explain.js";
|
|
4
|
-
import { ScanArtifactSchema } from "@vibecheck/schema";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import fs from "node:fs";
|
|
7
|
-
import os from "node:os";
|
|
8
|
-
describe("scan command", () => {
|
|
9
|
-
it("produces valid artifact", async () => {
|
|
10
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
11
|
-
const outputPath = path.join(tmpDir, "output", "scan.json");
|
|
12
|
-
// Create a simple test file
|
|
13
|
-
fs.writeFileSync(path.join(tmpDir, "app.ts"), `const url = process.env.DATABASE_URL;`);
|
|
14
|
-
try {
|
|
15
|
-
const options = {
|
|
16
|
-
out: outputPath,
|
|
17
|
-
format: "json",
|
|
18
|
-
failOn: "critical", // Don't fail on high so we can test output
|
|
19
|
-
changed: false,
|
|
20
|
-
};
|
|
21
|
-
// Capture console output
|
|
22
|
-
const originalLog = console.log;
|
|
23
|
-
const logs = [];
|
|
24
|
-
console.log = (...args) => logs.push(args.join(" "));
|
|
25
|
-
await executeScan(tmpDir, options);
|
|
26
|
-
console.log = originalLog;
|
|
27
|
-
// Read and validate output
|
|
28
|
-
const content = fs.readFileSync(outputPath, "utf-8");
|
|
29
|
-
const artifact = JSON.parse(content);
|
|
30
|
-
const result = ScanArtifactSchema.safeParse(artifact);
|
|
31
|
-
expect(result.success).toBe(true);
|
|
32
|
-
if (result.success) {
|
|
33
|
-
expect(result.data.artifactVersion).toBe("0.1");
|
|
34
|
-
expect(result.data.tool.name).toBe("vibecheck");
|
|
35
|
-
expect(result.data.findings.length).toBeGreaterThan(0);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
finally {
|
|
39
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
it("includes repo info when available", async () => {
|
|
43
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
44
|
-
const outputPath = path.join(tmpDir, "scan.json");
|
|
45
|
-
fs.writeFileSync(path.join(tmpDir, "app.ts"), `const x = 1;`);
|
|
46
|
-
try {
|
|
47
|
-
const options = {
|
|
48
|
-
out: outputPath,
|
|
49
|
-
format: "json",
|
|
50
|
-
repoName: "test-repo",
|
|
51
|
-
failOn: "critical",
|
|
52
|
-
changed: false,
|
|
53
|
-
};
|
|
54
|
-
const originalLog = console.log;
|
|
55
|
-
console.log = () => { };
|
|
56
|
-
await executeScan(tmpDir, options);
|
|
57
|
-
console.log = originalLog;
|
|
58
|
-
const artifact = JSON.parse(fs.readFileSync(outputPath, "utf-8"));
|
|
59
|
-
expect(artifact.repo.name).toBe("test-repo");
|
|
60
|
-
}
|
|
61
|
-
finally {
|
|
62
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
it("includes metrics in output", async () => {
|
|
66
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
67
|
-
const outputPath = path.join(tmpDir, "scan.json");
|
|
68
|
-
fs.writeFileSync(path.join(tmpDir, "app.ts"), `const x = 1;`);
|
|
69
|
-
try {
|
|
70
|
-
const options = {
|
|
71
|
-
out: outputPath,
|
|
72
|
-
format: "json",
|
|
73
|
-
failOn: "critical",
|
|
74
|
-
changed: false,
|
|
75
|
-
};
|
|
76
|
-
const originalLog = console.log;
|
|
77
|
-
console.log = () => { };
|
|
78
|
-
await executeScan(tmpDir, options);
|
|
79
|
-
console.log = originalLog;
|
|
80
|
-
const artifact = JSON.parse(fs.readFileSync(outputPath, "utf-8"));
|
|
81
|
-
expect(artifact.metrics).toBeDefined();
|
|
82
|
-
expect(artifact.metrics.filesScanned).toBe(1);
|
|
83
|
-
expect(artifact.metrics.scanDurationMs).toBeGreaterThanOrEqual(0);
|
|
84
|
-
}
|
|
85
|
-
finally {
|
|
86
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
it("exits with non-zero when findings exceed threshold", async () => {
|
|
90
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
91
|
-
const outputPath = path.join(tmpDir, "scan.json");
|
|
92
|
-
// Create file with high severity finding
|
|
93
|
-
fs.writeFileSync(path.join(tmpDir, "app.ts"), `const key = process.env.API_SECRET_KEY;`);
|
|
94
|
-
try {
|
|
95
|
-
const options = {
|
|
96
|
-
out: outputPath,
|
|
97
|
-
format: "json",
|
|
98
|
-
failOn: "high",
|
|
99
|
-
changed: false,
|
|
100
|
-
};
|
|
101
|
-
const originalLog = console.log;
|
|
102
|
-
console.log = () => { };
|
|
103
|
-
const exitCode = await executeScan(tmpDir, options);
|
|
104
|
-
console.log = originalLog;
|
|
105
|
-
expect(exitCode).toBe(1);
|
|
106
|
-
}
|
|
107
|
-
finally {
|
|
108
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
describe("explain command", () => {
|
|
113
|
-
it("reads and displays artifact", async () => {
|
|
114
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
115
|
-
const artifactPath = path.join(tmpDir, "scan.json");
|
|
116
|
-
const artifact = {
|
|
117
|
-
artifactVersion: "0.1",
|
|
118
|
-
generatedAt: new Date().toISOString(),
|
|
119
|
-
tool: { name: "vibecheck", version: "0.0.1" },
|
|
120
|
-
summary: {
|
|
121
|
-
totalFindings: 1,
|
|
122
|
-
bySeverity: { critical: 0, high: 1, medium: 0, low: 0, info: 0 },
|
|
123
|
-
byCategory: {
|
|
124
|
-
auth: 1,
|
|
125
|
-
validation: 0,
|
|
126
|
-
middleware: 0,
|
|
127
|
-
secrets: 0,
|
|
128
|
-
injection: 0,
|
|
129
|
-
privacy: 0,
|
|
130
|
-
config: 0,
|
|
131
|
-
other: 0,
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
findings: [
|
|
135
|
-
{
|
|
136
|
-
id: "test-001",
|
|
137
|
-
severity: "high",
|
|
138
|
-
confidence: 0.9,
|
|
139
|
-
category: "auth",
|
|
140
|
-
ruleId: "VC-AUTH-001",
|
|
141
|
-
title: "Test finding",
|
|
142
|
-
description: "Test description",
|
|
143
|
-
evidence: [
|
|
144
|
-
{ file: "test.ts", startLine: 1, endLine: 1, label: "Test label" },
|
|
145
|
-
],
|
|
146
|
-
remediation: { recommendedFix: "Fix it" },
|
|
147
|
-
fingerprint: "abc123",
|
|
148
|
-
},
|
|
149
|
-
],
|
|
150
|
-
};
|
|
151
|
-
fs.writeFileSync(artifactPath, JSON.stringify(artifact));
|
|
152
|
-
try {
|
|
153
|
-
const originalLog = console.log;
|
|
154
|
-
const logs = [];
|
|
155
|
-
console.log = (...args) => logs.push(args.join(" "));
|
|
156
|
-
const exitCode = await executeExplain(artifactPath, { limit: 5 });
|
|
157
|
-
console.log = originalLog;
|
|
158
|
-
expect(exitCode).toBe(0);
|
|
159
|
-
expect(logs.some((l) => l.includes("VIBECHECK SCAN REPORT"))).toBe(true);
|
|
160
|
-
expect(logs.some((l) => l.includes("Test finding"))).toBe(true);
|
|
161
|
-
}
|
|
162
|
-
finally {
|
|
163
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
it("returns error for invalid artifact", async () => {
|
|
167
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
168
|
-
const artifactPath = path.join(tmpDir, "invalid.json");
|
|
169
|
-
fs.writeFileSync(artifactPath, JSON.stringify({ invalid: true }));
|
|
170
|
-
try {
|
|
171
|
-
const originalLog = console.log;
|
|
172
|
-
const originalError = console.error;
|
|
173
|
-
console.log = () => { };
|
|
174
|
-
console.error = () => { };
|
|
175
|
-
const exitCode = await executeExplain(artifactPath, { limit: 5 });
|
|
176
|
-
console.log = originalLog;
|
|
177
|
-
console.error = originalError;
|
|
178
|
-
expect(exitCode).toBe(1);
|
|
179
|
-
}
|
|
180
|
-
finally {
|
|
181
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
describe("artifact output shape", () => {
|
|
186
|
-
it("has correct structure", async () => {
|
|
187
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
188
|
-
const outputPath = path.join(tmpDir, "scan.json");
|
|
189
|
-
fs.writeFileSync(path.join(tmpDir, "app.ts"), `import { z } from "zod";
|
|
190
|
-
// unused
|
|
191
|
-
`);
|
|
192
|
-
try {
|
|
193
|
-
const options = {
|
|
194
|
-
out: outputPath,
|
|
195
|
-
format: "json",
|
|
196
|
-
failOn: "critical",
|
|
197
|
-
changed: false,
|
|
198
|
-
};
|
|
199
|
-
const originalLog = console.log;
|
|
200
|
-
console.log = () => { };
|
|
201
|
-
await executeScan(tmpDir, options);
|
|
202
|
-
console.log = originalLog;
|
|
203
|
-
const artifact = JSON.parse(fs.readFileSync(outputPath, "utf-8"));
|
|
204
|
-
// Check top-level structure
|
|
205
|
-
expect(artifact).toHaveProperty("artifactVersion", "0.1");
|
|
206
|
-
expect(artifact).toHaveProperty("generatedAt");
|
|
207
|
-
expect(artifact).toHaveProperty("tool");
|
|
208
|
-
expect(artifact).toHaveProperty("summary");
|
|
209
|
-
expect(artifact).toHaveProperty("findings");
|
|
210
|
-
// Check tool info
|
|
211
|
-
expect(artifact.tool).toHaveProperty("name", "vibecheck");
|
|
212
|
-
expect(artifact.tool).toHaveProperty("version");
|
|
213
|
-
// Check summary structure
|
|
214
|
-
expect(artifact.summary).toHaveProperty("totalFindings");
|
|
215
|
-
expect(artifact.summary).toHaveProperty("bySeverity");
|
|
216
|
-
expect(artifact.summary).toHaveProperty("byCategory");
|
|
217
|
-
// Check finding structure (if any)
|
|
218
|
-
if (artifact.findings.length > 0) {
|
|
219
|
-
const finding = artifact.findings[0];
|
|
220
|
-
expect(finding).toHaveProperty("id");
|
|
221
|
-
expect(finding).toHaveProperty("severity");
|
|
222
|
-
expect(finding).toHaveProperty("confidence");
|
|
223
|
-
expect(finding).toHaveProperty("category");
|
|
224
|
-
expect(finding).toHaveProperty("ruleId");
|
|
225
|
-
expect(finding).toHaveProperty("title");
|
|
226
|
-
expect(finding).toHaveProperty("description");
|
|
227
|
-
expect(finding).toHaveProperty("evidence");
|
|
228
|
-
expect(finding).toHaveProperty("remediation");
|
|
229
|
-
expect(finding).toHaveProperty("fingerprint");
|
|
230
|
-
// Check ruleId format
|
|
231
|
-
expect(finding.ruleId).toMatch(/^VC-[A-Z]+-\d{3}$/);
|
|
232
|
-
// Check evidence structure
|
|
233
|
-
expect(finding.evidence[0]).toHaveProperty("file");
|
|
234
|
-
expect(finding.evidence[0]).toHaveProperty("startLine");
|
|
235
|
-
expect(finding.evidence[0]).toHaveProperty("endLine");
|
|
236
|
-
expect(finding.evidence[0]).toHaveProperty("label");
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
finally {
|
|
240
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { prisma } from "@/lib/prisma";
|
|
2
|
-
import { getServerSession } from "next-auth";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
const userSchema = z.object({
|
|
5
|
-
name: z.string(),
|
|
6
|
-
email: z.string().email(),
|
|
7
|
-
});
|
|
8
|
-
// Safe: Has auth check
|
|
9
|
-
export async function POST(request) {
|
|
10
|
-
const session = await getServerSession();
|
|
11
|
-
if (!session) {
|
|
12
|
-
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
|
13
|
-
}
|
|
14
|
-
const body = await request.json();
|
|
15
|
-
// Safe: Validation result is used
|
|
16
|
-
const validatedData = userSchema.parse(body);
|
|
17
|
-
const user = await prisma.user.create({
|
|
18
|
-
data: validatedData,
|
|
19
|
-
});
|
|
20
|
-
// Safe: Not logging sensitive data
|
|
21
|
-
console.log("Created user:", { id: user.id, timestamp: Date.now() });
|
|
22
|
-
return Response.json(user);
|
|
23
|
-
}
|
|
24
|
-
// Safe: Has auth check
|
|
25
|
-
export async function DELETE(request) {
|
|
26
|
-
const session = await getServerSession();
|
|
27
|
-
if (!session) {
|
|
28
|
-
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
|
29
|
-
}
|
|
30
|
-
const { searchParams } = new URL(request.url);
|
|
31
|
-
const id = searchParams.get("id");
|
|
32
|
-
await prisma.user.delete({
|
|
33
|
-
where: { id },
|
|
34
|
-
});
|
|
35
|
-
return Response.json({ success: true });
|
|
36
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { prisma } from "@/lib/prisma";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
const userSchema = z.object({
|
|
4
|
-
name: z.string(),
|
|
5
|
-
email: z.string().email(),
|
|
6
|
-
});
|
|
7
|
-
// VC-AUTH-001: No auth check before database write
|
|
8
|
-
export async function POST(request) {
|
|
9
|
-
const body = await request.json();
|
|
10
|
-
// VC-VAL-001: Validation called but result ignored
|
|
11
|
-
userSchema.parse(body);
|
|
12
|
-
// Using raw body instead of validated data
|
|
13
|
-
const user = await prisma.user.create({
|
|
14
|
-
data: body,
|
|
15
|
-
});
|
|
16
|
-
// VC-PRIV-001: Logging sensitive data
|
|
17
|
-
console.log("Created user:", { email: body.email, password: body.password });
|
|
18
|
-
return Response.json(user);
|
|
19
|
-
}
|
|
20
|
-
// VC-AUTH-001: Critical - delete without auth
|
|
21
|
-
export async function DELETE(request) {
|
|
22
|
-
const { searchParams } = new URL(request.url);
|
|
23
|
-
const id = searchParams.get("id");
|
|
24
|
-
await prisma.user.delete({
|
|
25
|
-
where: { id },
|
|
26
|
-
});
|
|
27
|
-
return Response.json({ success: true });
|
|
28
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../../src/__tests__/fixtures/vulnerable-app/lib/config.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,UAAU,QAA6C,CAAC;AAGrE,eAAO,MAAM,cAAc,QAA8C,CAAC;AAG1E,eAAO,MAAM,OAAO,QAAiD,CAAC"}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// VC-CONFIG-002: Insecure default for critical secret
|
|
2
|
-
export const JWT_SECRET = process.env.JWT_SECRET || "dev-secret-123";
|
|
3
|
-
// VC-CONFIG-002: Another insecure default
|
|
4
|
-
export const SESSION_SECRET = process.env.SESSION_SECRET ?? "session-dev";
|
|
5
|
-
// Safe - not a secret
|
|
6
|
-
export const API_URL = process.env.API_URL || "http://localhost:3000";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"env-config.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/scanners/env-config.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { parseEnvExample, findEnvUsages, scanEnvConfig, } from "../../scanners/env-config.js";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import fs from "node:fs";
|
|
5
|
-
import os from "node:os";
|
|
6
|
-
describe("parseEnvExample", () => {
|
|
7
|
-
it("parses simple env file", () => {
|
|
8
|
-
const content = `
|
|
9
|
-
DATABASE_URL=
|
|
10
|
-
API_KEY=your-key-here
|
|
11
|
-
SECRET_TOKEN=
|
|
12
|
-
`;
|
|
13
|
-
const vars = parseEnvExample(content);
|
|
14
|
-
expect(vars.has("DATABASE_URL")).toBe(true);
|
|
15
|
-
expect(vars.has("API_KEY")).toBe(true);
|
|
16
|
-
expect(vars.has("SECRET_TOKEN")).toBe(true);
|
|
17
|
-
});
|
|
18
|
-
it("ignores comments", () => {
|
|
19
|
-
const content = `
|
|
20
|
-
# This is a comment
|
|
21
|
-
DATABASE_URL=
|
|
22
|
-
# API_KEY=should-be-ignored
|
|
23
|
-
`;
|
|
24
|
-
const vars = parseEnvExample(content);
|
|
25
|
-
expect(vars.has("DATABASE_URL")).toBe(true);
|
|
26
|
-
expect(vars.has("API_KEY")).toBe(false);
|
|
27
|
-
});
|
|
28
|
-
it("handles empty lines", () => {
|
|
29
|
-
const content = `
|
|
30
|
-
|
|
31
|
-
DATABASE_URL=
|
|
32
|
-
|
|
33
|
-
API_KEY=
|
|
34
|
-
|
|
35
|
-
`;
|
|
36
|
-
const vars = parseEnvExample(content);
|
|
37
|
-
expect(vars.size).toBe(2);
|
|
38
|
-
});
|
|
39
|
-
it("handles vars without values", () => {
|
|
40
|
-
const content = `DATABASE_URL`;
|
|
41
|
-
const vars = parseEnvExample(content);
|
|
42
|
-
expect(vars.has("DATABASE_URL")).toBe(true);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
describe("findEnvUsages", () => {
|
|
46
|
-
it("finds process.env.VAR usage", () => {
|
|
47
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
48
|
-
const filePath = path.join(tmpDir, "test.ts");
|
|
49
|
-
fs.writeFileSync(filePath, `
|
|
50
|
-
const url = process.env.DATABASE_URL;
|
|
51
|
-
const key = process.env.API_KEY;
|
|
52
|
-
`);
|
|
53
|
-
try {
|
|
54
|
-
const usages = findEnvUsages(["test.ts"], tmpDir);
|
|
55
|
-
expect(usages).toHaveLength(2);
|
|
56
|
-
expect(usages[0].name).toBe("DATABASE_URL");
|
|
57
|
-
expect(usages[1].name).toBe("API_KEY");
|
|
58
|
-
}
|
|
59
|
-
finally {
|
|
60
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
it("finds bracket notation usage", () => {
|
|
64
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
65
|
-
const filePath = path.join(tmpDir, "test.ts");
|
|
66
|
-
fs.writeFileSync(filePath, `const url = process.env["DATABASE_URL"];`);
|
|
67
|
-
try {
|
|
68
|
-
const usages = findEnvUsages(["test.ts"], tmpDir);
|
|
69
|
-
expect(usages).toHaveLength(1);
|
|
70
|
-
expect(usages[0].name).toBe("DATABASE_URL");
|
|
71
|
-
}
|
|
72
|
-
finally {
|
|
73
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
it("returns correct line numbers", () => {
|
|
77
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
78
|
-
const filePath = path.join(tmpDir, "test.ts");
|
|
79
|
-
fs.writeFileSync(filePath, `// line 1
|
|
80
|
-
// line 2
|
|
81
|
-
const x = process.env.MY_VAR; // line 3
|
|
82
|
-
`);
|
|
83
|
-
try {
|
|
84
|
-
const usages = findEnvUsages(["test.ts"], tmpDir);
|
|
85
|
-
expect(usages[0].line).toBe(3);
|
|
86
|
-
}
|
|
87
|
-
finally {
|
|
88
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
describe("scanEnvConfig", () => {
|
|
93
|
-
it("creates findings for undocumented env vars", async () => {
|
|
94
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
95
|
-
const filePath = path.join(tmpDir, "app.ts");
|
|
96
|
-
fs.writeFileSync(filePath, `const url = process.env.DATABASE_URL;`);
|
|
97
|
-
try {
|
|
98
|
-
const context = {
|
|
99
|
-
targetDir: tmpDir,
|
|
100
|
-
sourceFiles: ["app.ts"],
|
|
101
|
-
};
|
|
102
|
-
const findings = await scanEnvConfig(context);
|
|
103
|
-
expect(findings).toHaveLength(1);
|
|
104
|
-
expect(findings[0].ruleId).toBe("VC-CONFIG-001");
|
|
105
|
-
expect(findings[0].title).toContain("DATABASE_URL");
|
|
106
|
-
}
|
|
107
|
-
finally {
|
|
108
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
it("does not create findings for documented vars", async () => {
|
|
112
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
113
|
-
fs.writeFileSync(path.join(tmpDir, "app.ts"), `const url = process.env.DATABASE_URL;`);
|
|
114
|
-
fs.writeFileSync(path.join(tmpDir, ".env.example"), "DATABASE_URL=");
|
|
115
|
-
try {
|
|
116
|
-
const context = {
|
|
117
|
-
targetDir: tmpDir,
|
|
118
|
-
sourceFiles: ["app.ts"],
|
|
119
|
-
};
|
|
120
|
-
const findings = await scanEnvConfig(context);
|
|
121
|
-
expect(findings).toHaveLength(0);
|
|
122
|
-
}
|
|
123
|
-
finally {
|
|
124
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
it("marks SECRET-like vars as high severity", async () => {
|
|
128
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vibecheck-test-"));
|
|
129
|
-
fs.writeFileSync(path.join(tmpDir, "app.ts"), `const key = process.env.API_SECRET_KEY;`);
|
|
130
|
-
try {
|
|
131
|
-
const context = {
|
|
132
|
-
targetDir: tmpDir,
|
|
133
|
-
sourceFiles: ["app.ts"],
|
|
134
|
-
};
|
|
135
|
-
const findings = await scanEnvConfig(context);
|
|
136
|
-
expect(findings[0].severity).toBe("high");
|
|
137
|
-
}
|
|
138
|
-
finally {
|
|
139
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"nextjs-middleware.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/scanners/nextjs-middleware.test.ts"],"names":[],"mappings":""}
|