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,120 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { discoverAuditFiles } from "./file-discovery.js";
|
|
6
|
+
import { createDefaultAuditConfig } from "./audit-types.js";
|
|
7
|
+
describe("file-discovery", () => {
|
|
8
|
+
let tmpDir;
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
tmpDir = await mkdtemp(join(tmpdir(), "vskill-audit-test-"));
|
|
11
|
+
});
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
14
|
+
});
|
|
15
|
+
it("TC-003: discovers .ts, .js, .py files in a directory tree", async () => {
|
|
16
|
+
await mkdir(join(tmpDir, "src"), { recursive: true });
|
|
17
|
+
await writeFile(join(tmpDir, "src", "app.ts"), "const x = 1;");
|
|
18
|
+
await writeFile(join(tmpDir, "src", "utils.js"), "module.exports = {};");
|
|
19
|
+
await writeFile(join(tmpDir, "script.py"), "print('hello')");
|
|
20
|
+
// Non-scannable file
|
|
21
|
+
await writeFile(join(tmpDir, "image.png"), Buffer.from([0x89, 0x50, 0x4e, 0x47]));
|
|
22
|
+
const config = createDefaultAuditConfig();
|
|
23
|
+
const files = await discoverAuditFiles(tmpDir, config);
|
|
24
|
+
const paths = files.map((f) => f.path).sort();
|
|
25
|
+
expect(paths).toContain("src/app.ts");
|
|
26
|
+
expect(paths).toContain("src/utils.js");
|
|
27
|
+
expect(paths).toContain("script.py");
|
|
28
|
+
expect(paths).not.toContain("image.png");
|
|
29
|
+
});
|
|
30
|
+
it("TC-004: skips node_modules and .git directories", async () => {
|
|
31
|
+
await mkdir(join(tmpDir, "src"), { recursive: true });
|
|
32
|
+
await mkdir(join(tmpDir, "node_modules", "pkg"), { recursive: true });
|
|
33
|
+
await mkdir(join(tmpDir, ".git", "objects"), { recursive: true });
|
|
34
|
+
await writeFile(join(tmpDir, "src", "app.ts"), "const x = 1;");
|
|
35
|
+
await writeFile(join(tmpDir, "node_modules", "pkg", "index.js"), "bad");
|
|
36
|
+
await writeFile(join(tmpDir, ".git", "objects", "data.js"), "bad");
|
|
37
|
+
const config = createDefaultAuditConfig();
|
|
38
|
+
const files = await discoverAuditFiles(tmpDir, config);
|
|
39
|
+
const paths = files.map((f) => f.path);
|
|
40
|
+
expect(paths).toContain("src/app.ts");
|
|
41
|
+
expect(paths).not.toContain("node_modules/pkg/index.js");
|
|
42
|
+
expect(paths).not.toContain(".git/objects/data.js");
|
|
43
|
+
});
|
|
44
|
+
it("TC-005: scans a single file when path points to a file", async () => {
|
|
45
|
+
const filePath = join(tmpDir, "app.ts");
|
|
46
|
+
await writeFile(filePath, "const x = 1;");
|
|
47
|
+
const config = createDefaultAuditConfig();
|
|
48
|
+
const files = await discoverAuditFiles(filePath, config);
|
|
49
|
+
expect(files).toHaveLength(1);
|
|
50
|
+
expect(files[0].path).toBe("app.ts");
|
|
51
|
+
expect(files[0].content).toBe("const x = 1;");
|
|
52
|
+
});
|
|
53
|
+
it("TC-006: respects maxFiles limit", async () => {
|
|
54
|
+
await mkdir(join(tmpDir, "src"), { recursive: true });
|
|
55
|
+
for (let i = 0; i < 10; i++) {
|
|
56
|
+
await writeFile(join(tmpDir, "src", `file${i}.ts`), `const x = ${i};`);
|
|
57
|
+
}
|
|
58
|
+
const config = createDefaultAuditConfig();
|
|
59
|
+
config.maxFiles = 5;
|
|
60
|
+
const files = await discoverAuditFiles(tmpDir, config);
|
|
61
|
+
expect(files.length).toBeLessThanOrEqual(5);
|
|
62
|
+
});
|
|
63
|
+
it("TC-007: skips binary files", async () => {
|
|
64
|
+
await writeFile(join(tmpDir, "text.ts"), "const x = 1;");
|
|
65
|
+
// Create a file with null bytes (binary indicator)
|
|
66
|
+
const binaryContent = Buffer.alloc(100);
|
|
67
|
+
binaryContent[50] = 0; // null byte
|
|
68
|
+
binaryContent.write("const y = 2;", 0);
|
|
69
|
+
await writeFile(join(tmpDir, "binary.ts"), binaryContent);
|
|
70
|
+
const config = createDefaultAuditConfig();
|
|
71
|
+
const files = await discoverAuditFiles(tmpDir, config);
|
|
72
|
+
const paths = files.map((f) => f.path);
|
|
73
|
+
expect(paths).toContain("text.ts");
|
|
74
|
+
expect(paths).not.toContain("binary.ts");
|
|
75
|
+
});
|
|
76
|
+
it("TC-008: respects exclude patterns", async () => {
|
|
77
|
+
await mkdir(join(tmpDir, "src"), { recursive: true });
|
|
78
|
+
await mkdir(join(tmpDir, "test"), { recursive: true });
|
|
79
|
+
await writeFile(join(tmpDir, "src", "app.ts"), "const x = 1;");
|
|
80
|
+
await writeFile(join(tmpDir, "test", "app.test.ts"), "test code");
|
|
81
|
+
const config = createDefaultAuditConfig();
|
|
82
|
+
config.excludePaths = ["**/test/**"];
|
|
83
|
+
const files = await discoverAuditFiles(tmpDir, config);
|
|
84
|
+
const paths = files.map((f) => f.path);
|
|
85
|
+
expect(paths).toContain("src/app.ts");
|
|
86
|
+
expect(paths).not.toContain("test/app.test.ts");
|
|
87
|
+
});
|
|
88
|
+
it("skips dist, build, coverage, .next directories", async () => {
|
|
89
|
+
await mkdir(join(tmpDir, "src"), { recursive: true });
|
|
90
|
+
await mkdir(join(tmpDir, "dist"), { recursive: true });
|
|
91
|
+
await mkdir(join(tmpDir, "build"), { recursive: true });
|
|
92
|
+
await mkdir(join(tmpDir, "coverage"), { recursive: true });
|
|
93
|
+
await mkdir(join(tmpDir, ".next"), { recursive: true });
|
|
94
|
+
await writeFile(join(tmpDir, "src", "app.ts"), "const x = 1;");
|
|
95
|
+
await writeFile(join(tmpDir, "dist", "app.js"), "compiled");
|
|
96
|
+
await writeFile(join(tmpDir, "build", "app.js"), "compiled");
|
|
97
|
+
await writeFile(join(tmpDir, "coverage", "lcov.js"), "data");
|
|
98
|
+
await writeFile(join(tmpDir, ".next", "server.js"), "data");
|
|
99
|
+
const config = createDefaultAuditConfig();
|
|
100
|
+
const files = await discoverAuditFiles(tmpDir, config);
|
|
101
|
+
const paths = files.map((f) => f.path);
|
|
102
|
+
expect(paths).toContain("src/app.ts");
|
|
103
|
+
expect(paths).not.toContain("dist/app.js");
|
|
104
|
+
expect(paths).not.toContain("build/app.js");
|
|
105
|
+
expect(paths).not.toContain("coverage/lcov.js");
|
|
106
|
+
expect(paths).not.toContain(".next/server.js");
|
|
107
|
+
});
|
|
108
|
+
it("respects maxFileSize limit", async () => {
|
|
109
|
+
await writeFile(join(tmpDir, "small.ts"), "x");
|
|
110
|
+
// Create a file larger than default maxFileSize
|
|
111
|
+
const largeContent = "x".repeat(200 * 1024);
|
|
112
|
+
await writeFile(join(tmpDir, "large.ts"), largeContent);
|
|
113
|
+
const config = createDefaultAuditConfig();
|
|
114
|
+
const files = await discoverAuditFiles(tmpDir, config);
|
|
115
|
+
const paths = files.map((f) => f.path);
|
|
116
|
+
expect(paths).toContain("small.ts");
|
|
117
|
+
expect(paths).not.toContain("large.ts");
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
//# sourceMappingURL=file-discovery.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-discovery.test.js","sourceRoot":"","sources":["../../src/audit/file-discovery.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fix suggestions for audit findings.
|
|
3
|
+
*
|
|
4
|
+
* Maps pattern IDs to human-readable remediation advice.
|
|
5
|
+
*/
|
|
6
|
+
import type { AuditFinding } from "./audit-types.js";
|
|
7
|
+
/** Fix suggestion lookup by pattern ID */
|
|
8
|
+
export declare const FIX_SUGGESTIONS: Record<string, string>;
|
|
9
|
+
/**
|
|
10
|
+
* Attach fix suggestions to findings based on their ruleId.
|
|
11
|
+
* Only attaches when fix=true.
|
|
12
|
+
*/
|
|
13
|
+
export declare function attachFixSuggestions(findings: AuditFinding[], fix: boolean): AuditFinding[];
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fix suggestions for audit findings.
|
|
3
|
+
*
|
|
4
|
+
* Maps pattern IDs to human-readable remediation advice.
|
|
5
|
+
*/
|
|
6
|
+
/** Fix suggestion lookup by pattern ID */
|
|
7
|
+
export const FIX_SUGGESTIONS = {
|
|
8
|
+
// --- Original SCAN_PATTERNS (37) ---
|
|
9
|
+
"CI-001": "Replace exec() with execFile() or spawn() with explicit argument arrays to prevent command injection",
|
|
10
|
+
"CI-002": "Ensure spawn() arguments are not user-controlled; use argument arrays instead of shell strings",
|
|
11
|
+
"CI-003": "Replace system() with safer subprocess APIs that don't invoke a shell",
|
|
12
|
+
"CI-004": "Avoid embedding shell paths in strings; use Node.js APIs or explicit command arrays",
|
|
13
|
+
"CI-005": "Use execFile/spawn instead of execSync/spawnSync when possible; never pass unsanitized input",
|
|
14
|
+
"CI-006": "Avoid shell pipe operators in exec calls; use separate process pipelines",
|
|
15
|
+
"CI-007": "Never interpolate user input into exec() template strings; use execFile with argument arrays",
|
|
16
|
+
"DE-001": "Validate and allowlist URLs before making fetch requests; avoid dynamic URL construction",
|
|
17
|
+
"DE-002": "Replace XMLHttpRequest with fetch() and apply URL allowlisting",
|
|
18
|
+
"DE-003": "Validate WebSocket connection URLs against an allowlist",
|
|
19
|
+
"DE-004": "Avoid encoding data into DNS lookups; review data flow for exfiltration",
|
|
20
|
+
"DE-005": "Review base64 encoding patterns for data exfiltration; ensure data isn't being sent externally",
|
|
21
|
+
"PE-001": "Remove sudo invocations; run the required commands with appropriate user permissions",
|
|
22
|
+
"PE-002": "Avoid chmod in application code; set permissions during deployment instead",
|
|
23
|
+
"PE-003": "Avoid chown in application code; configure ownership during deployment",
|
|
24
|
+
"PE-004": "Never use setuid/setgid in application code; run with least privilege",
|
|
25
|
+
"PE-005": "Remove process.setuid/setgid calls; use proper OS-level privilege management",
|
|
26
|
+
"CT-001": "Use environment variables or a secrets manager instead of reading .env files at runtime",
|
|
27
|
+
"CT-002": "Never access SSH keys from application code; use a key management service",
|
|
28
|
+
"CT-003": "Use IAM roles or a secrets manager instead of directly accessing AWS credential files",
|
|
29
|
+
"CT-004": "Never access the system keychain from application code; use a proper secrets API",
|
|
30
|
+
"CT-005": "Access environment variables by specific name, not dynamically; use a config loader",
|
|
31
|
+
"CT-006": "Never hardcode secrets; use environment variables or a secrets manager",
|
|
32
|
+
"PI-001": "Never allow user input to override system prompts; use input validation and sandboxing",
|
|
33
|
+
"PI-002": "Filter and validate all input before passing to AI models; implement prompt guardrails",
|
|
34
|
+
"PI-003": "Validate AI model inputs to prevent role impersonation attacks",
|
|
35
|
+
"PI-004": "Sanitize input to remove instruction boundary markers; use structured prompting",
|
|
36
|
+
"FS-001": "Avoid recursive delete operations; use targeted file removal with path validation",
|
|
37
|
+
"FS-002": "Never write to system paths from application code; use application-specific directories",
|
|
38
|
+
"FS-003": "Validate and normalize file paths; reject paths containing '..'",
|
|
39
|
+
"FS-004": "Validate symlink targets before creation; use realpath() to resolve paths",
|
|
40
|
+
"NA-001": "Validate and allowlist URLs for curl/wget; use application-level HTTP clients instead",
|
|
41
|
+
"NA-002": "Remove reverse shell patterns; this is a critical security vulnerability",
|
|
42
|
+
"NA-003": "Avoid constructing URLs from variables; use allowlisted base URLs with parameterized paths",
|
|
43
|
+
"CE-001": "Replace eval() with JSON.parse() for data or Function constructor for code; never eval user input",
|
|
44
|
+
"CE-002": "Avoid new Function(); use explicit function definitions instead",
|
|
45
|
+
"CE-003": "Avoid dynamic imports with user-controlled paths; use a module allowlist",
|
|
46
|
+
// --- Project-specific AUDIT_PATTERNS ---
|
|
47
|
+
"SQLI-001": "Use parameterized queries or prepared statements instead of string concatenation in SQL",
|
|
48
|
+
"SQLI-002": "Use parameterized queries; never use template literals for SQL with user input",
|
|
49
|
+
"SQLI-003": "Use query parameters (?) or named parameters (:name) instead of string interpolation",
|
|
50
|
+
"SSRF-001": "Validate URLs against an allowlist of trusted domains before making requests",
|
|
51
|
+
"SSRF-002": "Never pass request parameters directly to HTTP clients; validate and sanitize URLs",
|
|
52
|
+
"BAC-001": "Add authentication middleware to all non-public route handlers",
|
|
53
|
+
"BAC-002": "Verify resource ownership by comparing request user ID with resource owner ID",
|
|
54
|
+
"HS-001": "Move API keys to environment variables or a secrets manager; never hardcode in source",
|
|
55
|
+
"HS-002": "Remove private keys from source code; use a key management service or environment variables",
|
|
56
|
+
"HS-003": "Move passwords to environment variables or a secrets manager; use bcrypt/argon2 for hashing",
|
|
57
|
+
"HS-004": "Remove cloud credentials from source code; use IAM roles or environment-based configuration",
|
|
58
|
+
"XSS-001": "Use textContent instead of innerHTML; if HTML is needed, sanitize with DOMPurify",
|
|
59
|
+
"XSS-002": "Ensure content passed to dangerouslySetInnerHTML is sanitized with DOMPurify or similar",
|
|
60
|
+
"XSS-003": "Replace document.write() with DOM manipulation methods (createElement, appendChild)",
|
|
61
|
+
"OR-001": "Validate redirect URLs against an allowlist of trusted paths/domains",
|
|
62
|
+
"OR-002": "Only set Location header from a predefined list of valid redirect targets",
|
|
63
|
+
"ID-001": "Use yaml.safeLoad() or yaml.load() with SAFE_SCHEMA instead of yaml.load()",
|
|
64
|
+
"ID-002": "Replace pickle with JSON or another safe serialization format; never deserialize untrusted data",
|
|
65
|
+
"ID-003": "Validate parsed JSON against a schema (Zod, Joi, or ajv) before using it",
|
|
66
|
+
"WC-001": "Replace MD5 with SHA-256 or SHA-3 for cryptographic hashing",
|
|
67
|
+
"WC-002": "Replace SHA-1 with SHA-256 or SHA-3; SHA-1 is deprecated for security use",
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Attach fix suggestions to findings based on their ruleId.
|
|
71
|
+
* Only attaches when fix=true.
|
|
72
|
+
*/
|
|
73
|
+
export function attachFixSuggestions(findings, fix) {
|
|
74
|
+
if (!fix)
|
|
75
|
+
return findings;
|
|
76
|
+
return findings.map((f) => {
|
|
77
|
+
const suggestion = FIX_SUGGESTIONS[f.ruleId];
|
|
78
|
+
if (suggestion) {
|
|
79
|
+
return { ...f, suggestedFix: suggestion };
|
|
80
|
+
}
|
|
81
|
+
return f;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=fix-suggestions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-suggestions.js","sourceRoot":"","sources":["../../src/audit/fix-suggestions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,0CAA0C;AAC1C,MAAM,CAAC,MAAM,eAAe,GAA2B;IACrD,sCAAsC;IACtC,QAAQ,EAAE,sGAAsG;IAChH,QAAQ,EAAE,gGAAgG;IAC1G,QAAQ,EAAE,uEAAuE;IACjF,QAAQ,EAAE,qFAAqF;IAC/F,QAAQ,EAAE,8FAA8F;IACxG,QAAQ,EAAE,0EAA0E;IACpF,QAAQ,EAAE,8FAA8F;IACxG,QAAQ,EAAE,0FAA0F;IACpG,QAAQ,EAAE,gEAAgE;IAC1E,QAAQ,EAAE,yDAAyD;IACnE,QAAQ,EAAE,yEAAyE;IACnF,QAAQ,EAAE,gGAAgG;IAC1G,QAAQ,EAAE,sFAAsF;IAChG,QAAQ,EAAE,4EAA4E;IACtF,QAAQ,EAAE,wEAAwE;IAClF,QAAQ,EAAE,uEAAuE;IACjF,QAAQ,EAAE,8EAA8E;IACxF,QAAQ,EAAE,yFAAyF;IACnG,QAAQ,EAAE,2EAA2E;IACrF,QAAQ,EAAE,uFAAuF;IACjG,QAAQ,EAAE,kFAAkF;IAC5F,QAAQ,EAAE,qFAAqF;IAC/F,QAAQ,EAAE,wEAAwE;IAClF,QAAQ,EAAE,wFAAwF;IAClG,QAAQ,EAAE,wFAAwF;IAClG,QAAQ,EAAE,gEAAgE;IAC1E,QAAQ,EAAE,iFAAiF;IAC3F,QAAQ,EAAE,mFAAmF;IAC7F,QAAQ,EAAE,yFAAyF;IACnG,QAAQ,EAAE,iEAAiE;IAC3E,QAAQ,EAAE,2EAA2E;IACrF,QAAQ,EAAE,uFAAuF;IACjG,QAAQ,EAAE,0EAA0E;IACpF,QAAQ,EAAE,4FAA4F;IACtG,QAAQ,EAAE,mGAAmG;IAC7G,QAAQ,EAAE,iEAAiE;IAC3E,QAAQ,EAAE,0EAA0E;IAEpF,0CAA0C;IAC1C,UAAU,EAAE,yFAAyF;IACrG,UAAU,EAAE,gFAAgF;IAC5F,UAAU,EAAE,sFAAsF;IAClG,UAAU,EAAE,8EAA8E;IAC1F,UAAU,EAAE,oFAAoF;IAChG,SAAS,EAAE,gEAAgE;IAC3E,SAAS,EAAE,+EAA+E;IAC1F,QAAQ,EAAE,uFAAuF;IACjG,QAAQ,EAAE,6FAA6F;IACvG,QAAQ,EAAE,6FAA6F;IACvG,QAAQ,EAAE,6FAA6F;IACvG,SAAS,EAAE,kFAAkF;IAC7F,SAAS,EAAE,yFAAyF;IACpG,SAAS,EAAE,qFAAqF;IAChG,QAAQ,EAAE,sEAAsE;IAChF,QAAQ,EAAE,2EAA2E;IACrF,QAAQ,EAAE,4EAA4E;IACtF,QAAQ,EAAE,iGAAiG;IAC3G,QAAQ,EAAE,0EAA0E;IACpF,QAAQ,EAAE,6DAA6D;IACvE,QAAQ,EAAE,2EAA2E;CACtF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAwB,EACxB,GAAY;IAEZ,IAAI,CAAC,GAAG;QAAE,OAAO,QAAQ,CAAC;IAE1B,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACxB,MAAM,UAAU,GAAG,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC7C,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,EAAE,GAAG,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;QAC5C,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { FIX_SUGGESTIONS, attachFixSuggestions } from "./fix-suggestions.js";
|
|
3
|
+
import { AUDIT_PATTERNS } from "./audit-patterns.js";
|
|
4
|
+
describe("fix-suggestions", () => {
|
|
5
|
+
it("TC-047: every audit pattern ID has a fix suggestion", () => {
|
|
6
|
+
for (const pattern of AUDIT_PATTERNS) {
|
|
7
|
+
expect(FIX_SUGGESTIONS[pattern.id], `Missing fix suggestion for pattern ${pattern.id}`).toBeDefined();
|
|
8
|
+
expect(FIX_SUGGESTIONS[pattern.id].length).toBeGreaterThan(0);
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
it("TC-048: fix suggestion is attached to finding when fix=true", () => {
|
|
12
|
+
const findings = [
|
|
13
|
+
{
|
|
14
|
+
id: "AF-001", ruleId: "CI-001", severity: "critical", confidence: "high",
|
|
15
|
+
category: "command-injection", message: "exec() call",
|
|
16
|
+
filePath: "a.ts", line: 1, snippet: "code", source: "tier1",
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
const result = attachFixSuggestions(findings, true);
|
|
20
|
+
expect(result[0].suggestedFix).toBeDefined();
|
|
21
|
+
expect(result[0].suggestedFix.length).toBeGreaterThan(0);
|
|
22
|
+
});
|
|
23
|
+
it("TC-049: fix suggestion is absent when fix=false", () => {
|
|
24
|
+
const findings = [
|
|
25
|
+
{
|
|
26
|
+
id: "AF-001", ruleId: "CI-001", severity: "critical", confidence: "high",
|
|
27
|
+
category: "command-injection", message: "exec() call",
|
|
28
|
+
filePath: "a.ts", line: 1, snippet: "code", source: "tier1",
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
const result = attachFixSuggestions(findings, false);
|
|
32
|
+
expect(result[0].suggestedFix).toBeUndefined();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=fix-suggestions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-suggestions.test.js","sourceRoot":"","sources":["../../src/audit/fix-suggestions.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;YACrC,MAAM,CACJ,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC,EAC3B,sCAAsC,OAAO,CAAC,EAAE,EAAE,CACnD,CAAC,WAAW,EAAE,CAAC;YAChB,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,QAAQ,GAAmB;YAC/B;gBACE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM;gBACxE,QAAQ,EAAE,mBAAmB,EAAE,OAAO,EAAE,aAAa;gBACrD,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;aAC5D;SACF,CAAC;QAEF,MAAM,MAAM,GAAG,oBAAoB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAEpD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAa,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,QAAQ,GAAmB;YAC/B;gBACE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM;gBACxE,QAAQ,EAAE,mBAAmB,EAAE,OAAO,EAAE,aAAa;gBACrD,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;aAC5D;SACF,CAAC;QAEF,MAAM,MAAM,GAAG,oBAAoB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAErD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,aAAa,EAAE,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-formatter.js","sourceRoot":"","sources":["../../../src/audit/formatters/json-formatter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,MAAmB;IAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { formatJson } from "./json-formatter.js";
|
|
3
|
+
import { createDefaultAuditConfig } from "../audit-types.js";
|
|
4
|
+
function makeResult(overrides = {}) {
|
|
5
|
+
return {
|
|
6
|
+
rootPath: "/project",
|
|
7
|
+
startedAt: "2026-02-20T18:00:00Z",
|
|
8
|
+
completedAt: "2026-02-20T18:00:01Z",
|
|
9
|
+
durationMs: 1000,
|
|
10
|
+
filesScanned: 10,
|
|
11
|
+
filesWithFindings: 0,
|
|
12
|
+
findings: [],
|
|
13
|
+
summary: {
|
|
14
|
+
critical: 0, high: 0, medium: 0, low: 0, info: 0,
|
|
15
|
+
total: 0, score: 100, verdict: "PASS",
|
|
16
|
+
},
|
|
17
|
+
config: createDefaultAuditConfig(),
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
describe("json-formatter", () => {
|
|
22
|
+
it("TC-030: output is valid JSON", () => {
|
|
23
|
+
const output = formatJson(makeResult());
|
|
24
|
+
expect(() => JSON.parse(output)).not.toThrow();
|
|
25
|
+
});
|
|
26
|
+
it("TC-031: all findings are present in output", () => {
|
|
27
|
+
const findings = Array.from({ length: 5 }, (_, i) => ({
|
|
28
|
+
id: `AF-${i}`, ruleId: `R-${i}`, severity: "high", confidence: "high",
|
|
29
|
+
category: "test", message: `msg ${i}`, filePath: `f${i}.ts`, line: i + 1,
|
|
30
|
+
snippet: "code", source: "tier1",
|
|
31
|
+
}));
|
|
32
|
+
const output = formatJson(makeResult({ findings, summary: { critical: 0, high: 5, medium: 0, low: 0, info: 0, total: 5, score: 25, verdict: "FAIL" } }));
|
|
33
|
+
const parsed = JSON.parse(output);
|
|
34
|
+
expect(parsed.findings).toHaveLength(5);
|
|
35
|
+
});
|
|
36
|
+
it("TC-032: summary statistics are included", () => {
|
|
37
|
+
const output = formatJson(makeResult({
|
|
38
|
+
summary: { critical: 1, high: 2, medium: 3, low: 4, info: 5, total: 15, score: 50, verdict: "CONCERNS" },
|
|
39
|
+
}));
|
|
40
|
+
const parsed = JSON.parse(output);
|
|
41
|
+
expect(parsed.summary.critical).toBe(1);
|
|
42
|
+
expect(parsed.summary.high).toBe(2);
|
|
43
|
+
expect(parsed.summary.medium).toBe(3);
|
|
44
|
+
expect(parsed.summary.low).toBe(4);
|
|
45
|
+
expect(parsed.summary.info).toBe(5);
|
|
46
|
+
expect(parsed.summary.total).toBe(15);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=json-formatter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-formatter.test.js","sourceRoot":"","sources":["../../../src/audit/formatters/json-formatter.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown report formatter — generates a structured security audit report.
|
|
3
|
+
*/
|
|
4
|
+
import type { AuditResult } from "../audit-types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Format an AuditResult as a markdown report.
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatReport(result: AuditResult): string;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown report formatter — generates a structured security audit report.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Format an AuditResult as a markdown report.
|
|
6
|
+
*/
|
|
7
|
+
export function formatReport(result) {
|
|
8
|
+
const lines = [];
|
|
9
|
+
lines.push("# Security Audit Report");
|
|
10
|
+
lines.push("");
|
|
11
|
+
lines.push(`**Generated**: ${result.completedAt}`);
|
|
12
|
+
lines.push(`**Root**: ${result.rootPath}`);
|
|
13
|
+
lines.push("");
|
|
14
|
+
// Executive Summary
|
|
15
|
+
lines.push("## Executive Summary");
|
|
16
|
+
lines.push("");
|
|
17
|
+
lines.push(`| Metric | Value |`);
|
|
18
|
+
lines.push(`|--------|-------|`);
|
|
19
|
+
lines.push(`| Score | ${result.summary.score}/100 |`);
|
|
20
|
+
lines.push(`| Verdict | ${result.summary.verdict} |`);
|
|
21
|
+
lines.push(`| Files Scanned | ${result.filesScanned} |`);
|
|
22
|
+
lines.push(`| Files with Issues | ${result.filesWithFindings} |`);
|
|
23
|
+
lines.push(`| Total Findings | ${result.summary.total} |`);
|
|
24
|
+
lines.push(`| Duration | ${result.durationMs}ms |`);
|
|
25
|
+
lines.push("");
|
|
26
|
+
// Severity breakdown
|
|
27
|
+
lines.push("### Severity Breakdown");
|
|
28
|
+
lines.push("");
|
|
29
|
+
lines.push("| Severity | Count |");
|
|
30
|
+
lines.push("|----------|-------|");
|
|
31
|
+
lines.push(`| Critical | ${result.summary.critical} |`);
|
|
32
|
+
lines.push(`| High | ${result.summary.high} |`);
|
|
33
|
+
lines.push(`| Medium | ${result.summary.medium} |`);
|
|
34
|
+
lines.push(`| Low | ${result.summary.low} |`);
|
|
35
|
+
lines.push(`| Info | ${result.summary.info} |`);
|
|
36
|
+
lines.push("");
|
|
37
|
+
if (result.findings.length === 0) {
|
|
38
|
+
lines.push("No security issues found.");
|
|
39
|
+
lines.push("");
|
|
40
|
+
return lines.join("\n");
|
|
41
|
+
}
|
|
42
|
+
// Findings
|
|
43
|
+
lines.push("## Findings");
|
|
44
|
+
lines.push("");
|
|
45
|
+
// Group by file
|
|
46
|
+
const byFile = new Map();
|
|
47
|
+
for (const f of result.findings) {
|
|
48
|
+
const group = byFile.get(f.filePath) || [];
|
|
49
|
+
group.push(f);
|
|
50
|
+
byFile.set(f.filePath, group);
|
|
51
|
+
}
|
|
52
|
+
for (const [filePath, findings] of byFile) {
|
|
53
|
+
lines.push(`### ${filePath}`);
|
|
54
|
+
lines.push("");
|
|
55
|
+
for (const f of findings) {
|
|
56
|
+
lines.push(`**${f.severity.toUpperCase()}** — ${f.message} (${f.ruleId})`);
|
|
57
|
+
lines.push(`- Line: ${f.line}`);
|
|
58
|
+
lines.push(`- Confidence: ${f.confidence}`);
|
|
59
|
+
if (f.snippet) {
|
|
60
|
+
lines.push("");
|
|
61
|
+
lines.push("```");
|
|
62
|
+
lines.push(f.snippet);
|
|
63
|
+
lines.push("```");
|
|
64
|
+
lines.push("");
|
|
65
|
+
}
|
|
66
|
+
if (f.suggestedFix) {
|
|
67
|
+
lines.push(`**Fix**: ${f.suggestedFix}`);
|
|
68
|
+
lines.push("");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Recommendations
|
|
73
|
+
lines.push("## Recommendations");
|
|
74
|
+
lines.push("");
|
|
75
|
+
if (result.summary.critical > 0) {
|
|
76
|
+
lines.push("- Address all critical findings immediately before deployment");
|
|
77
|
+
}
|
|
78
|
+
if (result.summary.high > 0) {
|
|
79
|
+
lines.push("- Review high-severity findings and apply fixes in the current sprint");
|
|
80
|
+
}
|
|
81
|
+
if (result.summary.medium > 0) {
|
|
82
|
+
lines.push("- Plan remediation of medium-severity findings");
|
|
83
|
+
}
|
|
84
|
+
lines.push("- Run `vskill audit --fix` for suggested remediations");
|
|
85
|
+
lines.push("- Consider enabling LLM analysis for deeper data flow tracing");
|
|
86
|
+
lines.push("");
|
|
87
|
+
// Scan metadata
|
|
88
|
+
lines.push("## Scan Metadata");
|
|
89
|
+
lines.push("");
|
|
90
|
+
lines.push(`- **Tool**: vskill audit`);
|
|
91
|
+
lines.push(`- **Tier 1 Only**: ${result.config.tier1Only}`);
|
|
92
|
+
lines.push(`- **Max Files**: ${result.config.maxFiles}`);
|
|
93
|
+
lines.push(`- **Exclude Paths**: ${result.config.excludePaths.join(", ") || "none"}`);
|
|
94
|
+
lines.push("");
|
|
95
|
+
return lines.join("\n");
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=report-formatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-formatter.js","sourceRoot":"","sources":["../../../src/audit/formatters/report-formatter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { formatReport } from "./report-formatter.js";
|
|
3
|
+
import { createDefaultAuditConfig } from "../audit-types.js";
|
|
4
|
+
function makeResult(overrides = {}) {
|
|
5
|
+
return {
|
|
6
|
+
rootPath: "/project",
|
|
7
|
+
startedAt: "2026-02-20T18:00:00Z",
|
|
8
|
+
completedAt: "2026-02-20T18:00:01Z",
|
|
9
|
+
durationMs: 1000,
|
|
10
|
+
filesScanned: 10,
|
|
11
|
+
filesWithFindings: 0,
|
|
12
|
+
findings: [],
|
|
13
|
+
summary: {
|
|
14
|
+
critical: 0, high: 0, medium: 0, low: 0, info: 0,
|
|
15
|
+
total: 0, score: 100, verdict: "PASS",
|
|
16
|
+
},
|
|
17
|
+
config: createDefaultAuditConfig(),
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
describe("report-formatter", () => {
|
|
22
|
+
it("TC-037: report contains all sections", () => {
|
|
23
|
+
const output = formatReport(makeResult({
|
|
24
|
+
findings: [
|
|
25
|
+
{ id: "AF-001", ruleId: "CI-001", severity: "critical", confidence: "high", category: "cmd", message: "exec", filePath: "a.ts", line: 1, snippet: "code", source: "tier1" },
|
|
26
|
+
],
|
|
27
|
+
summary: { critical: 1, high: 0, medium: 0, low: 0, info: 0, total: 1, score: 75, verdict: "CONCERNS" },
|
|
28
|
+
}));
|
|
29
|
+
expect(output).toContain("Executive Summary");
|
|
30
|
+
expect(output).toContain("Findings");
|
|
31
|
+
expect(output).toContain("Recommendations");
|
|
32
|
+
});
|
|
33
|
+
it("TC-038: code snippets are in fenced code blocks", () => {
|
|
34
|
+
const output = formatReport(makeResult({
|
|
35
|
+
findings: [
|
|
36
|
+
{ id: "AF-001", ruleId: "CI-001", severity: "critical", confidence: "high", category: "cmd", message: "exec", filePath: "a.ts", line: 1, snippet: "> 1 | exec(cmd);", source: "tier1" },
|
|
37
|
+
],
|
|
38
|
+
summary: { critical: 1, high: 0, medium: 0, low: 0, info: 0, total: 1, score: 75, verdict: "CONCERNS" },
|
|
39
|
+
}));
|
|
40
|
+
expect(output).toContain("```");
|
|
41
|
+
});
|
|
42
|
+
it("TC-039: summary table has correct counts", () => {
|
|
43
|
+
const output = formatReport(makeResult({
|
|
44
|
+
summary: { critical: 2, high: 3, medium: 1, low: 0, info: 0, total: 6, score: 32, verdict: "FAIL" },
|
|
45
|
+
}));
|
|
46
|
+
expect(output).toContain("2");
|
|
47
|
+
expect(output).toContain("3");
|
|
48
|
+
expect(output).toContain("1");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=report-formatter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-formatter.test.js","sourceRoot":"","sources":["../../../src/audit/formatters/report-formatter.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SARIF v2.1.0 formatter — generates SARIF-compliant JSON for CI tools.
|
|
3
|
+
*
|
|
4
|
+
* Enables integration with GitHub Code Scanning, VS Code SARIF Viewer,
|
|
5
|
+
* and other standard static analysis result consumers.
|
|
6
|
+
*/
|
|
7
|
+
import type { AuditResult } from "../audit-types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Format an AuditResult as SARIF v2.1.0 JSON string.
|
|
10
|
+
*/
|
|
11
|
+
export declare function formatSarif(result: AuditResult): string;
|