kuzushi 0.1.0
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 +410 -0
- package/dist/agent-runtime/claude.d.ts +8 -0
- package/dist/agent-runtime/claude.js +124 -0
- package/dist/agent-runtime/claude.js.map +1 -0
- package/dist/agent-runtime/index.d.ts +9 -0
- package/dist/agent-runtime/index.js +31 -0
- package/dist/agent-runtime/index.js.map +1 -0
- package/dist/agent-runtime/model-spec.d.ts +8 -0
- package/dist/agent-runtime/model-spec.js +17 -0
- package/dist/agent-runtime/model-spec.js.map +1 -0
- package/dist/agent-runtime/pi-ai.d.ts +8 -0
- package/dist/agent-runtime/pi-ai.js +365 -0
- package/dist/agent-runtime/pi-ai.js.map +1 -0
- package/dist/agent-runtime/tools.d.ts +3 -0
- package/dist/agent-runtime/tools.js +330 -0
- package/dist/agent-runtime/tools.js.map +1 -0
- package/dist/agent-runtime/types.d.ts +72 -0
- package/dist/agent-runtime/types.js +2 -0
- package/dist/agent-runtime/types.js.map +1 -0
- package/dist/agents/index.d.ts +7 -0
- package/dist/agents/index.js +24 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/registry.d.ts +14 -0
- package/dist/agents/registry.js +97 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/agents/scanner-adapter.d.ts +5 -0
- package/dist/agents/scanner-adapter.js +18 -0
- package/dist/agents/scanner-adapter.js.map +1 -0
- package/dist/agents/tasks/augur-analyze.d.ts +22 -0
- package/dist/agents/tasks/augur-analyze.js +418 -0
- package/dist/agents/tasks/augur-analyze.js.map +1 -0
- package/dist/agents/tasks/augur-extraction-agent.d.ts +44 -0
- package/dist/agents/tasks/augur-extraction-agent.js +507 -0
- package/dist/agents/tasks/augur-extraction-agent.js.map +1 -0
- package/dist/agents/tasks/augur-label.d.ts +21 -0
- package/dist/agents/tasks/augur-label.js +627 -0
- package/dist/agents/tasks/augur-label.js.map +1 -0
- package/dist/agents/tasks/augur-preflight.d.ts +36 -0
- package/dist/agents/tasks/augur-preflight.js +471 -0
- package/dist/agents/tasks/augur-preflight.js.map +1 -0
- package/dist/agents/tasks/augur-types.d.ts +111 -0
- package/dist/agents/tasks/augur-types.js +169 -0
- package/dist/agents/tasks/augur-types.js.map +1 -0
- package/dist/agents/tasks/context-gatherer.d.ts +6 -0
- package/dist/agents/tasks/context-gatherer.js +320 -0
- package/dist/agents/tasks/context-gatherer.js.map +1 -0
- package/dist/agents/types.d.ts +28 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/bus/adapters/google-pubsub.d.ts +13 -0
- package/dist/bus/adapters/google-pubsub.js +26 -0
- package/dist/bus/adapters/google-pubsub.js.map +1 -0
- package/dist/bus/adapters/in-process.d.ts +12 -0
- package/dist/bus/adapters/in-process.js +118 -0
- package/dist/bus/adapters/in-process.js.map +1 -0
- package/dist/bus/adapters/index.d.ts +6 -0
- package/dist/bus/adapters/index.js +22 -0
- package/dist/bus/adapters/index.js.map +1 -0
- package/dist/bus/adapters/nats.d.ts +13 -0
- package/dist/bus/adapters/nats.js +26 -0
- package/dist/bus/adapters/nats.js.map +1 -0
- package/dist/bus/adapters/redis.d.ts +13 -0
- package/dist/bus/adapters/redis.js +26 -0
- package/dist/bus/adapters/redis.js.map +1 -0
- package/dist/bus/events.d.ts +295 -0
- package/dist/bus/events.js +2 -0
- package/dist/bus/events.js.map +1 -0
- package/dist/bus/helpers.d.ts +7 -0
- package/dist/bus/helpers.js +16 -0
- package/dist/bus/helpers.js.map +1 -0
- package/dist/bus/index.d.ts +30 -0
- package/dist/bus/index.js +66 -0
- package/dist/bus/index.js.map +1 -0
- package/dist/bus/orchestrator.d.ts +51 -0
- package/dist/bus/orchestrator.js +1350 -0
- package/dist/bus/orchestrator.js.map +1 -0
- package/dist/bus/types.d.ts +23 -0
- package/dist/bus/types.js +2 -0
- package/dist/bus/types.js.map +1 -0
- package/dist/bus/workers/audit-worker.d.ts +9 -0
- package/dist/bus/workers/audit-worker.js +125 -0
- package/dist/bus/workers/audit-worker.js.map +1 -0
- package/dist/bus/workers/poc-harness-worker.d.ts +9 -0
- package/dist/bus/workers/poc-harness-worker.js +96 -0
- package/dist/bus/workers/poc-harness-worker.js.map +1 -0
- package/dist/bus/workers/report-worker.d.ts +11 -0
- package/dist/bus/workers/report-worker.js +235 -0
- package/dist/bus/workers/report-worker.js.map +1 -0
- package/dist/bus/workers/scan-worker.d.ts +13 -0
- package/dist/bus/workers/scan-worker.js +223 -0
- package/dist/bus/workers/scan-worker.js.map +1 -0
- package/dist/bus/workers/store-worker.d.ts +10 -0
- package/dist/bus/workers/store-worker.js +62 -0
- package/dist/bus/workers/store-worker.js.map +1 -0
- package/dist/bus/workers/triage-worker.d.ts +13 -0
- package/dist/bus/workers/triage-worker.js +129 -0
- package/dist/bus/workers/triage-worker.js.map +1 -0
- package/dist/bus/workers/verification-worker.d.ts +9 -0
- package/dist/bus/workers/verification-worker.js +91 -0
- package/dist/bus/workers/verification-worker.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +513 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +866 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +5 -0
- package/dist/context.js +26 -0
- package/dist/context.js.map +1 -0
- package/dist/deps.d.ts +15 -0
- package/dist/deps.js +18 -0
- package/dist/deps.js.map +1 -0
- package/dist/llm/anthropic.d.ts +3 -0
- package/dist/llm/anthropic.js +21 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/poc-harness.d.ts +49 -0
- package/dist/poc-harness.js +420 -0
- package/dist/poc-harness.js.map +1 -0
- package/dist/report-markdown.d.ts +3 -0
- package/dist/report-markdown.js +197 -0
- package/dist/report-markdown.js.map +1 -0
- package/dist/report-sarif.d.ts +3 -0
- package/dist/report-sarif.js +253 -0
- package/dist/report-sarif.js.map +1 -0
- package/dist/report.d.ts +18 -0
- package/dist/report.js +146 -0
- package/dist/report.js.map +1 -0
- package/dist/retry.d.ts +6 -0
- package/dist/retry.js +25 -0
- package/dist/retry.js.map +1 -0
- package/dist/scanner/claude-adk.d.ts +26 -0
- package/dist/scanner/claude-adk.js +265 -0
- package/dist/scanner/claude-adk.js.map +1 -0
- package/dist/scanner/resolve.d.ts +10 -0
- package/dist/scanner/resolve.js +99 -0
- package/dist/scanner/resolve.js.map +1 -0
- package/dist/scanner.d.ts +47 -0
- package/dist/scanner.js +123 -0
- package/dist/scanner.js.map +1 -0
- package/dist/scanners/agentic.d.ts +11 -0
- package/dist/scanners/agentic.js +71 -0
- package/dist/scanners/agentic.js.map +1 -0
- package/dist/scanners/claude-adk.d.ts +11 -0
- package/dist/scanners/claude-adk.js +74 -0
- package/dist/scanners/claude-adk.js.map +1 -0
- package/dist/scanners/codeql.d.ts +15 -0
- package/dist/scanners/codeql.js +97 -0
- package/dist/scanners/codeql.js.map +1 -0
- package/dist/scanners/finding-selection.d.ts +7 -0
- package/dist/scanners/finding-selection.js +120 -0
- package/dist/scanners/finding-selection.js.map +1 -0
- package/dist/scanners/index.d.ts +4 -0
- package/dist/scanners/index.js +14 -0
- package/dist/scanners/index.js.map +1 -0
- package/dist/scanners/registry.d.ts +12 -0
- package/dist/scanners/registry.js +70 -0
- package/dist/scanners/registry.js.map +1 -0
- package/dist/scanners/resolve-codeql.d.ts +9 -0
- package/dist/scanners/resolve-codeql.js +38 -0
- package/dist/scanners/resolve-codeql.js.map +1 -0
- package/dist/scanners/resolve-semgrep.d.ts +10 -0
- package/dist/scanners/resolve-semgrep.js +96 -0
- package/dist/scanners/resolve-semgrep.js.map +1 -0
- package/dist/scanners/run-agentic.d.ts +33 -0
- package/dist/scanners/run-agentic.js +267 -0
- package/dist/scanners/run-agentic.js.map +1 -0
- package/dist/scanners/run-claude-adk.d.ts +33 -0
- package/dist/scanners/run-claude-adk.js +267 -0
- package/dist/scanners/run-claude-adk.js.map +1 -0
- package/dist/scanners/run-codeql.d.ts +100 -0
- package/dist/scanners/run-codeql.js +538 -0
- package/dist/scanners/run-codeql.js.map +1 -0
- package/dist/scanners/run-semgrep.d.ts +41 -0
- package/dist/scanners/run-semgrep.js +115 -0
- package/dist/scanners/run-semgrep.js.map +1 -0
- package/dist/scanners/scoring.d.ts +7 -0
- package/dist/scanners/scoring.js +14 -0
- package/dist/scanners/scoring.js.map +1 -0
- package/dist/scanners/semgrep.d.ts +11 -0
- package/dist/scanners/semgrep.js +64 -0
- package/dist/scanners/semgrep.js.map +1 -0
- package/dist/scanners/types.d.ts +36 -0
- package/dist/scanners/types.js +2 -0
- package/dist/scanners/types.js.map +1 -0
- package/dist/scanners.d.ts +25 -0
- package/dist/scanners.js +88 -0
- package/dist/scanners.js.map +1 -0
- package/dist/store.d.ts +106 -0
- package/dist/store.js +609 -0
- package/dist/store.js.map +1 -0
- package/dist/triage.d.ts +74 -0
- package/dist/triage.js +314 -0
- package/dist/triage.js.map +1 -0
- package/dist/types.d.ts +166 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +6 -0
- package/dist/utils.js +19 -0
- package/dist/utils.js.map +1 -0
- package/dist/verify.d.ts +56 -0
- package/dist/verify.js +298 -0
- package/dist/verify.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { scoreFindingMetadata } from "./scoring.js";
|
|
4
|
+
/** Score a Semgrep finding by severity * likelihood * impact * subcategory. */
|
|
5
|
+
export function scoreSemgrepFinding(finding) {
|
|
6
|
+
const extra = finding.extra ?? {};
|
|
7
|
+
const meta = extra.metadata ?? {};
|
|
8
|
+
return scoreFindingMetadata({
|
|
9
|
+
severity: extra.severity,
|
|
10
|
+
likelihood: meta.likelihood,
|
|
11
|
+
impact: meta.impact,
|
|
12
|
+
subcategory: meta.subcategory,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/** Content-based fingerprint that survives line shifts. */
|
|
16
|
+
export function computeFingerprint(repoName, ref, finding) {
|
|
17
|
+
const extra = finding.extra ?? {};
|
|
18
|
+
let matched = (extra.lines ?? "").trim();
|
|
19
|
+
if (!matched || matched === "requires login") {
|
|
20
|
+
matched = String(finding.start?.line ?? "");
|
|
21
|
+
}
|
|
22
|
+
const source = `${repoName}|${ref}|${finding.check_id}|${finding.path}|${matched}|${extra.message ?? ""}`;
|
|
23
|
+
return createHash("sha256").update(source).digest("hex");
|
|
24
|
+
}
|
|
25
|
+
/** Run semgrep/opengrep and return parsed JSON output. */
|
|
26
|
+
export async function runSemgrep(repoRoot, opts, scannerBin = "semgrep", configFlag = "auto") {
|
|
27
|
+
const binName = scannerBin.split("/").pop() ?? "scanner";
|
|
28
|
+
const args = [
|
|
29
|
+
"--config", configFlag,
|
|
30
|
+
"--json",
|
|
31
|
+
"--metrics", "on",
|
|
32
|
+
];
|
|
33
|
+
for (const sev of opts.severity) {
|
|
34
|
+
args.push("--severity", sev);
|
|
35
|
+
}
|
|
36
|
+
for (const pattern of opts.excludePatterns) {
|
|
37
|
+
args.push("--exclude", pattern);
|
|
38
|
+
}
|
|
39
|
+
args.push(".");
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const proc = spawn(scannerBin, args, {
|
|
42
|
+
cwd: repoRoot,
|
|
43
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
44
|
+
});
|
|
45
|
+
const stdoutChunks = [];
|
|
46
|
+
proc.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
47
|
+
proc.stderr.on("data", (chunk) => {
|
|
48
|
+
process.stderr.write(` [${binName}] ${chunk.toString()}`);
|
|
49
|
+
});
|
|
50
|
+
proc.on("close", (code) => {
|
|
51
|
+
if (code !== null && code > 1) {
|
|
52
|
+
reject(new Error(`${binName} failed (exit ${code})`));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const json = JSON.parse(Buffer.concat(stdoutChunks).toString());
|
|
57
|
+
resolve(json);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
reject(new Error(`Failed to parse ${binName} output: ${err}`));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
proc.on("error", (err) => {
|
|
64
|
+
reject(new Error(`Failed to spawn ${binName}: ${err.message}`));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** Rank, deduplicate, and select top findings. */
|
|
69
|
+
export function selectFindings(semgrepOutput, repoName, ref, maxFindings) {
|
|
70
|
+
const results = semgrepOutput.results ?? [];
|
|
71
|
+
const scored = results.map((f) => [
|
|
72
|
+
scoreSemgrepFinding(f),
|
|
73
|
+
f,
|
|
74
|
+
]);
|
|
75
|
+
scored.sort((a, b) => b[0] - a[0]);
|
|
76
|
+
const seen = new Map();
|
|
77
|
+
for (const [score, finding] of scored) {
|
|
78
|
+
const fp = computeFingerprint(repoName, ref, finding);
|
|
79
|
+
const existing = seen.get(fp);
|
|
80
|
+
if (!existing || score > existing[0]) {
|
|
81
|
+
seen.set(fp, [score, finding]);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const deduped = [...seen.entries()]
|
|
85
|
+
.sort((a, b) => b[1][0] - a[1][0])
|
|
86
|
+
.slice(0, maxFindings);
|
|
87
|
+
return deduped.map(([fp, [score, finding]]) => {
|
|
88
|
+
const extra = finding.extra ?? {};
|
|
89
|
+
const meta = extra.metadata ?? {};
|
|
90
|
+
return {
|
|
91
|
+
scanner: "semgrep",
|
|
92
|
+
fingerprint: fp,
|
|
93
|
+
ruleId: finding.check_id,
|
|
94
|
+
filePath: finding.path,
|
|
95
|
+
startLine: finding.start.line,
|
|
96
|
+
severity: extra.severity ?? "UNKNOWN",
|
|
97
|
+
message: extra.message ?? "",
|
|
98
|
+
evidence: normalizeEvidence(extra.lines),
|
|
99
|
+
scannerConfidence: meta.confidence ?? "UNKNOWN",
|
|
100
|
+
subcategory: meta.subcategory ?? ["unknown"],
|
|
101
|
+
likelihood: meta.likelihood ?? "UNKNOWN",
|
|
102
|
+
impact: meta.impact ?? "UNKNOWN",
|
|
103
|
+
cwe: meta.cwe ?? [],
|
|
104
|
+
score,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function normalizeEvidence(value) {
|
|
109
|
+
if (typeof value !== "string") {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const trimmed = value.trim();
|
|
113
|
+
return trimmed || null;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=run-semgrep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-semgrep.js","sourceRoot":"","sources":["../../src/scanners/run-semgrep.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const SEVERITY_SCORE = { ERROR: 3, WARNING: 2, INFO: 1 };
|
|
2
|
+
const LIKELIHOOD_SCORE = { HIGH: 3, MEDIUM: 2, LOW: 1 };
|
|
3
|
+
const IMPACT_SCORE = { HIGH: 3, MEDIUM: 2, LOW: 1 };
|
|
4
|
+
const SUBCATEGORY_SCORE = { vuln: 2, audit: 1 };
|
|
5
|
+
/** Shared risk scoring used by all scanners. */
|
|
6
|
+
export function scoreFindingMetadata(input) {
|
|
7
|
+
const severity = SEVERITY_SCORE[input.severity ?? "INFO"] ?? 1;
|
|
8
|
+
const likelihood = LIKELIHOOD_SCORE[input.likelihood ?? "LOW"] ?? 1;
|
|
9
|
+
const impact = IMPACT_SCORE[input.impact ?? "LOW"] ?? 1;
|
|
10
|
+
const subcategories = input.subcategory ?? ["audit"];
|
|
11
|
+
const subcategory = Math.max(...subcategories.map((s) => SUBCATEGORY_SCORE[s] ?? 0), 1);
|
|
12
|
+
return severity * likelihood * impact * subcategory;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=scoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoring.js","sourceRoot":"","sources":["../../src/scanners/scoring.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc,GAA2B,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AACjF,MAAM,gBAAgB,GAA2B,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;AAChF,MAAM,YAAY,GAA2B,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;AAC5E,MAAM,iBAAiB,GAA2B,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAExE,gDAAgD;AAChD,MAAM,UAAU,oBAAoB,CAAC,KAKpC;IACC,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,aAAa,GAAG,KAAK,CAAC,WAAW,IAAI,CAAC,OAAO,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxF,OAAO,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,WAAW,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ScanOpts } from "../types.js";
|
|
2
|
+
import type { Finding, ScannerConfig, ScannerPlugin, TriageAgentConfig } from "./types.js";
|
|
3
|
+
export declare class SemgrepPlugin implements ScannerPlugin {
|
|
4
|
+
readonly id = "semgrep";
|
|
5
|
+
readonly displayName = "Semgrep/Opengrep";
|
|
6
|
+
private scannerBin;
|
|
7
|
+
private configFlag;
|
|
8
|
+
init(config: ScannerConfig): Promise<void>;
|
|
9
|
+
scan(repoRoot: string, opts: ScanOpts, _config: ScannerConfig): Promise<Finding[]>;
|
|
10
|
+
triageAgentConfig(finding: Finding, repoName: string): TriageAgentConfig;
|
|
11
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { resolveSemgrepBinary } from "./resolve-semgrep.js";
|
|
3
|
+
import { runSemgrep, selectFindings } from "./run-semgrep.js";
|
|
4
|
+
export class SemgrepPlugin {
|
|
5
|
+
id = "semgrep";
|
|
6
|
+
displayName = "Semgrep/Opengrep";
|
|
7
|
+
scannerBin = "semgrep";
|
|
8
|
+
configFlag = "auto";
|
|
9
|
+
async init(config) {
|
|
10
|
+
const configuredBinary = asOptionalString(config["binary"]);
|
|
11
|
+
const configuredFlag = asOptionalString(config["configFlag"]);
|
|
12
|
+
this.configFlag = configuredFlag || "auto";
|
|
13
|
+
if (configuredBinary) {
|
|
14
|
+
this.scannerBin = configuredBinary;
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
this.scannerBin = await resolveSemgrepBinary();
|
|
18
|
+
}
|
|
19
|
+
async scan(repoRoot, opts, _config) {
|
|
20
|
+
const repoName = path.basename(repoRoot);
|
|
21
|
+
const output = await runSemgrep(repoRoot, opts, this.scannerBin, this.configFlag);
|
|
22
|
+
return selectFindings(output, repoName, "HEAD", opts.maxFindings);
|
|
23
|
+
}
|
|
24
|
+
triageAgentConfig(finding, repoName) {
|
|
25
|
+
return {
|
|
26
|
+
prompt: [
|
|
27
|
+
"You are a Semgrep-focused security triage agent.",
|
|
28
|
+
"Semgrep findings come from syntactic/AST pattern matches and can include false positives without full program context.",
|
|
29
|
+
"Investigate thoroughly before deciding verdict.",
|
|
30
|
+
"",
|
|
31
|
+
"## Finding",
|
|
32
|
+
`Repo: ${repoName}`,
|
|
33
|
+
`Scanner: ${finding.scanner}`,
|
|
34
|
+
`Rule: ${finding.ruleId}`,
|
|
35
|
+
`File: ${finding.filePath}:${finding.startLine}`,
|
|
36
|
+
`Severity: ${finding.severity}`,
|
|
37
|
+
`Scanner Confidence: ${finding.scannerConfidence}`,
|
|
38
|
+
`Likelihood: ${finding.likelihood}`,
|
|
39
|
+
`Impact: ${finding.impact}`,
|
|
40
|
+
`CWE: ${finding.cwe.join(", ") || "none"}`,
|
|
41
|
+
`Message: ${finding.message}`,
|
|
42
|
+
`Evidence: ${finding.evidence || "none"}`,
|
|
43
|
+
`Fingerprint: ${finding.fingerprint}`,
|
|
44
|
+
"",
|
|
45
|
+
"## Investigation instructions",
|
|
46
|
+
"1. Use Read on the flagged file and inspect code around the target line.",
|
|
47
|
+
"2. Use Grep/Glob to find callers, data sources, sinks, validators, sanitizers, and guards.",
|
|
48
|
+
"3. Trace end-to-end dataflow and trust boundaries.",
|
|
49
|
+
"4. Confirm reachability and realistic attacker control.",
|
|
50
|
+
"5. Explicitly explain why Semgrep's pattern signal is correct or a false alarm.",
|
|
51
|
+
"",
|
|
52
|
+
"## Output requirements",
|
|
53
|
+
"Return ONLY a JSON object with:",
|
|
54
|
+
"fingerprint, verdict(tp|fp|needs_review), confidence(0..1), rationale, verification_steps(array), fix_patch(string|null)",
|
|
55
|
+
].join("\n"),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function asOptionalString(value) {
|
|
60
|
+
if (typeof value !== "string")
|
|
61
|
+
return "";
|
|
62
|
+
return value.trim();
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=semgrep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semgrep.js","sourceRoot":"","sources":["../../src/scanners/semgrep.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE9D,MAAM,OAAO,aAAa;IACf,EAAE,GAAG,SAAS,CAAC;IACf,WAAW,GAAG,kBAAkB,CAAC;IAElC,UAAU,GAAW,SAAS,CAAC;IAC/B,UAAU,GAAW,MAAM,CAAC;IAEpC,KAAK,CAAC,IAAI,CAAC,MAAqB;QAC9B,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC5D,MAAM,cAAc,GAAG,gBAAgB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;QAE9D,IAAI,CAAC,UAAU,GAAG,cAAc,IAAI,MAAM,CAAC;QAC3C,IAAI,gBAAgB,EAAE,CAAC;YACrB,IAAI,CAAC,UAAU,GAAG,gBAAgB,CAAC;YACnC,OAAO;QACT,CAAC;QAED,IAAI,CAAC,UAAU,GAAG,MAAM,oBAAoB,EAAE,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAgB,EAAE,IAAc,EAAE,OAAsB;QACjE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAClF,OAAO,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACpE,CAAC;IAED,iBAAiB,CAAC,OAAgB,EAAE,QAAgB;QAClD,OAAO;YACL,MAAM,EAAE;gBACN,kDAAkD;gBAClD,wHAAwH;gBACxH,iDAAiD;gBACjD,EAAE;gBACF,YAAY;gBACZ,SAAS,QAAQ,EAAE;gBACnB,YAAY,OAAO,CAAC,OAAO,EAAE;gBAC7B,SAAS,OAAO,CAAC,MAAM,EAAE;gBACzB,SAAS,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,SAAS,EAAE;gBAChD,aAAa,OAAO,CAAC,QAAQ,EAAE;gBAC/B,uBAAuB,OAAO,CAAC,iBAAiB,EAAE;gBAClD,eAAe,OAAO,CAAC,UAAU,EAAE;gBACnC,WAAW,OAAO,CAAC,MAAM,EAAE;gBAC3B,QAAQ,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE;gBAC1C,YAAY,OAAO,CAAC,OAAO,EAAE;gBAC7B,aAAa,OAAO,CAAC,QAAQ,IAAI,MAAM,EAAE;gBACzC,gBAAgB,OAAO,CAAC,WAAW,EAAE;gBACrC,EAAE;gBACF,+BAA+B;gBAC/B,0EAA0E;gBAC1E,4FAA4F;gBAC5F,oDAAoD;gBACpD,yDAAyD;gBACzD,iFAAiF;gBACjF,EAAE;gBACF,wBAAwB;gBACxB,iCAAiC;gBACjC,0HAA0H;aAC3H,CAAC,IAAI,CAAC,IAAI,CAAC;SACb,CAAC;IACJ,CAAC;CACF;AAED,SAAS,gBAAgB,CAAC,KAAc;IACtC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzC,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ScanOpts } from "../types.js";
|
|
2
|
+
export type ScannerId = string;
|
|
3
|
+
export type ScannerConfig = Record<string, unknown>;
|
|
4
|
+
export interface Finding {
|
|
5
|
+
scanner: ScannerId;
|
|
6
|
+
fingerprint: string;
|
|
7
|
+
ruleId: string;
|
|
8
|
+
filePath: string;
|
|
9
|
+
startLine: number;
|
|
10
|
+
severity: string;
|
|
11
|
+
message: string;
|
|
12
|
+
/** Optional scanner-provided evidence (for example, source-to-sink flow summaries). */
|
|
13
|
+
evidence?: string | null;
|
|
14
|
+
scannerConfidence: string;
|
|
15
|
+
subcategory: string[];
|
|
16
|
+
likelihood: string;
|
|
17
|
+
impact: string;
|
|
18
|
+
cwe: string[];
|
|
19
|
+
score: number;
|
|
20
|
+
}
|
|
21
|
+
export type AllowedTriageTool = "Read" | "Glob" | "Grep";
|
|
22
|
+
export interface TriageAgentConfig {
|
|
23
|
+
/** Prompt for the Agent SDK query(). Finding metadata + investigation instructions. */
|
|
24
|
+
prompt: string;
|
|
25
|
+
/** Override default tools. Defaults to ["Read", "Glob", "Grep"]. */
|
|
26
|
+
allowedTools?: AllowedTriageTool[];
|
|
27
|
+
/** Override default max turns. Defaults to config.triageMaxTurns. */
|
|
28
|
+
maxTurns?: number;
|
|
29
|
+
}
|
|
30
|
+
export interface ScannerPlugin {
|
|
31
|
+
readonly id: ScannerId;
|
|
32
|
+
readonly displayName: string;
|
|
33
|
+
scan(repoRoot: string, opts: ScanOpts, config: ScannerConfig): Promise<Finding[]>;
|
|
34
|
+
triageAgentConfig(finding: Finding, repoName: string): TriageAgentConfig;
|
|
35
|
+
init?(config: ScannerConfig): Promise<void>;
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/scanners/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type KuzushiConfig, type RawFinding, type ScanOpts, type Scanner } from "./types.js";
|
|
2
|
+
export interface ScannerPlugin {
|
|
3
|
+
id: Scanner;
|
|
4
|
+
displayName: string;
|
|
5
|
+
scan(ctx: {
|
|
6
|
+
repoRoot: string;
|
|
7
|
+
repoName: string;
|
|
8
|
+
ref: string;
|
|
9
|
+
opts: ScanOpts;
|
|
10
|
+
}): Promise<RawFinding[]>;
|
|
11
|
+
}
|
|
12
|
+
export interface ScannerRunResult {
|
|
13
|
+
scanner: Scanner;
|
|
14
|
+
findings: RawFinding[];
|
|
15
|
+
}
|
|
16
|
+
/** Parse a comma-separated scanner list into validated scanner IDs. */
|
|
17
|
+
export declare function parseScannerList(raw: string): Scanner[];
|
|
18
|
+
/** Validate scanner IDs and de-duplicate while preserving order. */
|
|
19
|
+
export declare function validateScannerList(raw: string[]): Scanner[];
|
|
20
|
+
/** Resolve scanner selection from CLI opts and config defaults. */
|
|
21
|
+
export declare function resolveSelectedScanners(opts: ScanOpts, config: KuzushiConfig): Scanner[];
|
|
22
|
+
/** Run all selected scanners and merge their findings. */
|
|
23
|
+
export declare function runSelectedScanners(repoRoot: string, opts: ScanOpts, config: KuzushiConfig): Promise<ScannerRunResult[]>;
|
|
24
|
+
/** Merge scanner outputs, dedupe by fingerprint, and rank globally by score. */
|
|
25
|
+
export declare function mergeScannerFindings(scannerResults: ScannerRunResult[], maxFindings?: number): RawFinding[];
|
package/dist/scanners.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { SCANNERS, isScanner, } from "./types.js";
|
|
3
|
+
import { runSemgrep, selectFindings } from "./scanner.js";
|
|
4
|
+
import { resolveScanner } from "./scanner/resolve.js";
|
|
5
|
+
import { runClaudeAdk, selectClaudeFindings } from "./scanner/claude-adk.js";
|
|
6
|
+
const PLUGIN_FACTORIES = {
|
|
7
|
+
"semgrep": createSemgrepPlugin,
|
|
8
|
+
"claude-adk": createClaudeAdkPlugin,
|
|
9
|
+
};
|
|
10
|
+
/** Parse a comma-separated scanner list into validated scanner IDs. */
|
|
11
|
+
export function parseScannerList(raw) {
|
|
12
|
+
const scanners = raw
|
|
13
|
+
.split(",")
|
|
14
|
+
.map((s) => s.trim().toLowerCase())
|
|
15
|
+
.filter(Boolean);
|
|
16
|
+
return validateScannerList(scanners);
|
|
17
|
+
}
|
|
18
|
+
/** Validate scanner IDs and de-duplicate while preserving order. */
|
|
19
|
+
export function validateScannerList(raw) {
|
|
20
|
+
const invalid = raw.filter((s) => !isScanner(s));
|
|
21
|
+
if (invalid.length > 0) {
|
|
22
|
+
throw new Error(`Unknown scanner(s): ${invalid.join(", ")}. Supported scanners: ${SCANNERS.join(", ")}`);
|
|
23
|
+
}
|
|
24
|
+
const deduped = [...new Set(raw)];
|
|
25
|
+
if (deduped.length === 0) {
|
|
26
|
+
throw new Error("At least one scanner must be configured.");
|
|
27
|
+
}
|
|
28
|
+
return deduped;
|
|
29
|
+
}
|
|
30
|
+
/** Resolve scanner selection from CLI opts and config defaults. */
|
|
31
|
+
export function resolveSelectedScanners(opts, config) {
|
|
32
|
+
const selected = opts.scanners?.length ? opts.scanners : config.scanners;
|
|
33
|
+
return validateScannerList(selected);
|
|
34
|
+
}
|
|
35
|
+
/** Run all selected scanners and merge their findings. */
|
|
36
|
+
export async function runSelectedScanners(repoRoot, opts, config) {
|
|
37
|
+
const repoName = path.basename(repoRoot);
|
|
38
|
+
const ref = "HEAD";
|
|
39
|
+
const selectedScanners = resolveSelectedScanners(opts, config);
|
|
40
|
+
const results = [];
|
|
41
|
+
for (const scannerId of selectedScanners) {
|
|
42
|
+
const factory = PLUGIN_FACTORIES[scannerId];
|
|
43
|
+
const plugin = await factory(config);
|
|
44
|
+
const findings = await plugin.scan({ repoRoot, repoName, ref, opts });
|
|
45
|
+
results.push({
|
|
46
|
+
scanner: plugin.id,
|
|
47
|
+
findings,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return results;
|
|
51
|
+
}
|
|
52
|
+
/** Merge scanner outputs, dedupe by fingerprint, and rank globally by score. */
|
|
53
|
+
export function mergeScannerFindings(scannerResults, maxFindings) {
|
|
54
|
+
const deduped = new Map();
|
|
55
|
+
for (const result of scannerResults) {
|
|
56
|
+
for (const finding of result.findings) {
|
|
57
|
+
const existing = deduped.get(finding.fingerprint);
|
|
58
|
+
if (!existing || finding.score > existing.score) {
|
|
59
|
+
deduped.set(finding.fingerprint, finding);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return [...deduped.values()]
|
|
64
|
+
.sort((a, b) => b.score - a.score)
|
|
65
|
+
.slice(0, maxFindings);
|
|
66
|
+
}
|
|
67
|
+
async function createSemgrepPlugin() {
|
|
68
|
+
const scannerBin = await resolveScanner();
|
|
69
|
+
return {
|
|
70
|
+
id: "semgrep",
|
|
71
|
+
displayName: "Semgrep/Opengrep",
|
|
72
|
+
async scan({ repoRoot, repoName, ref, opts }) {
|
|
73
|
+
const output = await runSemgrep(repoRoot, opts, scannerBin);
|
|
74
|
+
return selectFindings(output, repoName, ref);
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function createClaudeAdkPlugin(config) {
|
|
79
|
+
return {
|
|
80
|
+
id: "claude-adk",
|
|
81
|
+
displayName: "Claude Agent SDK",
|
|
82
|
+
async scan({ repoRoot, repoName, ref, opts }) {
|
|
83
|
+
const output = await runClaudeAdk(repoRoot, opts, config.claudeAdkModel, config.claudeAdkMaxFindings);
|
|
84
|
+
return selectClaudeFindings(output, repoRoot, repoName, ref);
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=scanners.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanners.js","sourceRoot":"","sources":["../src/scanners.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import type { KuzushiConfig, PocHarnessResult, RepoContext, ScanOpts, TriageResult, VerificationResult, Verdict } from "./types.js";
|
|
3
|
+
import type { Finding, ScannerId } from "./scanners/types.js";
|
|
4
|
+
export type PipelineRunStatus = "scanning" | "triaging" | "verifying" | "poc-harnessing" | "complete" | "error";
|
|
5
|
+
export interface PipelineRunRecord {
|
|
6
|
+
runId: string;
|
|
7
|
+
repo: string;
|
|
8
|
+
startedAt: string;
|
|
9
|
+
status: PipelineRunStatus;
|
|
10
|
+
configJson: string;
|
|
11
|
+
scanOptsJson: string;
|
|
12
|
+
findingsJson: string | null;
|
|
13
|
+
contextJson: string | null;
|
|
14
|
+
totalCostUsd: number;
|
|
15
|
+
completedAt: string | null;
|
|
16
|
+
updatedAt: string;
|
|
17
|
+
}
|
|
18
|
+
/** Initialize the SQLite database with the findings schema. */
|
|
19
|
+
export declare function initDb(dbPath: string): Database.Database;
|
|
20
|
+
/** Default DB path: .kuzushi/findings.sqlite3 in the scanned repo root. */
|
|
21
|
+
export declare function defaultDbPath(repoRoot: string): string;
|
|
22
|
+
/** Upsert a triage result into the database. */
|
|
23
|
+
export declare function upsertFinding(db: Database.Database, finding: Finding, triage: TriageResult, repo: string, ref: string): void;
|
|
24
|
+
/** Get set of already-triaged fingerprints from the database. */
|
|
25
|
+
export declare function getTriagedFingerprints(db: Database.Database): Set<string>;
|
|
26
|
+
/** Get set of already-verified fingerprints from the database. */
|
|
27
|
+
export declare function getVerifiedFingerprints(db: Database.Database): Set<string>;
|
|
28
|
+
/** Get set of fingerprints with completed PoC harness generation. */
|
|
29
|
+
export declare function getPocHarnessedFingerprints(db: Database.Database): Set<string>;
|
|
30
|
+
/** Load persisted triage results for a set of fingerprints. */
|
|
31
|
+
export declare function getStoredTriageResults(db: Database.Database, fingerprints: string[]): Map<string, TriageResult>;
|
|
32
|
+
/** Load persisted verification results for a set of fingerprints. */
|
|
33
|
+
export declare function getStoredVerificationResults(db: Database.Database, fingerprints: string[]): Map<string, VerificationResult>;
|
|
34
|
+
/** Get summary counts by verdict. */
|
|
35
|
+
export declare function getVerdictCounts(db: Database.Database, repo?: string): Record<Verdict, number>;
|
|
36
|
+
/** Get total triage cost in USD from persisted findings. */
|
|
37
|
+
export declare function getTotalCost(db: Database.Database, repo?: string): number;
|
|
38
|
+
/** Get total verification cost in USD from persisted findings. */
|
|
39
|
+
export declare function getTotalVerificationCost(db: Database.Database, repo?: string): number;
|
|
40
|
+
/** Get total PoC harness generation cost in USD from persisted findings. */
|
|
41
|
+
export declare function getTotalPocHarnessCost(db: Database.Database, repo?: string): number;
|
|
42
|
+
/** Get verification counts grouped by exploitability outcome. */
|
|
43
|
+
export declare function getVerificationCounts(db: Database.Database, repo?: string): {
|
|
44
|
+
exploitable: number;
|
|
45
|
+
notExploitable: number;
|
|
46
|
+
};
|
|
47
|
+
/** Get PoC harness generation counts. */
|
|
48
|
+
export declare function getPocHarnessCounts(db: Database.Database, repo?: string): {
|
|
49
|
+
generated: number;
|
|
50
|
+
skipped: number;
|
|
51
|
+
};
|
|
52
|
+
/** Get triaged findings, ordered by verdict priority and confidence. */
|
|
53
|
+
export declare function getActionableFindings(db: Database.Database, repo?: string, options?: {
|
|
54
|
+
includeFalsePositives?: boolean;
|
|
55
|
+
}): Array<{
|
|
56
|
+
scanner: ScannerId;
|
|
57
|
+
fingerprint: string;
|
|
58
|
+
ruleId: string;
|
|
59
|
+
filePath: string;
|
|
60
|
+
startLine: number;
|
|
61
|
+
severity: string;
|
|
62
|
+
message: string;
|
|
63
|
+
verdict: Verdict;
|
|
64
|
+
confidence: number;
|
|
65
|
+
rationale: string;
|
|
66
|
+
fixPatch: string | null;
|
|
67
|
+
verifyExploitable: number | null;
|
|
68
|
+
verifyPocPayload: string | null;
|
|
69
|
+
verifyPocDescription: string | null;
|
|
70
|
+
verifyAttackVector: string | null;
|
|
71
|
+
verifyPreconditions: string | null;
|
|
72
|
+
verifyConfidence: number | null;
|
|
73
|
+
verifyCostUsd: number | null;
|
|
74
|
+
pocHarnessPath: string | null;
|
|
75
|
+
pocHarnessLanguage: string | null;
|
|
76
|
+
pocHarnessParsed: number | null;
|
|
77
|
+
pocHarnessParseError: string | null;
|
|
78
|
+
pocHarnessSkipped: number | null;
|
|
79
|
+
pocHarnessSkipReason: string | null;
|
|
80
|
+
pocHarnessCostUsd: number | null;
|
|
81
|
+
}>;
|
|
82
|
+
/** Create a new persisted pipeline run checkpoint. */
|
|
83
|
+
export declare function createRun(db: Database.Database, runId: string, repo: string, config: KuzushiConfig, scanOpts: ScanOpts): void;
|
|
84
|
+
/** Update run status, optionally setting additional persisted fields. */
|
|
85
|
+
export declare function updateRunStatus(db: Database.Database, runId: string, status: PipelineRunStatus, extra?: {
|
|
86
|
+
findingsJson?: string | null;
|
|
87
|
+
completedAt?: string | null;
|
|
88
|
+
}): void;
|
|
89
|
+
/** Persist ranked findings checkpoint after scan completes. */
|
|
90
|
+
export declare function updateRunFindings(db: Database.Database, runId: string, findings: Finding[]): void;
|
|
91
|
+
/** Persist gathered repository context for resume support. */
|
|
92
|
+
export declare function updateRunContext(db: Database.Database, runId: string, repoContext: RepoContext): void;
|
|
93
|
+
/** Increment persisted run cost in USD. */
|
|
94
|
+
export declare function updateRunCost(db: Database.Database, runId: string, costUsd: number): void;
|
|
95
|
+
/** Persist verification result fields for an existing finding row. */
|
|
96
|
+
export declare function updateVerificationResult(db: Database.Database, fingerprint: string, result: VerificationResult): void;
|
|
97
|
+
/** Persist verification cost for a finding even when verification errors. */
|
|
98
|
+
export declare function updateVerificationCost(db: Database.Database, fingerprint: string, costUsd: number): void;
|
|
99
|
+
/** Persist PoC harness generation result fields for an existing finding row. */
|
|
100
|
+
export declare function updatePocHarnessResult(db: Database.Database, fingerprint: string, result: PocHarnessResult): void;
|
|
101
|
+
/** Persist PoC harness generation cost for a finding even when generation errors. */
|
|
102
|
+
export declare function updatePocHarnessCost(db: Database.Database, fingerprint: string, costUsd: number): void;
|
|
103
|
+
/** Get latest resumable run for repo, if any. */
|
|
104
|
+
export declare function getLatestResumableRun(db: Database.Database, repo: string): PipelineRunRecord | undefined;
|
|
105
|
+
/** Get persisted run checkpoint by run id. */
|
|
106
|
+
export declare function getRun(db: Database.Database, runId: string): PipelineRunRecord | undefined;
|