@quantracode/vibecheck 0.0.1
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/LICENSE +21 -0
- package/README.md +839 -0
- package/dist/__tests__/cli.test.d.ts +2 -0
- package/dist/__tests__/cli.test.d.ts.map +1 -0
- package/dist/__tests__/cli.test.js +243 -0
- package/dist/__tests__/fixtures/safe-app/app/api/users/route.js +36 -0
- package/dist/__tests__/fixtures/vulnerable-app/app/api/users/route.js +28 -0
- package/dist/__tests__/fixtures/vulnerable-app/lib/config.d.ts +4 -0
- package/dist/__tests__/fixtures/vulnerable-app/lib/config.d.ts.map +1 -0
- package/dist/__tests__/fixtures/vulnerable-app/lib/config.js +6 -0
- package/dist/__tests__/scanners/env-config.test.d.ts +2 -0
- package/dist/__tests__/scanners/env-config.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/env-config.test.js +142 -0
- package/dist/__tests__/scanners/nextjs-middleware.test.d.ts +2 -0
- package/dist/__tests__/scanners/nextjs-middleware.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/nextjs-middleware.test.js +193 -0
- package/dist/__tests__/scanners/scanner-packs.test.d.ts +2 -0
- package/dist/__tests__/scanners/scanner-packs.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/scanner-packs.test.js +126 -0
- package/dist/__tests__/scanners/unused-security-imports.test.d.ts +2 -0
- package/dist/__tests__/scanners/unused-security-imports.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/unused-security-imports.test.js +145 -0
- package/dist/commands/demo-artifact.d.ts +7 -0
- package/dist/commands/demo-artifact.d.ts.map +1 -0
- package/dist/commands/demo-artifact.js +322 -0
- package/dist/commands/evaluate.d.ts +30 -0
- package/dist/commands/evaluate.d.ts.map +1 -0
- package/dist/commands/evaluate.js +258 -0
- package/dist/commands/explain.d.ts +12 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +214 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/intent.d.ts +21 -0
- package/dist/commands/intent.d.ts.map +1 -0
- package/dist/commands/intent.js +192 -0
- package/dist/commands/scan.d.ts +44 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +497 -0
- package/dist/commands/waivers.d.ts +30 -0
- package/dist/commands/waivers.d.ts.map +1 -0
- package/dist/commands/waivers.js +249 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/phase3/index.d.ts +11 -0
- package/dist/phase3/index.d.ts.map +1 -0
- package/dist/phase3/index.js +12 -0
- package/dist/phase3/intent-miner.d.ts +32 -0
- package/dist/phase3/intent-miner.d.ts.map +1 -0
- package/dist/phase3/intent-miner.js +323 -0
- package/dist/phase3/proof-trace-builder.d.ts +42 -0
- package/dist/phase3/proof-trace-builder.d.ts.map +1 -0
- package/dist/phase3/proof-trace-builder.js +441 -0
- package/dist/phase3/scanners/auth-by-ui-server-gap.d.ts +15 -0
- package/dist/phase3/scanners/auth-by-ui-server-gap.d.ts.map +1 -0
- package/dist/phase3/scanners/auth-by-ui-server-gap.js +237 -0
- package/dist/phase3/scanners/comment-claim-unproven.d.ts +14 -0
- package/dist/phase3/scanners/comment-claim-unproven.d.ts.map +1 -0
- package/dist/phase3/scanners/comment-claim-unproven.js +161 -0
- package/dist/phase3/scanners/index.d.ts +31 -0
- package/dist/phase3/scanners/index.d.ts.map +1 -0
- package/dist/phase3/scanners/index.js +40 -0
- package/dist/phase3/scanners/middleware-assumed-not-matching.d.ts +14 -0
- package/dist/phase3/scanners/middleware-assumed-not-matching.d.ts.map +1 -0
- package/dist/phase3/scanners/middleware-assumed-not-matching.js +172 -0
- package/dist/phase3/scanners/validation-claimed-missing.d.ts +15 -0
- package/dist/phase3/scanners/validation-claimed-missing.d.ts.map +1 -0
- package/dist/phase3/scanners/validation-claimed-missing.js +204 -0
- package/dist/scanners/abuse/compute-abuse.d.ts +20 -0
- package/dist/scanners/abuse/compute-abuse.d.ts.map +1 -0
- package/dist/scanners/abuse/compute-abuse.js +509 -0
- package/dist/scanners/abuse/index.d.ts +12 -0
- package/dist/scanners/abuse/index.d.ts.map +1 -0
- package/dist/scanners/abuse/index.js +15 -0
- package/dist/scanners/auth/index.d.ts +5 -0
- package/dist/scanners/auth/index.d.ts.map +1 -0
- package/dist/scanners/auth/index.js +10 -0
- package/dist/scanners/auth/middleware-gap.d.ts +22 -0
- package/dist/scanners/auth/middleware-gap.d.ts.map +1 -0
- package/dist/scanners/auth/middleware-gap.js +203 -0
- package/dist/scanners/auth/unprotected-api-route.d.ts +12 -0
- package/dist/scanners/auth/unprotected-api-route.d.ts.map +1 -0
- package/dist/scanners/auth/unprotected-api-route.js +126 -0
- package/dist/scanners/config/index.d.ts +5 -0
- package/dist/scanners/config/index.d.ts.map +1 -0
- package/dist/scanners/config/index.js +10 -0
- package/dist/scanners/config/insecure-defaults.d.ts +12 -0
- package/dist/scanners/config/insecure-defaults.d.ts.map +1 -0
- package/dist/scanners/config/insecure-defaults.js +77 -0
- package/dist/scanners/config/undocumented-env.d.ts +24 -0
- package/dist/scanners/config/undocumented-env.d.ts.map +1 -0
- package/dist/scanners/config/undocumented-env.js +159 -0
- package/dist/scanners/crypto/index.d.ts +6 -0
- package/dist/scanners/crypto/index.d.ts.map +1 -0
- package/dist/scanners/crypto/index.js +11 -0
- package/dist/scanners/crypto/jwt-decode-unverified.d.ts +14 -0
- package/dist/scanners/crypto/jwt-decode-unverified.d.ts.map +1 -0
- package/dist/scanners/crypto/jwt-decode-unverified.js +87 -0
- package/dist/scanners/crypto/math-random-tokens.d.ts +13 -0
- package/dist/scanners/crypto/math-random-tokens.d.ts.map +1 -0
- package/dist/scanners/crypto/math-random-tokens.js +80 -0
- package/dist/scanners/crypto/weak-hashing.d.ts +11 -0
- package/dist/scanners/crypto/weak-hashing.d.ts.map +1 -0
- package/dist/scanners/crypto/weak-hashing.js +95 -0
- package/dist/scanners/env-config.d.ts +24 -0
- package/dist/scanners/env-config.d.ts.map +1 -0
- package/dist/scanners/env-config.js +164 -0
- package/dist/scanners/hallucinations/index.d.ts +4 -0
- package/dist/scanners/hallucinations/index.d.ts.map +1 -0
- package/dist/scanners/hallucinations/index.js +8 -0
- package/dist/scanners/hallucinations/unused-security-imports.d.ts +36 -0
- package/dist/scanners/hallucinations/unused-security-imports.d.ts.map +1 -0
- package/dist/scanners/hallucinations/unused-security-imports.js +309 -0
- package/dist/scanners/helpers/ast-helpers.d.ts +6 -0
- package/dist/scanners/helpers/ast-helpers.d.ts.map +1 -0
- package/dist/scanners/helpers/ast-helpers.js +945 -0
- package/dist/scanners/helpers/context-builder.d.ts +17 -0
- package/dist/scanners/helpers/context-builder.d.ts.map +1 -0
- package/dist/scanners/helpers/context-builder.js +148 -0
- package/dist/scanners/helpers/index.d.ts +3 -0
- package/dist/scanners/helpers/index.d.ts.map +1 -0
- package/dist/scanners/helpers/index.js +2 -0
- package/dist/scanners/index.d.ts +30 -0
- package/dist/scanners/index.d.ts.map +1 -0
- package/dist/scanners/index.js +102 -0
- package/dist/scanners/middleware/index.d.ts +4 -0
- package/dist/scanners/middleware/index.d.ts.map +1 -0
- package/dist/scanners/middleware/index.js +7 -0
- package/dist/scanners/middleware/missing-rate-limit.d.ts +13 -0
- package/dist/scanners/middleware/missing-rate-limit.d.ts.map +1 -0
- package/dist/scanners/middleware/missing-rate-limit.js +140 -0
- package/dist/scanners/network/cors-misconfiguration.d.ts +14 -0
- package/dist/scanners/network/cors-misconfiguration.d.ts.map +1 -0
- package/dist/scanners/network/cors-misconfiguration.js +89 -0
- package/dist/scanners/network/index.d.ts +7 -0
- package/dist/scanners/network/index.d.ts.map +1 -0
- package/dist/scanners/network/index.js +18 -0
- package/dist/scanners/network/missing-timeout.d.ts +15 -0
- package/dist/scanners/network/missing-timeout.d.ts.map +1 -0
- package/dist/scanners/network/missing-timeout.js +93 -0
- package/dist/scanners/network/open-redirect.d.ts +15 -0
- package/dist/scanners/network/open-redirect.d.ts.map +1 -0
- package/dist/scanners/network/open-redirect.js +88 -0
- package/dist/scanners/network/ssrf-prone-fetch.d.ts +12 -0
- package/dist/scanners/network/ssrf-prone-fetch.d.ts.map +1 -0
- package/dist/scanners/network/ssrf-prone-fetch.js +90 -0
- package/dist/scanners/nextjs-middleware.d.ts +26 -0
- package/dist/scanners/nextjs-middleware.d.ts.map +1 -0
- package/dist/scanners/nextjs-middleware.js +246 -0
- package/dist/scanners/privacy/debug-flags.d.ts +13 -0
- package/dist/scanners/privacy/debug-flags.d.ts.map +1 -0
- package/dist/scanners/privacy/debug-flags.js +124 -0
- package/dist/scanners/privacy/index.d.ts +6 -0
- package/dist/scanners/privacy/index.d.ts.map +1 -0
- package/dist/scanners/privacy/index.js +11 -0
- package/dist/scanners/privacy/over-broad-response.d.ts +15 -0
- package/dist/scanners/privacy/over-broad-response.d.ts.map +1 -0
- package/dist/scanners/privacy/over-broad-response.js +109 -0
- package/dist/scanners/privacy/sensitive-logging.d.ts +11 -0
- package/dist/scanners/privacy/sensitive-logging.d.ts.map +1 -0
- package/dist/scanners/privacy/sensitive-logging.js +78 -0
- package/dist/scanners/types.d.ts +456 -0
- package/dist/scanners/types.d.ts.map +1 -0
- package/dist/scanners/types.js +16 -0
- package/dist/scanners/unused-security-imports.d.ts +34 -0
- package/dist/scanners/unused-security-imports.d.ts.map +1 -0
- package/dist/scanners/unused-security-imports.js +206 -0
- package/dist/scanners/uploads/index.d.ts +5 -0
- package/dist/scanners/uploads/index.d.ts.map +1 -0
- package/dist/scanners/uploads/index.js +9 -0
- package/dist/scanners/uploads/missing-constraints.d.ts +15 -0
- package/dist/scanners/uploads/missing-constraints.d.ts.map +1 -0
- package/dist/scanners/uploads/missing-constraints.js +109 -0
- package/dist/scanners/uploads/public-path.d.ts +11 -0
- package/dist/scanners/uploads/public-path.d.ts.map +1 -0
- package/dist/scanners/uploads/public-path.js +87 -0
- package/dist/scanners/validation/client-side-only.d.ts +14 -0
- package/dist/scanners/validation/client-side-only.d.ts.map +1 -0
- package/dist/scanners/validation/client-side-only.js +140 -0
- package/dist/scanners/validation/ignored-validation.d.ts +12 -0
- package/dist/scanners/validation/ignored-validation.d.ts.map +1 -0
- package/dist/scanners/validation/ignored-validation.js +119 -0
- package/dist/scanners/validation/index.d.ts +5 -0
- package/dist/scanners/validation/index.d.ts.map +1 -0
- package/dist/scanners/validation/index.js +9 -0
- package/dist/utils/exclude-patterns.d.ts +35 -0
- package/dist/utils/exclude-patterns.d.ts.map +1 -0
- package/dist/utils/exclude-patterns.js +78 -0
- package/dist/utils/file-utils.d.ts +37 -0
- package/dist/utils/file-utils.d.ts.map +1 -0
- package/dist/utils/file-utils.js +77 -0
- package/dist/utils/fingerprint.d.ts +25 -0
- package/dist/utils/fingerprint.d.ts.map +1 -0
- package/dist/utils/fingerprint.js +28 -0
- package/dist/utils/git-info.d.ts +14 -0
- package/dist/utils/git-info.d.ts.map +1 -0
- package/dist/utils/git-info.js +55 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/progress.d.ts +42 -0
- package/dist/utils/progress.d.ts.map +1 -0
- package/dist/utils/progress.js +165 -0
- package/dist/utils/sarif-formatter.d.ts +92 -0
- package/dist/utils/sarif-formatter.d.ts.map +1 -0
- package/dist/utils/sarif-formatter.js +172 -0
- package/package.json +66 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner-packs.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/scanners/scanner-packs.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,126 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unused-security-imports.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/scanners/unused-security-imports.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,145 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { ARTIFACT_VERSION, computeSummary, } from "@vibecheck/schema";
|
|
4
|
+
function generateDemoArtifact() {
|
|
5
|
+
const findings = [
|
|
6
|
+
{
|
|
7
|
+
id: "demo-finding-1",
|
|
8
|
+
ruleId: "VC-AUTH-001",
|
|
9
|
+
title: "Missing Authentication in API Route",
|
|
10
|
+
description: "The API route /api/users does not verify user authentication before returning sensitive data. An attacker could access user information without logging in.",
|
|
11
|
+
severity: "critical",
|
|
12
|
+
confidence: 0.95,
|
|
13
|
+
category: "auth",
|
|
14
|
+
evidence: [
|
|
15
|
+
{
|
|
16
|
+
file: "pages/api/users.ts",
|
|
17
|
+
startLine: 5,
|
|
18
|
+
endLine: 12,
|
|
19
|
+
snippet: `export default async function handler(req, res) {
|
|
20
|
+
// No authentication check!
|
|
21
|
+
const users = await db.query("SELECT * FROM users");
|
|
22
|
+
res.json(users);
|
|
23
|
+
}`,
|
|
24
|
+
label: "Unauthenticated API handler",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
remediation: {
|
|
28
|
+
recommendedFix: "Add authentication middleware to verify the user session before processing the request.",
|
|
29
|
+
patch: `import { getServerSession } from "next-auth";
|
|
30
|
+
|
|
31
|
+
export default async function handler(req, res) {
|
|
32
|
+
const session = await getServerSession(req, res);
|
|
33
|
+
if (!session) {
|
|
34
|
+
return res.status(401).json({ error: "Unauthorized" });
|
|
35
|
+
}
|
|
36
|
+
const users = await db.query("SELECT * FROM users");
|
|
37
|
+
res.json(users);
|
|
38
|
+
}`,
|
|
39
|
+
},
|
|
40
|
+
links: {
|
|
41
|
+
owasp: "https://owasp.org/Top10/A01_2021-Broken_Access_Control/",
|
|
42
|
+
cwe: "https://cwe.mitre.org/data/definitions/306.html",
|
|
43
|
+
},
|
|
44
|
+
fingerprint: "demo-fp-001",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "demo-finding-2",
|
|
48
|
+
ruleId: "VC-INJECT-001",
|
|
49
|
+
title: "SQL Injection Vulnerability",
|
|
50
|
+
description: "User input is directly interpolated into SQL query without parameterization, allowing attackers to execute arbitrary SQL commands.",
|
|
51
|
+
severity: "critical",
|
|
52
|
+
confidence: 0.92,
|
|
53
|
+
category: "injection",
|
|
54
|
+
evidence: [
|
|
55
|
+
{
|
|
56
|
+
file: "lib/database.ts",
|
|
57
|
+
startLine: 23,
|
|
58
|
+
endLine: 25,
|
|
59
|
+
snippet: `async function getUser(id: string) {
|
|
60
|
+
return db.query(\`SELECT * FROM users WHERE id = '\${id}'\`);
|
|
61
|
+
}`,
|
|
62
|
+
label: "SQL string interpolation",
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
proof: {
|
|
66
|
+
summary: "User input flows from API parameter to SQL query without sanitization",
|
|
67
|
+
nodes: [
|
|
68
|
+
{
|
|
69
|
+
kind: "route",
|
|
70
|
+
label: "User ID received from request query parameter",
|
|
71
|
+
file: "pages/api/user/[id].ts",
|
|
72
|
+
line: 4,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
kind: "function",
|
|
76
|
+
label: "ID passed to getUser() without validation",
|
|
77
|
+
file: "pages/api/user/[id].ts",
|
|
78
|
+
line: 8,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
kind: "sink",
|
|
82
|
+
label: "ID interpolated directly into SQL query",
|
|
83
|
+
file: "lib/database.ts",
|
|
84
|
+
line: 24,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
remediation: {
|
|
89
|
+
recommendedFix: "Use parameterized queries or prepared statements instead of string interpolation.",
|
|
90
|
+
patch: `async function getUser(id: string) {
|
|
91
|
+
return db.query("SELECT * FROM users WHERE id = $1", [id]);
|
|
92
|
+
}`,
|
|
93
|
+
},
|
|
94
|
+
links: {
|
|
95
|
+
owasp: "https://owasp.org/Top10/A03_2021-Injection/",
|
|
96
|
+
cwe: "https://cwe.mitre.org/data/definitions/89.html",
|
|
97
|
+
},
|
|
98
|
+
fingerprint: "demo-fp-002",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: "demo-finding-3",
|
|
102
|
+
ruleId: "VC-XSS-001",
|
|
103
|
+
title: "Cross-Site Scripting (XSS) via dangerouslySetInnerHTML",
|
|
104
|
+
description: "User-generated content is rendered using dangerouslySetInnerHTML without sanitization, enabling XSS attacks.",
|
|
105
|
+
severity: "high",
|
|
106
|
+
confidence: 0.88,
|
|
107
|
+
category: "validation",
|
|
108
|
+
evidence: [
|
|
109
|
+
{
|
|
110
|
+
file: "components/Comment.tsx",
|
|
111
|
+
startLine: 15,
|
|
112
|
+
endLine: 17,
|
|
113
|
+
snippet: `function Comment({ content }: { content: string }) {
|
|
114
|
+
return <div dangerouslySetInnerHTML={{ __html: content }} />;
|
|
115
|
+
}`,
|
|
116
|
+
label: "Unsanitized HTML rendering",
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
remediation: {
|
|
120
|
+
recommendedFix: "Sanitize HTML content using a library like DOMPurify before rendering.",
|
|
121
|
+
patch: `import DOMPurify from "dompurify";
|
|
122
|
+
|
|
123
|
+
function Comment({ content }: { content: string }) {
|
|
124
|
+
const sanitized = DOMPurify.sanitize(content);
|
|
125
|
+
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
|
|
126
|
+
}`,
|
|
127
|
+
},
|
|
128
|
+
links: {
|
|
129
|
+
owasp: "https://owasp.org/Top10/A03_2021-Injection/",
|
|
130
|
+
cwe: "https://cwe.mitre.org/data/definitions/79.html",
|
|
131
|
+
},
|
|
132
|
+
fingerprint: "demo-fp-003",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: "demo-finding-4",
|
|
136
|
+
ruleId: "VC-SECRETS-001",
|
|
137
|
+
title: "Hardcoded API Key in Source Code",
|
|
138
|
+
description: "An API key is hardcoded in the source file. This could be exposed in version control or client bundles.",
|
|
139
|
+
severity: "high",
|
|
140
|
+
confidence: 0.85,
|
|
141
|
+
category: "secrets",
|
|
142
|
+
evidence: [
|
|
143
|
+
{
|
|
144
|
+
file: "lib/stripe.ts",
|
|
145
|
+
startLine: 3,
|
|
146
|
+
endLine: 3,
|
|
147
|
+
snippet: `const STRIPE_KEY = "sk_live_abc123xyz789";`,
|
|
148
|
+
label: "Hardcoded secret key",
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
remediation: {
|
|
152
|
+
recommendedFix: "Move secrets to environment variables and never commit them to version control.",
|
|
153
|
+
patch: `const STRIPE_KEY = process.env.STRIPE_SECRET_KEY;
|
|
154
|
+
|
|
155
|
+
if (!STRIPE_KEY) {
|
|
156
|
+
throw new Error("STRIPE_SECRET_KEY environment variable is required");
|
|
157
|
+
}`,
|
|
158
|
+
},
|
|
159
|
+
fingerprint: "demo-fp-004",
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: "demo-finding-5",
|
|
163
|
+
ruleId: "VC-HALL-001",
|
|
164
|
+
title: "Unused Security Import: helmet",
|
|
165
|
+
description: "The helmet security middleware is imported but never used, suggesting incomplete security implementation.",
|
|
166
|
+
severity: "medium",
|
|
167
|
+
confidence: 0.94,
|
|
168
|
+
category: "middleware",
|
|
169
|
+
evidence: [
|
|
170
|
+
{
|
|
171
|
+
file: "server.ts",
|
|
172
|
+
startLine: 2,
|
|
173
|
+
endLine: 2,
|
|
174
|
+
snippet: `import helmet from "helmet";`,
|
|
175
|
+
label: "Unused helmet import",
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
claim: {
|
|
179
|
+
type: "OTHER",
|
|
180
|
+
source: "import",
|
|
181
|
+
scope: "module",
|
|
182
|
+
strength: "weak",
|
|
183
|
+
textEvidence: 'import helmet from "helmet"',
|
|
184
|
+
location: {
|
|
185
|
+
file: "server.ts",
|
|
186
|
+
startLine: 2,
|
|
187
|
+
endLine: 2,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
remediation: {
|
|
191
|
+
recommendedFix: "Apply the helmet middleware to add security headers, or remove the unused import.",
|
|
192
|
+
patch: `import helmet from "helmet";
|
|
193
|
+
|
|
194
|
+
app.use(helmet());`,
|
|
195
|
+
},
|
|
196
|
+
fingerprint: "demo-fp-005",
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: "demo-finding-6",
|
|
200
|
+
ruleId: "VC-PRIVACY-001",
|
|
201
|
+
title: "Sensitive Data in Console Logs",
|
|
202
|
+
description: "User passwords are being logged to the console, which could expose credentials in log files or monitoring systems.",
|
|
203
|
+
severity: "medium",
|
|
204
|
+
confidence: 0.91,
|
|
205
|
+
category: "privacy",
|
|
206
|
+
evidence: [
|
|
207
|
+
{
|
|
208
|
+
file: "lib/auth.ts",
|
|
209
|
+
startLine: 45,
|
|
210
|
+
endLine: 45,
|
|
211
|
+
snippet: `console.log("Login attempt:", { email, password });`,
|
|
212
|
+
label: "Password logged to console",
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
remediation: {
|
|
216
|
+
recommendedFix: "Remove sensitive data from log statements. Only log non-sensitive identifiers.",
|
|
217
|
+
patch: `console.log("Login attempt:", { email, timestamp: Date.now() });`,
|
|
218
|
+
},
|
|
219
|
+
fingerprint: "demo-fp-006",
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: "demo-finding-7",
|
|
223
|
+
ruleId: "VC-CONFIG-001",
|
|
224
|
+
title: "Insecure Cookie Configuration",
|
|
225
|
+
description: "Session cookies are configured without the Secure and HttpOnly flags, making them vulnerable to interception and XSS attacks.",
|
|
226
|
+
severity: "low",
|
|
227
|
+
confidence: 0.82,
|
|
228
|
+
category: "config",
|
|
229
|
+
evidence: [
|
|
230
|
+
{
|
|
231
|
+
file: "lib/session.ts",
|
|
232
|
+
startLine: 8,
|
|
233
|
+
endLine: 12,
|
|
234
|
+
snippet: `const sessionConfig = {
|
|
235
|
+
name: "session",
|
|
236
|
+
secret: process.env.SESSION_SECRET,
|
|
237
|
+
// Missing: secure, httpOnly, sameSite
|
|
238
|
+
};`,
|
|
239
|
+
label: "Insecure session config",
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
remediation: {
|
|
243
|
+
recommendedFix: "Add secure cookie options to protect against common attacks.",
|
|
244
|
+
patch: `const sessionConfig = {
|
|
245
|
+
name: "session",
|
|
246
|
+
secret: process.env.SESSION_SECRET,
|
|
247
|
+
cookie: {
|
|
248
|
+
secure: process.env.NODE_ENV === "production",
|
|
249
|
+
httpOnly: true,
|
|
250
|
+
sameSite: "strict",
|
|
251
|
+
maxAge: 60 * 60 * 24 * 7, // 1 week
|
|
252
|
+
},
|
|
253
|
+
};`,
|
|
254
|
+
},
|
|
255
|
+
fingerprint: "demo-fp-007",
|
|
256
|
+
},
|
|
257
|
+
];
|
|
258
|
+
const artifact = {
|
|
259
|
+
artifactVersion: ARTIFACT_VERSION,
|
|
260
|
+
generatedAt: new Date().toISOString(),
|
|
261
|
+
tool: {
|
|
262
|
+
name: "vibecheck",
|
|
263
|
+
version: "0.0.1",
|
|
264
|
+
},
|
|
265
|
+
repo: {
|
|
266
|
+
name: "demo-project",
|
|
267
|
+
rootPathHash: "demo-hash-12345",
|
|
268
|
+
git: {
|
|
269
|
+
branch: "main",
|
|
270
|
+
commit: "abc1234",
|
|
271
|
+
isDirty: false,
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
summary: computeSummary(findings),
|
|
275
|
+
findings,
|
|
276
|
+
metrics: {
|
|
277
|
+
filesScanned: 42,
|
|
278
|
+
linesOfCode: 3847,
|
|
279
|
+
scanDurationMs: 1250,
|
|
280
|
+
rulesExecuted: 12,
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
return artifact;
|
|
284
|
+
}
|
|
285
|
+
export function executeDemoArtifact(options) {
|
|
286
|
+
const artifact = generateDemoArtifact();
|
|
287
|
+
const json = JSON.stringify(artifact, null, 2);
|
|
288
|
+
if (options.out) {
|
|
289
|
+
const outPath = resolve(process.cwd(), options.out);
|
|
290
|
+
writeFileSync(outPath, json, "utf-8");
|
|
291
|
+
console.log(`Demo artifact written to: ${outPath}`);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
console.log(json);
|
|
295
|
+
}
|
|
296
|
+
console.log(`
|
|
297
|
+
============================================================
|
|
298
|
+
Demo Artifact Generated
|
|
299
|
+
============================================================
|
|
300
|
+
|
|
301
|
+
Findings: ${artifact.summary.totalFindings}
|
|
302
|
+
- Critical: ${artifact.summary.bySeverity.critical}
|
|
303
|
+
- High: ${artifact.summary.bySeverity.high}
|
|
304
|
+
- Medium: ${artifact.summary.bySeverity.medium}
|
|
305
|
+
- Low: ${artifact.summary.bySeverity.low}
|
|
306
|
+
- Info: ${artifact.summary.bySeverity.info}
|
|
307
|
+
|
|
308
|
+
Use this artifact to test the VibeCheck UI:
|
|
309
|
+
1. Run: pnpm dev:web
|
|
310
|
+
2. Open: http://localhost:3000
|
|
311
|
+
3. Import the generated JSON file
|
|
312
|
+
`);
|
|
313
|
+
}
|
|
314
|
+
export function registerDemoArtifactCommand(program) {
|
|
315
|
+
program
|
|
316
|
+
.command("demo-artifact")
|
|
317
|
+
.description("Generate a demo artifact for testing the UI")
|
|
318
|
+
.option("-o, --out <path>", "Output file path (prints to stdout if not specified)")
|
|
319
|
+
.action((options) => {
|
|
320
|
+
executeDemoArtifact(options);
|
|
321
|
+
});
|
|
322
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { type ProfileName } from "@vibecheck/policy";
|
|
3
|
+
/**
|
|
4
|
+
* Evaluate command options
|
|
5
|
+
*/
|
|
6
|
+
export interface EvaluateOptions {
|
|
7
|
+
/** Path to scan artifact (JSON) */
|
|
8
|
+
artifact: string;
|
|
9
|
+
/** Optional baseline artifact for regression detection */
|
|
10
|
+
baseline?: string;
|
|
11
|
+
/** Policy profile name */
|
|
12
|
+
profile: ProfileName;
|
|
13
|
+
/** Path to policy config file (JSON) */
|
|
14
|
+
config?: string;
|
|
15
|
+
/** Path to waivers file (JSON) */
|
|
16
|
+
waivers?: string;
|
|
17
|
+
/** Output report to file */
|
|
18
|
+
out?: string;
|
|
19
|
+
/** JSON output only (no console summary) */
|
|
20
|
+
quiet: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Execute the evaluate command
|
|
24
|
+
*/
|
|
25
|
+
export declare function executeEvaluate(options: EvaluateOptions): Promise<number>;
|
|
26
|
+
/**
|
|
27
|
+
* Register evaluate command with commander
|
|
28
|
+
*/
|
|
29
|
+
export declare function registerEvaluateCommand(program: Command): void;
|
|
30
|
+
//# sourceMappingURL=evaluate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/commands/evaluate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAKL,KAAK,WAAW,EAOjB,MAAM,mBAAmB,CAAC;AAG3B;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,KAAK,EAAE,OAAO,CAAC;CAChB;AAkLD;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAqD/E;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0E9D"}
|