vskill 0.1.2 → 0.1.4
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/dist/audit/__fixtures__/clean-project/app.d.ts +1 -0
- package/dist/audit/__fixtures__/clean-project/app.js +8 -0
- package/dist/audit/__fixtures__/clean-project/app.js.map +1 -0
- package/dist/audit/__fixtures__/clean-project/utils.d.ts +2 -0
- package/dist/audit/__fixtures__/clean-project/utils.js +8 -0
- package/dist/audit/__fixtures__/clean-project/utils.js.map +1 -0
- package/dist/audit/__fixtures__/mixed-project/risky.d.ts +1 -0
- package/dist/audit/__fixtures__/mixed-project/risky.js +6 -0
- package/dist/audit/__fixtures__/mixed-project/risky.js.map +1 -0
- package/dist/audit/__fixtures__/mixed-project/safe.d.ts +1 -0
- package/dist/audit/__fixtures__/mixed-project/safe.js +5 -0
- package/dist/audit/__fixtures__/mixed-project/safe.js.map +1 -0
- package/dist/audit/__fixtures__/vulnerable-project/handler.d.ts +4 -0
- package/dist/audit/__fixtures__/vulnerable-project/handler.js +21 -0
- package/dist/audit/__fixtures__/vulnerable-project/handler.js.map +1 -0
- package/dist/audit/audit-integration.test.d.ts +1 -0
- package/dist/audit/audit-integration.test.js +92 -0
- package/dist/audit/audit-integration.test.js.map +1 -0
- package/dist/audit/audit-llm.d.ts +25 -0
- package/dist/audit/audit-llm.js +139 -0
- package/dist/audit/audit-llm.js.map +1 -0
- package/dist/audit/audit-llm.test.d.ts +1 -0
- package/dist/audit/audit-llm.test.js +110 -0
- package/dist/audit/audit-llm.test.js.map +1 -0
- package/dist/audit/audit-patterns.d.ts +31 -0
- package/dist/audit/audit-patterns.js +239 -0
- package/dist/audit/audit-patterns.js.map +1 -0
- package/dist/audit/audit-patterns.test.d.ts +1 -0
- package/dist/audit/audit-patterns.test.js +91 -0
- package/dist/audit/audit-patterns.test.js.map +1 -0
- package/dist/audit/audit-scanner.d.ts +16 -0
- package/dist/audit/audit-scanner.js +151 -0
- package/dist/audit/audit-scanner.js.map +1 -0
- package/dist/audit/audit-scanner.test.d.ts +1 -0
- package/dist/audit/audit-scanner.test.js +112 -0
- package/dist/audit/audit-scanner.test.js.map +1 -0
- package/dist/audit/audit-types.d.ts +98 -0
- package/dist/audit/audit-types.js +19 -0
- package/dist/audit/audit-types.js.map +1 -0
- package/dist/audit/audit-types.test.d.ts +1 -0
- package/dist/audit/audit-types.test.js +140 -0
- package/dist/audit/audit-types.test.js.map +1 -0
- package/dist/audit/config.d.ts +13 -0
- package/dist/audit/config.js +90 -0
- package/dist/audit/config.js.map +1 -0
- package/dist/audit/config.test.d.ts +1 -0
- package/dist/audit/config.test.js +44 -0
- package/dist/audit/config.test.js.map +1 -0
- package/dist/audit/file-discovery.d.ts +15 -0
- package/dist/audit/file-discovery.js +153 -0
- package/dist/audit/file-discovery.js.map +1 -0
- package/dist/audit/file-discovery.test.d.ts +1 -0
- package/dist/audit/file-discovery.test.js +120 -0
- package/dist/audit/file-discovery.test.js.map +1 -0
- package/dist/audit/fix-suggestions.d.ts +13 -0
- package/dist/audit/fix-suggestions.js +84 -0
- package/dist/audit/fix-suggestions.js.map +1 -0
- package/dist/audit/fix-suggestions.test.d.ts +1 -0
- package/dist/audit/fix-suggestions.test.js +35 -0
- package/dist/audit/fix-suggestions.test.js.map +1 -0
- package/dist/audit/formatters/json-formatter.d.ts +8 -0
- package/dist/audit/formatters/json-formatter.js +10 -0
- package/dist/audit/formatters/json-formatter.js.map +1 -0
- package/dist/audit/formatters/json-formatter.test.d.ts +1 -0
- package/dist/audit/formatters/json-formatter.test.js +49 -0
- package/dist/audit/formatters/json-formatter.test.js.map +1 -0
- package/dist/audit/formatters/report-formatter.d.ts +8 -0
- package/dist/audit/formatters/report-formatter.js +97 -0
- package/dist/audit/formatters/report-formatter.js.map +1 -0
- package/dist/audit/formatters/report-formatter.test.d.ts +1 -0
- package/dist/audit/formatters/report-formatter.test.js +51 -0
- package/dist/audit/formatters/report-formatter.test.js.map +1 -0
- package/dist/audit/formatters/sarif-formatter.d.ts +11 -0
- package/dist/audit/formatters/sarif-formatter.js +87 -0
- package/dist/audit/formatters/sarif-formatter.js.map +1 -0
- package/dist/audit/formatters/sarif-formatter.test.d.ts +1 -0
- package/dist/audit/formatters/sarif-formatter.test.js +71 -0
- package/dist/audit/formatters/sarif-formatter.test.js.map +1 -0
- package/dist/audit/formatters/terminal-formatter.d.ts +9 -0
- package/dist/audit/formatters/terminal-formatter.js +61 -0
- package/dist/audit/formatters/terminal-formatter.js.map +1 -0
- package/dist/audit/formatters/terminal-formatter.test.d.ts +1 -0
- package/dist/audit/formatters/terminal-formatter.test.js +51 -0
- package/dist/audit/formatters/terminal-formatter.test.js.map +1 -0
- package/dist/audit/index.d.ts +2 -0
- package/dist/audit/index.js +2 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/blocklist/blocklist-e2e.test.d.ts +1 -0
- package/dist/blocklist/blocklist-e2e.test.js +346 -0
- package/dist/blocklist/blocklist-e2e.test.js.map +1 -0
- package/dist/commands/add-blocklist-e2e.test.d.ts +1 -0
- package/dist/commands/add-blocklist-e2e.test.js +390 -0
- package/dist/commands/add-blocklist-e2e.test.js.map +1 -0
- package/dist/commands/add.js +184 -7
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/add.test.js +159 -19
- package/dist/commands/add.test.js.map +1 -1
- package/dist/commands/audit.d.ts +23 -0
- package/dist/commands/audit.js +100 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/audit.test.d.ts +1 -0
- package/dist/commands/audit.test.js +79 -0
- package/dist/commands/audit.test.js.map +1 -0
- package/dist/discovery/github-tree.d.ts +15 -0
- package/dist/discovery/github-tree.js +56 -0
- package/dist/discovery/github-tree.js.map +1 -0
- package/dist/discovery/github-tree.test.d.ts +1 -0
- package/dist/discovery/github-tree.test.js +143 -0
- package/dist/discovery/github-tree.test.js.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -1
- package/dist/lockfile/index.d.ts +1 -0
- package/dist/lockfile/index.js +1 -0
- package/dist/lockfile/index.js.map +1 -1
- package/dist/lockfile/lockfile.js +2 -1
- package/dist/lockfile/lockfile.js.map +1 -1
- package/dist/lockfile/project-root.d.ts +11 -0
- package/dist/lockfile/project-root.js +29 -0
- package/dist/lockfile/project-root.js.map +1 -0
- package/dist/lockfile/project-root.test.d.ts +1 -0
- package/dist/lockfile/project-root.test.js +49 -0
- package/dist/lockfile/project-root.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Clean application — no security issues
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
const server = createServer((req, res) => {
|
|
4
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
5
|
+
res.end(JSON.stringify({ status: "ok" }));
|
|
6
|
+
});
|
|
7
|
+
server.listen(3000);
|
|
8
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../../../src/audit/__fixtures__/clean-project/app.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IACvC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../src/audit/__fixtures__/clean-project/utils.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,MAAM,UAAU,GAAG,CAAC,CAAS,EAAE,CAAS;IACtC,OAAO,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,IAAY;IAChC,OAAO,UAAU,IAAI,GAAG,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function deploy(branch: string): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"risky.js","sourceRoot":"","sources":["../../../../src/audit/__fixtures__/mixed-project/risky.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C,MAAM,UAAU,MAAM,CAAC,MAAc;IACnC,IAAI,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseId(raw: string): number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe.js","sourceRoot":"","sources":["../../../../src/audit/__fixtures__/mixed-project/safe.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,OAAO,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Vulnerable handler — multiple security issues
|
|
2
|
+
import { exec } from "node:child_process";
|
|
3
|
+
// CI-001: Command injection via exec
|
|
4
|
+
export function runCommand(userInput) {
|
|
5
|
+
exec(`ls ${userInput}`);
|
|
6
|
+
}
|
|
7
|
+
// CE-001: Code execution via eval
|
|
8
|
+
export function evaluate(code) {
|
|
9
|
+
return eval(code);
|
|
10
|
+
}
|
|
11
|
+
// XSS-001: innerHTML assignment
|
|
12
|
+
export function renderHtml(el, content) {
|
|
13
|
+
el.innerHTML = content;
|
|
14
|
+
}
|
|
15
|
+
// HS-001: Hardcoded API key
|
|
16
|
+
const API_KEY = "sk-1234567890abcdef1234567890abcdef";
|
|
17
|
+
// SQLI-001: SQL injection via concatenation
|
|
18
|
+
export function getUser(db, userId) {
|
|
19
|
+
return db.query("SELECT * FROM users WHERE id = '" + userId + "'");
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../../src/audit/__fixtures__/vulnerable-project/handler.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C,qCAAqC;AACrC,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,IAAI,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC;AACpB,CAAC;AAED,gCAAgC;AAChC,MAAM,UAAU,UAAU,CAAC,EAAe,EAAE,OAAe;IACzD,EAAE,CAAC,SAAS,GAAG,OAAO,CAAC;AACzB,CAAC;AAED,4BAA4B;AAC5B,MAAM,OAAO,GAAG,qCAAqC,CAAC;AAEtD,4CAA4C;AAC5C,MAAM,UAAU,OAAO,CAAC,EAAO,EAAE,MAAc;IAC7C,OAAO,EAAE,CAAC,KAAK,CAAC,kCAAkC,GAAG,MAAM,GAAG,GAAG,CAAC,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { discoverAuditFiles } from "./file-discovery.js";
|
|
4
|
+
import { runAuditScan } from "./audit-scanner.js";
|
|
5
|
+
import { createDefaultAuditConfig } from "./audit-types.js";
|
|
6
|
+
import { formatJson } from "./formatters/json-formatter.js";
|
|
7
|
+
import { formatSarif } from "./formatters/sarif-formatter.js";
|
|
8
|
+
import { attachFixSuggestions } from "./fix-suggestions.js";
|
|
9
|
+
import { loadAuditConfig } from "./config.js";
|
|
10
|
+
const FIXTURES = join(import.meta.dirname, "__fixtures__");
|
|
11
|
+
describe("audit integration", () => {
|
|
12
|
+
it("TC-054: clean project produces PASS verdict", async () => {
|
|
13
|
+
const config = createDefaultAuditConfig();
|
|
14
|
+
const files = await discoverAuditFiles(join(FIXTURES, "clean-project"), config);
|
|
15
|
+
const result = runAuditScan(files, config);
|
|
16
|
+
expect(result.findings).toHaveLength(0);
|
|
17
|
+
expect(result.summary.verdict).toBe("PASS");
|
|
18
|
+
expect(result.summary.score).toBe(100);
|
|
19
|
+
expect(result.filesScanned).toBe(2);
|
|
20
|
+
});
|
|
21
|
+
it("TC-055: vulnerable project detects all planted vulnerabilities", async () => {
|
|
22
|
+
const config = createDefaultAuditConfig();
|
|
23
|
+
const files = await discoverAuditFiles(join(FIXTURES, "vulnerable-project"), config);
|
|
24
|
+
const result = runAuditScan(files, config);
|
|
25
|
+
expect(result.findings.length).toBeGreaterThanOrEqual(5);
|
|
26
|
+
const ruleIds = result.findings.map((f) => f.ruleId);
|
|
27
|
+
// exec() detection
|
|
28
|
+
expect(ruleIds.some((id) => id.startsWith("CI-"))).toBe(true);
|
|
29
|
+
// eval() detection
|
|
30
|
+
expect(ruleIds.some((id) => id.startsWith("CE-"))).toBe(true);
|
|
31
|
+
// innerHTML detection
|
|
32
|
+
expect(ruleIds.some((id) => id.startsWith("XSS-"))).toBe(true);
|
|
33
|
+
// Hardcoded secret detection
|
|
34
|
+
expect(ruleIds.some((id) => id.startsWith("HS-"))).toBe(true);
|
|
35
|
+
// SQL injection detection
|
|
36
|
+
expect(ruleIds.some((id) => id.startsWith("SQLI-"))).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
it("TC-056: JSON output is valid and complete", async () => {
|
|
39
|
+
const config = createDefaultAuditConfig();
|
|
40
|
+
const files = await discoverAuditFiles(join(FIXTURES, "mixed-project"), config);
|
|
41
|
+
const result = runAuditScan(files, config);
|
|
42
|
+
const json = formatJson(result);
|
|
43
|
+
const parsed = JSON.parse(json);
|
|
44
|
+
expect(parsed.findings).toBeDefined();
|
|
45
|
+
expect(parsed.summary).toBeDefined();
|
|
46
|
+
expect(parsed.summary.critical).toBeDefined();
|
|
47
|
+
expect(parsed.summary.high).toBeDefined();
|
|
48
|
+
expect(parsed.summary.medium).toBeDefined();
|
|
49
|
+
expect(parsed.summary.low).toBeDefined();
|
|
50
|
+
expect(parsed.summary.info).toBeDefined();
|
|
51
|
+
expect(parsed.summary.score).toBeGreaterThanOrEqual(0);
|
|
52
|
+
expect(parsed.summary.verdict).toBeDefined();
|
|
53
|
+
expect(parsed.filesScanned).toBe(2);
|
|
54
|
+
});
|
|
55
|
+
it("TC-057: SARIF output has correct structure", async () => {
|
|
56
|
+
const config = createDefaultAuditConfig();
|
|
57
|
+
const files = await discoverAuditFiles(join(FIXTURES, "vulnerable-project"), config);
|
|
58
|
+
const result = runAuditScan(files, config);
|
|
59
|
+
const sarif = formatSarif(result);
|
|
60
|
+
const parsed = JSON.parse(sarif);
|
|
61
|
+
expect(parsed.version).toBe("2.1.0");
|
|
62
|
+
expect(parsed.$schema).toContain("sarif");
|
|
63
|
+
expect(parsed.runs).toHaveLength(1);
|
|
64
|
+
expect(parsed.runs[0].tool.driver.name).toBe("vskill-audit");
|
|
65
|
+
expect(parsed.runs[0].results.length).toBeGreaterThanOrEqual(5);
|
|
66
|
+
// Each result has correct location structure
|
|
67
|
+
for (const r of parsed.runs[0].results) {
|
|
68
|
+
expect(r.ruleId).toBeDefined();
|
|
69
|
+
expect(r.message.text).toBeDefined();
|
|
70
|
+
expect(r.locations[0].physicalLocation.artifactLocation.uri).toBeDefined();
|
|
71
|
+
expect(r.locations[0].physicalLocation.region.startLine).toBeGreaterThan(0);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
it("fix suggestions are attached when fix=true", async () => {
|
|
75
|
+
const config = createDefaultAuditConfig();
|
|
76
|
+
config.fix = true;
|
|
77
|
+
const files = await discoverAuditFiles(join(FIXTURES, "vulnerable-project"), config);
|
|
78
|
+
const result = runAuditScan(files, config);
|
|
79
|
+
const withFixes = attachFixSuggestions(result.findings, true);
|
|
80
|
+
// Every finding should have a suggestedFix
|
|
81
|
+
for (const f of withFixes) {
|
|
82
|
+
expect(f.suggestedFix).toBeDefined();
|
|
83
|
+
expect(f.suggestedFix.length).toBeGreaterThan(0);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
it("config loading works end-to-end", async () => {
|
|
87
|
+
const config = await loadAuditConfig(join(FIXTURES, "clean-project"), {});
|
|
88
|
+
expect(config.maxFiles).toBe(500);
|
|
89
|
+
expect(config.excludePaths).toEqual([]);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=audit-integration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-integration.test.js","sourceRoot":"","sources":["../../src/audit/audit-integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-based security analysis engine.
|
|
3
|
+
*
|
|
4
|
+
* Performs deeper analysis on Tier 1 flagged files using an LLM
|
|
5
|
+
* (via subprocess). Traces data flows and identifies complex
|
|
6
|
+
* vulnerabilities that regex patterns alone cannot detect.
|
|
7
|
+
*/
|
|
8
|
+
import type { AuditConfig, AuditFile, AuditFinding } from "./audit-types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Build a security analysis prompt for the LLM.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildLlmPrompt(file: AuditFile, tier1Findings: AuditFinding[]): string;
|
|
13
|
+
/**
|
|
14
|
+
* Parse the LLM's JSON response into AuditFinding objects.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseLlmResponse(response: string, filePath: string): AuditFinding[];
|
|
17
|
+
/**
|
|
18
|
+
* Run LLM analysis on flagged files.
|
|
19
|
+
*
|
|
20
|
+
* @param files - Files that had Tier 1 findings
|
|
21
|
+
* @param tier1Findings - All Tier 1 findings (to provide context)
|
|
22
|
+
* @param config - Audit configuration
|
|
23
|
+
* @returns Additional findings from LLM analysis
|
|
24
|
+
*/
|
|
25
|
+
export declare function runLlmAnalysis(files: AuditFile[], tier1Findings: AuditFinding[], config: AuditConfig): Promise<AuditFinding[]>;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-based security analysis engine.
|
|
3
|
+
*
|
|
4
|
+
* Performs deeper analysis on Tier 1 flagged files using an LLM
|
|
5
|
+
* (via subprocess). Traces data flows and identifies complex
|
|
6
|
+
* vulnerabilities that regex patterns alone cannot detect.
|
|
7
|
+
*/
|
|
8
|
+
import { execFile } from "node:child_process";
|
|
9
|
+
import { promisify } from "node:util";
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
|
+
/**
|
|
12
|
+
* Build a security analysis prompt for the LLM.
|
|
13
|
+
*/
|
|
14
|
+
export function buildLlmPrompt(file, tier1Findings) {
|
|
15
|
+
const findingSummaries = tier1Findings
|
|
16
|
+
.map((f) => ` - [${f.ruleId}] ${f.severity} (${f.category}): ${f.message} (line ${f.line})`)
|
|
17
|
+
.join("\n");
|
|
18
|
+
return `Analyze this file for security vulnerabilities. Focus on data flow from user input to dangerous sinks.
|
|
19
|
+
|
|
20
|
+
File: ${file.path}
|
|
21
|
+
Tier 1 findings:
|
|
22
|
+
${findingSummaries}
|
|
23
|
+
|
|
24
|
+
Source code:
|
|
25
|
+
\`\`\`
|
|
26
|
+
${file.content}
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
Respond with JSON only:
|
|
30
|
+
{
|
|
31
|
+
"findings": [{
|
|
32
|
+
"ruleId": "LLM-XXX-NNN",
|
|
33
|
+
"severity": "critical|high|medium|low|info",
|
|
34
|
+
"confidence": "high|medium|low",
|
|
35
|
+
"category": "category-name",
|
|
36
|
+
"message": "description",
|
|
37
|
+
"line": <number>,
|
|
38
|
+
"dataFlow": {
|
|
39
|
+
"steps": [{"file": "path", "line": <number>, "description": "what happens", "code": "snippet"}]
|
|
40
|
+
},
|
|
41
|
+
"suggestedFix": "how to fix"
|
|
42
|
+
}]
|
|
43
|
+
}`;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Parse the LLM's JSON response into AuditFinding objects.
|
|
47
|
+
*/
|
|
48
|
+
export function parseLlmResponse(response, filePath) {
|
|
49
|
+
try {
|
|
50
|
+
// Extract JSON from response (may contain markdown wrapping)
|
|
51
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
52
|
+
if (!jsonMatch)
|
|
53
|
+
return [];
|
|
54
|
+
const data = JSON.parse(jsonMatch[0]);
|
|
55
|
+
if (!data.findings || !Array.isArray(data.findings))
|
|
56
|
+
return [];
|
|
57
|
+
let counter = 0;
|
|
58
|
+
return data.findings.map((f) => {
|
|
59
|
+
counter++;
|
|
60
|
+
const finding = {
|
|
61
|
+
id: `LLM-${String(counter).padStart(3, "0")}`,
|
|
62
|
+
ruleId: f.ruleId || "LLM-UNKNOWN",
|
|
63
|
+
severity: f.severity || "medium",
|
|
64
|
+
confidence: f.confidence || "medium",
|
|
65
|
+
category: f.category || "unknown",
|
|
66
|
+
message: f.message || "",
|
|
67
|
+
filePath,
|
|
68
|
+
line: f.line || 1,
|
|
69
|
+
snippet: "",
|
|
70
|
+
source: "llm",
|
|
71
|
+
};
|
|
72
|
+
// Parse data flow if present
|
|
73
|
+
if (f.dataFlow && typeof f.dataFlow === "object") {
|
|
74
|
+
const df = f.dataFlow;
|
|
75
|
+
if (Array.isArray(df.steps)) {
|
|
76
|
+
finding.dataFlow = {
|
|
77
|
+
steps: df.steps.map((s) => ({
|
|
78
|
+
file: s.file || filePath,
|
|
79
|
+
line: s.line || 1,
|
|
80
|
+
description: s.description || "",
|
|
81
|
+
code: s.code || "",
|
|
82
|
+
})),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Parse suggested fix
|
|
87
|
+
if (f.suggestedFix && typeof f.suggestedFix === "string") {
|
|
88
|
+
finding.suggestedFix = f.suggestedFix;
|
|
89
|
+
}
|
|
90
|
+
return finding;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Run LLM analysis on flagged files.
|
|
99
|
+
*
|
|
100
|
+
* @param files - Files that had Tier 1 findings
|
|
101
|
+
* @param tier1Findings - All Tier 1 findings (to provide context)
|
|
102
|
+
* @param config - Audit configuration
|
|
103
|
+
* @returns Additional findings from LLM analysis
|
|
104
|
+
*/
|
|
105
|
+
export async function runLlmAnalysis(files, tier1Findings, config) {
|
|
106
|
+
if (config.tier1Only || files.length === 0)
|
|
107
|
+
return [];
|
|
108
|
+
// Group Tier 1 findings by file
|
|
109
|
+
const findingsByFile = new Map();
|
|
110
|
+
for (const f of tier1Findings) {
|
|
111
|
+
const group = findingsByFile.get(f.filePath) || [];
|
|
112
|
+
group.push(f);
|
|
113
|
+
findingsByFile.set(f.filePath, group);
|
|
114
|
+
}
|
|
115
|
+
const allLlmFindings = [];
|
|
116
|
+
// Process files with concurrency limit
|
|
117
|
+
const concurrency = config.llmConcurrency;
|
|
118
|
+
for (let i = 0; i < files.length; i += concurrency) {
|
|
119
|
+
const batch = files.slice(i, i + concurrency);
|
|
120
|
+
const promises = batch.map(async (file) => {
|
|
121
|
+
const fileFindings = findingsByFile.get(file.path) || [];
|
|
122
|
+
const prompt = buildLlmPrompt(file, fileFindings);
|
|
123
|
+
try {
|
|
124
|
+
const { stdout } = await execFileAsync(config.llmProvider || "claude", ["--print", prompt], { timeout: config.llmTimeout });
|
|
125
|
+
return parseLlmResponse(stdout, file.path);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Graceful fallback: LLM unavailable or timed out
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
const results = await Promise.all(promises);
|
|
133
|
+
for (const findings of results) {
|
|
134
|
+
allLlmFindings.push(...findings);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return allLlmFindings;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=audit-llm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-llm.js","sourceRoot":"","sources":["../../src/audit/audit-llm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { buildLlmPrompt, parseLlmResponse, runLlmAnalysis } from "./audit-llm.js";
|
|
3
|
+
import { createDefaultAuditConfig } from "./audit-types.js";
|
|
4
|
+
describe("audit-llm", () => {
|
|
5
|
+
describe("buildLlmPrompt", () => {
|
|
6
|
+
it("TC-040: builds correct prompt with file content and Tier 1 findings", () => {
|
|
7
|
+
const file = {
|
|
8
|
+
path: "src/handler.ts",
|
|
9
|
+
content: 'const msg = req.body.message;\nexec(`echo ${msg}`);',
|
|
10
|
+
sizeBytes: 50,
|
|
11
|
+
};
|
|
12
|
+
const findings = [
|
|
13
|
+
{
|
|
14
|
+
id: "AF-001", ruleId: "CI-001", severity: "critical", confidence: "high",
|
|
15
|
+
category: "command-injection", message: "exec() call detected",
|
|
16
|
+
filePath: "src/handler.ts", line: 2, snippet: "exec(`echo ${msg}`);",
|
|
17
|
+
source: "tier1",
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
const prompt = buildLlmPrompt(file, findings);
|
|
21
|
+
expect(prompt).toContain("src/handler.ts");
|
|
22
|
+
expect(prompt).toContain("exec(`echo ${msg}`)");
|
|
23
|
+
expect(prompt).toContain("CI-001");
|
|
24
|
+
expect(prompt).toContain("command-injection");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe("parseLlmResponse", () => {
|
|
28
|
+
it("TC-041: parses valid LLM JSON response", () => {
|
|
29
|
+
const response = JSON.stringify({
|
|
30
|
+
findings: [
|
|
31
|
+
{
|
|
32
|
+
ruleId: "LLM-CI-001",
|
|
33
|
+
severity: "critical",
|
|
34
|
+
confidence: "high",
|
|
35
|
+
category: "command-injection",
|
|
36
|
+
message: "Command injection via webhook payload",
|
|
37
|
+
line: 2,
|
|
38
|
+
dataFlow: {
|
|
39
|
+
steps: [
|
|
40
|
+
{ file: "src/handler.ts", line: 1, description: "User input", code: "const msg = req.body.message;" },
|
|
41
|
+
{ file: "src/handler.ts", line: 2, description: "Shell exec", code: "exec(`echo ${msg}`);" },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
suggestedFix: "Use execFile with array args instead of exec with template",
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
const parsed = parseLlmResponse(response, "src/handler.ts");
|
|
49
|
+
expect(parsed).toHaveLength(1);
|
|
50
|
+
expect(parsed[0].ruleId).toBe("LLM-CI-001");
|
|
51
|
+
expect(parsed[0].source).toBe("llm");
|
|
52
|
+
expect(parsed[0].filePath).toBe("src/handler.ts");
|
|
53
|
+
});
|
|
54
|
+
it("TC-042: returns empty array on invalid JSON", () => {
|
|
55
|
+
const parsed = parseLlmResponse("not json at all", "src/file.ts");
|
|
56
|
+
expect(parsed).toHaveLength(0);
|
|
57
|
+
});
|
|
58
|
+
it("TC-045: data flow trace is parsed from LLM response", () => {
|
|
59
|
+
const response = JSON.stringify({
|
|
60
|
+
findings: [{
|
|
61
|
+
ruleId: "LLM-SSRF-001",
|
|
62
|
+
severity: "high",
|
|
63
|
+
confidence: "high",
|
|
64
|
+
category: "ssrf",
|
|
65
|
+
message: "SSRF via user URL",
|
|
66
|
+
line: 10,
|
|
67
|
+
dataFlow: {
|
|
68
|
+
steps: [
|
|
69
|
+
{ file: "src/api.ts", line: 5, description: "Input received", code: "const url = req.query.url;" },
|
|
70
|
+
{ file: "src/api.ts", line: 10, description: "Fetch called", code: "fetch(url);" },
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
}],
|
|
74
|
+
});
|
|
75
|
+
const parsed = parseLlmResponse(response, "src/api.ts");
|
|
76
|
+
expect(parsed[0].dataFlow).toBeDefined();
|
|
77
|
+
expect(parsed[0].dataFlow.steps).toHaveLength(2);
|
|
78
|
+
expect(parsed[0].dataFlow.steps[0].file).toBe("src/api.ts");
|
|
79
|
+
expect(parsed[0].dataFlow.steps[1].line).toBe(10);
|
|
80
|
+
});
|
|
81
|
+
it("TC-046: missing data flow is handled gracefully", () => {
|
|
82
|
+
const response = JSON.stringify({
|
|
83
|
+
findings: [{
|
|
84
|
+
ruleId: "LLM-XSS-001",
|
|
85
|
+
severity: "high",
|
|
86
|
+
confidence: "medium",
|
|
87
|
+
category: "xss",
|
|
88
|
+
message: "Potential XSS",
|
|
89
|
+
line: 5,
|
|
90
|
+
}],
|
|
91
|
+
});
|
|
92
|
+
const parsed = parseLlmResponse(response, "src/page.ts");
|
|
93
|
+
expect(parsed[0].dataFlow).toBeUndefined();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe("runLlmAnalysis", () => {
|
|
97
|
+
it("TC-043: skips LLM when tier1Only is true", async () => {
|
|
98
|
+
const config = createDefaultAuditConfig();
|
|
99
|
+
config.tier1Only = true;
|
|
100
|
+
const result = await runLlmAnalysis([], [], config);
|
|
101
|
+
expect(result).toHaveLength(0);
|
|
102
|
+
});
|
|
103
|
+
it("TC-044: returns empty when no flagged files", async () => {
|
|
104
|
+
const config = createDefaultAuditConfig();
|
|
105
|
+
const result = await runLlmAnalysis([], [], config);
|
|
106
|
+
expect(result).toHaveLength(0);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=audit-llm.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-llm.test.js","sourceRoot":"","sources":["../../src/audit/audit-llm.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAkB,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAqC,MAAM,kBAAkB,CAAC;AAE/F,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;YAC7E,MAAM,IAAI,GAAc;gBACtB,IAAI,EAAE,gBAAgB;gBACtB,OAAO,EAAE,qDAAqD;gBAC9D,SAAS,EAAE,EAAE;aACd,CAAC;YACF,MAAM,QAAQ,GAAmB;gBAC/B;oBACE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM;oBACxE,QAAQ,EAAE,mBAAmB,EAAE,OAAO,EAAE,sBAAsB;oBAC9D,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,sBAAsB;oBACpE,MAAM,EAAE,OAAO;iBAChB;aACF,CAAC;YAEF,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAE9C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;YAChD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;gBAC9B,QAAQ,EAAE;oBACR;wBACE,MAAM,EAAE,YAAY;wBACpB,QAAQ,EAAE,UAAU;wBACpB,UAAU,EAAE,MAAM;wBAClB,QAAQ,EAAE,mBAAmB;wBAC7B,OAAO,EAAE,uCAAuC;wBAChD,IAAI,EAAE,CAAC;wBACP,QAAQ,EAAE;4BACR,KAAK,EAAE;gCACL,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,EAAE,+BAA+B,EAAE;gCACrG,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,EAAE,sBAAsB,EAAE;6BAC7F;yBACF;wBACD,YAAY,EAAE,4DAA4D;qBAC3E;iBACF;aACF,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;YAE5D,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,MAAM,MAAM,GAAG,gBAAgB,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;YAClE,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;YAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;gBAC9B,QAAQ,EAAE,CAAC;wBACT,MAAM,EAAE,cAAc;wBACtB,QAAQ,EAAE,MAAM;wBAChB,UAAU,EAAE,MAAM;wBAClB,QAAQ,EAAE,MAAM;wBAChB,OAAO,EAAE,mBAAmB;wBAC5B,IAAI,EAAE,EAAE;wBACR,QAAQ,EAAE;4BACR,KAAK,EAAE;gCACL,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,IAAI,EAAE,4BAA4B,EAAE;gCAClG,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE;6BACnF;yBACF;qBACF,CAAC;aACH,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YAExD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;YACzC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAS,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAClD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC7D,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;YACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;gBAC9B,QAAQ,EAAE,CAAC;wBACT,MAAM,EAAE,aAAa;wBACrB,QAAQ,EAAE,MAAM;wBAChB,UAAU,EAAE,QAAQ;wBACpB,QAAQ,EAAE,KAAK;wBACf,OAAO,EAAE,eAAe;wBACxB,IAAI,EAAE,CAAC;qBACR,CAAC;aACH,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;YAEzD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,aAAa,EAAE,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,MAAM,MAAM,GAAG,wBAAwB,EAAE,CAAC;YAC1C,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;YAExB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;YAEpD,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC3D,MAAM,MAAM,GAAG,wBAAwB,EAAE,CAAC;YAC1C,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;YACpD,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended audit patterns for project-level security scanning.
|
|
3
|
+
*
|
|
4
|
+
* Imports the existing 37 SCAN_PATTERNS from the scanner module
|
|
5
|
+
* and extends them with project-audit-specific patterns for
|
|
6
|
+
* SQL injection, SSRF, XSS, hardcoded secrets, etc.
|
|
7
|
+
*/
|
|
8
|
+
import type { PatternCheck } from "../scanner/types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Extended pattern with safe-context awareness and confidence.
|
|
11
|
+
*/
|
|
12
|
+
export interface AuditPatternCheck extends PatternCheck {
|
|
13
|
+
/** Pattern ID (unique across all patterns) */
|
|
14
|
+
id: string;
|
|
15
|
+
/** Human-readable name */
|
|
16
|
+
name: string;
|
|
17
|
+
/** Regexes that suppress the finding when they match the line */
|
|
18
|
+
safeContexts?: RegExp[];
|
|
19
|
+
/** Default confidence for this pattern */
|
|
20
|
+
confidence: "high" | "medium" | "low";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Project-specific security patterns (beyond the existing 37 skill patterns).
|
|
24
|
+
*/
|
|
25
|
+
export declare const PROJECT_PATTERNS: AuditPatternCheck[];
|
|
26
|
+
/**
|
|
27
|
+
* Combined audit patterns: original SCAN_PATTERNS + project-specific patterns.
|
|
28
|
+
*
|
|
29
|
+
* The SCAN_PATTERNS are adapted to AuditPatternCheck interface (id from ScanPattern.id).
|
|
30
|
+
*/
|
|
31
|
+
export declare const AUDIT_PATTERNS: AuditPatternCheck[];
|