dravix-agent 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/.claude/settings.example.json +30 -0
- package/ARCHITECTURE.md +410 -0
- package/LICENSE +21 -0
- package/README.md +153 -0
- package/ROADMAP.md +117 -0
- package/data/vulnkb.json +666 -0
- package/dist/bin/aegis.d.ts +3 -0
- package/dist/bin/aegis.d.ts.map +1 -0
- package/dist/bin/aegis.js +489 -0
- package/dist/bin/aegis.js.map +1 -0
- package/dist/cache.d.ts +9 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +146 -0
- package/dist/cache.js.map +1 -0
- package/dist/engines/ai-sinks.d.ts +52 -0
- package/dist/engines/ai-sinks.d.ts.map +1 -0
- package/dist/engines/ai-sinks.js +204 -0
- package/dist/engines/ai-sinks.js.map +1 -0
- package/dist/engines/eslint.d.ts +9 -0
- package/dist/engines/eslint.d.ts.map +1 -0
- package/dist/engines/eslint.js +245 -0
- package/dist/engines/eslint.js.map +1 -0
- package/dist/engines/joern.d.ts +3 -0
- package/dist/engines/joern.d.ts.map +1 -0
- package/dist/engines/joern.js +98 -0
- package/dist/engines/joern.js.map +1 -0
- package/dist/engines/js-sinks.d.ts +70 -0
- package/dist/engines/js-sinks.d.ts.map +1 -0
- package/dist/engines/js-sinks.js +370 -0
- package/dist/engines/js-sinks.js.map +1 -0
- package/dist/engines/llm-critic.d.ts +130 -0
- package/dist/engines/llm-critic.d.ts.map +1 -0
- package/dist/engines/llm-critic.js +551 -0
- package/dist/engines/llm-critic.js.map +1 -0
- package/dist/engines/pragma.d.ts +20 -0
- package/dist/engines/pragma.d.ts.map +1 -0
- package/dist/engines/pragma.js +83 -0
- package/dist/engines/pragma.js.map +1 -0
- package/dist/engines/property-test.d.ts +3 -0
- package/dist/engines/property-test.d.ts.map +1 -0
- package/dist/engines/property-test.js +134 -0
- package/dist/engines/property-test.js.map +1 -0
- package/dist/engines/pyright.d.ts +10 -0
- package/dist/engines/pyright.d.ts.map +1 -0
- package/dist/engines/pyright.js +143 -0
- package/dist/engines/pyright.js.map +1 -0
- package/dist/engines/pysa.d.ts +3 -0
- package/dist/engines/pysa.d.ts.map +1 -0
- package/dist/engines/pysa.js +83 -0
- package/dist/engines/pysa.js.map +1 -0
- package/dist/engines/python-sinks.d.ts +82 -0
- package/dist/engines/python-sinks.d.ts.map +1 -0
- package/dist/engines/python-sinks.js +459 -0
- package/dist/engines/python-sinks.js.map +1 -0
- package/dist/engines/registry.d.ts +26 -0
- package/dist/engines/registry.d.ts.map +1 -0
- package/dist/engines/registry.js +70 -0
- package/dist/engines/registry.js.map +1 -0
- package/dist/engines/secret-scan.d.ts +22 -0
- package/dist/engines/secret-scan.d.ts.map +1 -0
- package/dist/engines/secret-scan.js +179 -0
- package/dist/engines/secret-scan.js.map +1 -0
- package/dist/engines/semgrep.d.ts +10 -0
- package/dist/engines/semgrep.d.ts.map +1 -0
- package/dist/engines/semgrep.js +200 -0
- package/dist/engines/semgrep.js.map +1 -0
- package/dist/engines/treesitter.d.ts +18 -0
- package/dist/engines/treesitter.d.ts.map +1 -0
- package/dist/engines/treesitter.js +135 -0
- package/dist/engines/treesitter.js.map +1 -0
- package/dist/engines/tsc.d.ts +10 -0
- package/dist/engines/tsc.d.ts.map +1 -0
- package/dist/engines/tsc.js +142 -0
- package/dist/engines/tsc.js.map +1 -0
- package/dist/engines/types.d.ts +47 -0
- package/dist/engines/types.d.ts.map +1 -0
- package/dist/engines/types.js +27 -0
- package/dist/engines/types.js.map +1 -0
- package/dist/findings.d.ts +121 -0
- package/dist/findings.d.ts.map +1 -0
- package/dist/findings.js +98 -0
- package/dist/findings.js.map +1 -0
- package/dist/hooks/claude-code.d.ts +3 -0
- package/dist/hooks/claude-code.d.ts.map +1 -0
- package/dist/hooks/claude-code.js +187 -0
- package/dist/hooks/claude-code.js.map +1 -0
- package/dist/index/context.d.ts +127 -0
- package/dist/index/context.d.ts.map +1 -0
- package/dist/index/context.js +267 -0
- package/dist/index/context.js.map +1 -0
- package/dist/index/embeddings.d.ts +68 -0
- package/dist/index/embeddings.d.ts.map +1 -0
- package/dist/index/embeddings.js +570 -0
- package/dist/index/embeddings.js.map +1 -0
- package/dist/index/graph_routing.d.ts +36 -0
- package/dist/index/graph_routing.d.ts.map +1 -0
- package/dist/index/graph_routing.js +170 -0
- package/dist/index/graph_routing.js.map +1 -0
- package/dist/index/joern.d.ts +76 -0
- package/dist/index/joern.d.ts.map +1 -0
- package/dist/index/joern.js +782 -0
- package/dist/index/joern.js.map +1 -0
- package/dist/index/property-test.d.ts +88 -0
- package/dist/index/property-test.d.ts.map +1 -0
- package/dist/index/property-test.js +466 -0
- package/dist/index/property-test.js.map +1 -0
- package/dist/index/proto/scip.proto +897 -0
- package/dist/index/pysa.d.ts +91 -0
- package/dist/index/pysa.d.ts.map +1 -0
- package/dist/index/pysa.js +617 -0
- package/dist/index/pysa.js.map +1 -0
- package/dist/index/scip.d.ts +76 -0
- package/dist/index/scip.d.ts.map +1 -0
- package/dist/index/scip.js +541 -0
- package/dist/index/scip.js.map +1 -0
- package/dist/index/vulrag.d.ts +86 -0
- package/dist/index/vulrag.d.ts.map +1 -0
- package/dist/index/vulrag.js +242 -0
- package/dist/index/vulrag.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/install/claude-code.d.ts +31 -0
- package/dist/install/claude-code.d.ts.map +1 -0
- package/dist/install/claude-code.js +447 -0
- package/dist/install/claude-code.js.map +1 -0
- package/dist/lang.d.ts +5 -0
- package/dist/lang.d.ts.map +1 -0
- package/dist/lang.js +52 -0
- package/dist/lang.js.map +1 -0
- package/dist/learning/suppressions.d.ts +70 -0
- package/dist/learning/suppressions.d.ts.map +1 -0
- package/dist/learning/suppressions.js +179 -0
- package/dist/learning/suppressions.js.map +1 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +187 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/explain.d.ts +58 -0
- package/dist/mcp/tools/explain.d.ts.map +1 -0
- package/dist/mcp/tools/explain.js +60 -0
- package/dist/mcp/tools/explain.js.map +1 -0
- package/dist/mcp/tools/precheck.d.ts +29 -0
- package/dist/mcp/tools/precheck.d.ts.map +1 -0
- package/dist/mcp/tools/precheck.js +42 -0
- package/dist/mcp/tools/precheck.js.map +1 -0
- package/dist/mcp/tools/validate.d.ts +73 -0
- package/dist/mcp/tools/validate.d.ts.map +1 -0
- package/dist/mcp/tools/validate.js +66 -0
- package/dist/mcp/tools/validate.js.map +1 -0
- package/dist/mcp/warm.d.ts +88 -0
- package/dist/mcp/warm.d.ts.map +1 -0
- package/dist/mcp/warm.js +331 -0
- package/dist/mcp/warm.js.map +1 -0
- package/dist/orchestrator.d.ts +46 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +596 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/policy.d.ts +51 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +201 -0
- package/dist/policy.js.map +1 -0
- package/dist/risk.d.ts +31 -0
- package/dist/risk.d.ts.map +1 -0
- package/dist/risk.js +92 -0
- package/dist/risk.js.map +1 -0
- package/dist/stats.d.ts +72 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +217 -0
- package/dist/stats.js.map +1 -0
- package/dist/telemetry/collector.d.ts +10 -0
- package/dist/telemetry/collector.d.ts.map +1 -0
- package/dist/telemetry/collector.js +75 -0
- package/dist/telemetry/collector.js.map +1 -0
- package/dist/telemetry/consent.d.ts +9 -0
- package/dist/telemetry/consent.d.ts.map +1 -0
- package/dist/telemetry/consent.js +42 -0
- package/dist/telemetry/consent.js.map +1 -0
- package/dist/telemetry/installation.d.ts +2 -0
- package/dist/telemetry/installation.d.ts.map +1 -0
- package/dist/telemetry/installation.js +32 -0
- package/dist/telemetry/installation.js.map +1 -0
- package/dist/telemetry/sanitizer.d.ts +5 -0
- package/dist/telemetry/sanitizer.d.ts.map +1 -0
- package/dist/telemetry/sanitizer.js +60 -0
- package/dist/telemetry/sanitizer.js.map +1 -0
- package/dist/telemetry/types.d.ts +39 -0
- package/dist/telemetry/types.d.ts.map +1 -0
- package/dist/telemetry/types.js +4 -0
- package/dist/telemetry/types.js.map +1 -0
- package/dist/telemetry/uploader.d.ts +12 -0
- package/dist/telemetry/uploader.d.ts.map +1 -0
- package/dist/telemetry/uploader.js +92 -0
- package/dist/telemetry/uploader.js.map +1 -0
- package/dist/util/logger.d.ts +19 -0
- package/dist/util/logger.d.ts.map +1 -0
- package/dist/util/logger.js +58 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/safe-paths.d.ts +8 -0
- package/dist/util/safe-paths.d.ts.map +1 -0
- package/dist/util/safe-paths.js +102 -0
- package/dist/util/safe-paths.js.map +1 -0
- package/dist/util/subprocess.d.ts +32 -0
- package/dist/util/subprocess.d.ts.map +1 -0
- package/dist/util/subprocess.js +137 -0
- package/dist/util/subprocess.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript compiler (tsc) adapter — subprocess to `tsc --noEmit --pretty false`.
|
|
3
|
+
*
|
|
4
|
+
* Catches type errors in .ts/.tsx (and .js with checkJs). Mirrors what an
|
|
5
|
+
* editor would show on save, but reachable from the gate.
|
|
6
|
+
*/
|
|
7
|
+
import { writeFile, mkdir, rm } from "node:fs/promises";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { makeFindingId } from "../findings.js";
|
|
11
|
+
import { run, which } from "../util/subprocess.js";
|
|
12
|
+
import { getLogger } from "../util/logger.js";
|
|
13
|
+
const log = getLogger("aegis.engine.tsc");
|
|
14
|
+
const SUPPORTED = ["typescript", "tsx", "javascript", "jsx"];
|
|
15
|
+
// Lines from `tsc --pretty false` look like:
|
|
16
|
+
// path/to/file.ts(12,5): error TS2304: Cannot find name 'foo'.
|
|
17
|
+
const LINE_RE = /^(?<file>.+?)\((?<line>\d+),(?<col>\d+)\):\s+(?<kind>error|warning)\s+TS(?<code>\d+):\s+(?<msg>.+)$/;
|
|
18
|
+
async function findTsc() {
|
|
19
|
+
const override = process.env.AEGIS_TSC_BIN;
|
|
20
|
+
if (override)
|
|
21
|
+
return override;
|
|
22
|
+
return (await which("tsc")) ?? (await which("tsc.cmd"));
|
|
23
|
+
}
|
|
24
|
+
export class TscEngine {
|
|
25
|
+
name = "tsc";
|
|
26
|
+
languages = SUPPORTED;
|
|
27
|
+
_availability = null;
|
|
28
|
+
async available() {
|
|
29
|
+
if (this._availability !== null)
|
|
30
|
+
return this._availability;
|
|
31
|
+
this._availability = (await findTsc()) !== null;
|
|
32
|
+
return this._availability;
|
|
33
|
+
}
|
|
34
|
+
async run(input) {
|
|
35
|
+
const t0 = Date.now();
|
|
36
|
+
const bin = await findTsc();
|
|
37
|
+
if (!bin) {
|
|
38
|
+
return {
|
|
39
|
+
engine: this.name,
|
|
40
|
+
findings: [],
|
|
41
|
+
unavailable: true,
|
|
42
|
+
durationMs: Date.now() - t0,
|
|
43
|
+
reason: "tsc_not_installed",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const scratch = (await mkdir(join(tmpdir(), `aegis-tsc-${process.pid}-${Date.now()}`), { recursive: true }));
|
|
47
|
+
const ext = input.lang === "tsx" ? ".tsx" :
|
|
48
|
+
input.lang === "typescript" ? ".ts" :
|
|
49
|
+
input.lang === "jsx" ? ".jsx" :
|
|
50
|
+
".js";
|
|
51
|
+
const scratchPath = join(scratch, basename(input.filePath, ext));
|
|
52
|
+
try {
|
|
53
|
+
await writeFile(scratchPath, input.content, "utf8");
|
|
54
|
+
const checkJs = input.lang === "javascript" || input.lang === "jsx";
|
|
55
|
+
const args = [
|
|
56
|
+
"--noEmit",
|
|
57
|
+
"--pretty", "false",
|
|
58
|
+
"--strict",
|
|
59
|
+
"--target", "ES2022",
|
|
60
|
+
"--module", "ESNext",
|
|
61
|
+
"--moduleResolution", "Bundler",
|
|
62
|
+
"--allowJs",
|
|
63
|
+
...(checkJs ? ["--checkJs"] : []),
|
|
64
|
+
"--skipLibCheck",
|
|
65
|
+
scratchPath,
|
|
66
|
+
];
|
|
67
|
+
const r = await run(bin, {
|
|
68
|
+
args,
|
|
69
|
+
timeoutMs: Math.min(input.timeoutMs, 15_000),
|
|
70
|
+
});
|
|
71
|
+
if (r.timedOut) {
|
|
72
|
+
return {
|
|
73
|
+
engine: this.name,
|
|
74
|
+
findings: [],
|
|
75
|
+
unavailable: true,
|
|
76
|
+
durationMs: Date.now() - t0,
|
|
77
|
+
reason: "timeout",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const out = (r.stdout + "\n" + r.stderr).split(/\r?\n/);
|
|
81
|
+
const findings = [];
|
|
82
|
+
for (const ln of out) {
|
|
83
|
+
const m = LINE_RE.exec(ln);
|
|
84
|
+
if (!m || !m.groups)
|
|
85
|
+
continue;
|
|
86
|
+
const lineNum = parseInt(m.groups.line ?? "0", 10);
|
|
87
|
+
const colNum = parseInt(m.groups.col ?? "0", 10);
|
|
88
|
+
const code = m.groups.code ?? "0";
|
|
89
|
+
const msg = m.groups.msg ?? "";
|
|
90
|
+
const sev = m.groups.kind === "error" ? "high" : "medium";
|
|
91
|
+
const conf = m.groups.kind === "error" ? 0.85 : 0.7;
|
|
92
|
+
findings.push({
|
|
93
|
+
id: makeFindingId({
|
|
94
|
+
engine: this.name,
|
|
95
|
+
file: input.filePath,
|
|
96
|
+
line: lineNum,
|
|
97
|
+
rule_id: `TS${code}`,
|
|
98
|
+
}),
|
|
99
|
+
engine: "tsc",
|
|
100
|
+
file: input.filePath,
|
|
101
|
+
line: lineNum,
|
|
102
|
+
col: colNum,
|
|
103
|
+
rule_id: `TS${code}`,
|
|
104
|
+
severity: sev,
|
|
105
|
+
message: msg.slice(0, 1900),
|
|
106
|
+
confidence: conf,
|
|
107
|
+
source: "type",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
engine: this.name,
|
|
112
|
+
findings,
|
|
113
|
+
unavailable: false,
|
|
114
|
+
durationMs: Date.now() - t0,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
log.warn("tsc run threw", { err: String(err) });
|
|
119
|
+
return {
|
|
120
|
+
engine: this.name,
|
|
121
|
+
findings: [],
|
|
122
|
+
unavailable: true,
|
|
123
|
+
durationMs: Date.now() - t0,
|
|
124
|
+
reason: "threw",
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
try {
|
|
129
|
+
await rm(scratch, { recursive: true, force: true });
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// ignore
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function basename(p, ext) {
|
|
138
|
+
const m = p.replace(/\\/g, "/").split("/");
|
|
139
|
+
const name = m[m.length - 1] || "file";
|
|
140
|
+
return name.includes(".") ? name : name + ext;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=tsc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tsc.js","sourceRoot":"","sources":["../../src/engines/tsc.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAW,aAAa,EAAY,MAAM,gBAAgB,CAAC;AAElE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,GAAG,GAAG,SAAS,CAAC,kBAAkB,CAAC,CAAC;AAE1C,MAAM,SAAS,GAAwB,CAAC,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;AAElF,6CAA6C;AAC7C,iEAAiE;AACjE,MAAM,OAAO,GACX,qGAAqG,CAAC;AAExG,KAAK,UAAU,OAAO;IACpB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAC3C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,OAAO,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,OAAO,SAAS;IACX,IAAI,GAAG,KAAK,CAAC;IACb,SAAS,GAAG,SAAS,CAAC;IAEvB,aAAa,GAAmB,IAAI,CAAC;IAE7C,KAAK,CAAC,SAAS;QACb,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,aAAa,CAAC;QAC3D,IAAI,CAAC,aAAa,GAAG,CAAC,MAAM,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC;QAChD,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAqB;QAC7B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO;gBACL,MAAM,EAAE,IAAI,CAAC,IAAI;gBACjB,QAAQ,EAAE,EAAE;gBACZ,WAAW,EAAE,IAAI;gBACjB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;gBAC3B,MAAM,EAAE,mBAAmB;aAC5B,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,GAAG,CAAC,MAAM,KAAK,CAC1B,IAAI,CAAC,MAAM,EAAE,EAAE,aAAa,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,EACxD,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAW,CAAC;QACb,MAAM,GAAG,GACP,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC/B,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;gBACrC,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;oBAC/B,KAAK,CAAC;QACR,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;QACjE,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACpD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC;YACpE,MAAM,IAAI,GAAG;gBACX,UAAU;gBACV,UAAU,EAAE,OAAO;gBACnB,UAAU;gBACV,UAAU,EAAE,QAAQ;gBACpB,UAAU,EAAE,QAAQ;gBACpB,oBAAoB,EAAE,SAAS;gBAC/B,WAAW;gBACX,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjC,gBAAgB;gBAChB,WAAW;aACZ,CAAC;YACF,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,GAAG,EAAE;gBACvB,IAAI;gBACJ,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC;aAC7C,CAAC,CAAC;YACH,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;gBACf,OAAO;oBACL,MAAM,EAAE,IAAI,CAAC,IAAI;oBACjB,QAAQ,EAAE,EAAE;oBACZ,WAAW,EAAE,IAAI;oBACjB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;oBAC3B,MAAM,EAAE,SAAS;iBAClB,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACxD,MAAM,QAAQ,GAAc,EAAE,CAAC;YAC/B,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;gBACrB,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC3B,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;oBAAE,SAAS;gBAC9B,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;gBACnD,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;gBACjD,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC;gBAClC,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;gBAC/B,MAAM,GAAG,GAAa,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;gBACpE,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;gBACpD,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,aAAa,CAAC;wBAChB,MAAM,EAAE,IAAI,CAAC,IAAI;wBACjB,IAAI,EAAE,KAAK,CAAC,QAAQ;wBACpB,IAAI,EAAE,OAAO;wBACb,OAAO,EAAE,KAAK,IAAI,EAAE;qBACrB,CAAC;oBACF,MAAM,EAAE,KAAK;oBACb,IAAI,EAAE,KAAK,CAAC,QAAQ;oBACpB,IAAI,EAAE,OAAO;oBACb,GAAG,EAAE,MAAM;oBACX,OAAO,EAAE,KAAK,IAAI,EAAE;oBACpB,QAAQ,EAAE,GAAG;oBACb,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC;oBAC3B,UAAU,EAAE,IAAI;oBAChB,MAAM,EAAE,MAAM;iBACf,CAAC,CAAC;YACL,CAAC;YACD,OAAO;gBACL,MAAM,EAAE,IAAI,CAAC,IAAI;gBACjB,QAAQ;gBACR,WAAW,EAAE,KAAK;gBAClB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;aAC5B,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChD,OAAO;gBACL,MAAM,EAAE,IAAI,CAAC,IAAI;gBACjB,QAAQ,EAAE,EAAE;gBACZ,WAAW,EAAE,IAAI;gBACjB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;gBAC3B,MAAM,EAAE,OAAO;aAChB,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACtD,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAW;IACtC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC;IACvC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Engine interface — every analyzer implements this. */
|
|
2
|
+
import type { ProjectContextBlock } from "../index/context.js";
|
|
3
|
+
import { Finding } from "../findings.js";
|
|
4
|
+
import { Lang } from "../lang.js";
|
|
5
|
+
export interface EngineRunInput {
|
|
6
|
+
/** Absolute path of the file under judgement. */
|
|
7
|
+
filePath: string;
|
|
8
|
+
/** Full file contents (UTF-8). */
|
|
9
|
+
content: string;
|
|
10
|
+
/** Detected language. */
|
|
11
|
+
lang: Lang;
|
|
12
|
+
/** Optional project root for engines that need cross-file context. */
|
|
13
|
+
projectRoot?: string;
|
|
14
|
+
/** Max wall-clock the engine has to return. Hard cap. */
|
|
15
|
+
timeoutMs: number;
|
|
16
|
+
/** Findings emitted by stage-1 engines (semgrep, pyright, etc.). Populated
|
|
17
|
+
* by the orchestrator BEFORE invoking stage-2 engines (currently:
|
|
18
|
+
* llm-critic) so the critic can reason over the static evidence instead
|
|
19
|
+
* of re-deriving it. Empty / undefined for stage-1 engines. */
|
|
20
|
+
priorFindings?: ReadonlyArray<Finding>;
|
|
21
|
+
/** Cross-file context block (SCIP-derived). Populated when the project
|
|
22
|
+
* has been indexed. Stage-2 engines use it as evidence in their prompt. */
|
|
23
|
+
projectContext?: ProjectContextBlock;
|
|
24
|
+
}
|
|
25
|
+
export interface EngineRunResult {
|
|
26
|
+
engine: string;
|
|
27
|
+
findings: Finding[];
|
|
28
|
+
/** True if the engine couldn't run (binary missing, parse failed). */
|
|
29
|
+
unavailable: boolean;
|
|
30
|
+
/** Wall-clock duration in ms. */
|
|
31
|
+
durationMs: number;
|
|
32
|
+
/** Optional short reason if unavailable. */
|
|
33
|
+
reason?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface Engine {
|
|
36
|
+
/** Stable name; must match the `Engine` enum in `findings.ts`. */
|
|
37
|
+
readonly name: string;
|
|
38
|
+
/** Languages this engine supports. Empty = all. */
|
|
39
|
+
readonly languages: ReadonlyArray<Lang> | "all";
|
|
40
|
+
/** Quick check (no work) — is this engine available on the host? */
|
|
41
|
+
available(): Promise<boolean>;
|
|
42
|
+
/** Run the engine on one file. Must never throw. */
|
|
43
|
+
run(input: EngineRunInput): Promise<EngineRunResult>;
|
|
44
|
+
}
|
|
45
|
+
/** Wraps an engine `run` with try/catch + timing — never lets an engine kill the gate. */
|
|
46
|
+
export declare function safeRun(engine: Engine, input: EngineRunInput): Promise<EngineRunResult>;
|
|
47
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/engines/types.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,MAAM,WAAW,cAAc;IAC7B,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB;;;mEAG+D;IAC/D,aAAa,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACvC;+EAC2E;IAC3E,cAAc,CAAC,EAAE,mBAAmB,CAAC;CACtC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,sEAAsE;IACtE,WAAW,EAAE,OAAO,CAAC;IACrB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,MAAM;IACrB,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IAChD,oEAAoE;IACpE,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,oDAAoD;IACpD,GAAG,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CACtD;AAED,0FAA0F;AAC1F,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAuB7F"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Wraps an engine `run` with try/catch + timing — never lets an engine kill the gate. */
|
|
2
|
+
export async function safeRun(engine, input) {
|
|
3
|
+
const t0 = Date.now();
|
|
4
|
+
try {
|
|
5
|
+
if (engine.languages !== "all" && !engine.languages.includes(input.lang)) {
|
|
6
|
+
return {
|
|
7
|
+
engine: engine.name,
|
|
8
|
+
findings: [],
|
|
9
|
+
unavailable: false,
|
|
10
|
+
durationMs: 0,
|
|
11
|
+
reason: "lang_not_supported",
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const r = await engine.run(input);
|
|
15
|
+
return r;
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
return {
|
|
19
|
+
engine: engine.name,
|
|
20
|
+
findings: [],
|
|
21
|
+
unavailable: true,
|
|
22
|
+
durationMs: Date.now() - t0,
|
|
23
|
+
reason: err instanceof Error ? err.message.slice(0, 200) : "unknown_error",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/engines/types.ts"],"names":[],"mappings":"AAgDA,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,MAAc,EAAE,KAAqB;IACjE,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACtB,IAAI,CAAC;QACH,IAAI,MAAM,CAAC,SAAS,KAAK,KAAK,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACzE,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,IAAI;gBACnB,QAAQ,EAAE,EAAE;gBACZ,WAAW,EAAE,KAAK;gBAClB,UAAU,EAAE,CAAC;gBACb,MAAM,EAAE,oBAAoB;aAC7B,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClC,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,IAAI;YACnB,QAAQ,EAAE,EAAE;YACZ,WAAW,EAAE,IAAI;YACjB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;YAC3B,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe;SAC3E,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const SeverityEnum: z.ZodEnum<["info", "low", "medium", "high", "critical"]>;
|
|
3
|
+
export type Severity = z.infer<typeof SeverityEnum>;
|
|
4
|
+
export declare const SEVERITY_ORDER: readonly Severity[];
|
|
5
|
+
export declare function severityRank(s: Severity): number;
|
|
6
|
+
export declare const EngineEnum: z.ZodEnum<["treesitter", "semgrep", "pyright", "tsc", "eslint", "secret-scan", "python-sinks", "js-sinks", "ai-sinks", "scip", "joern", "embeddings", "pysa", "racerd", "vulrag", "router-150m", "llm-critic"]>;
|
|
7
|
+
export type Engine = z.infer<typeof EngineEnum>;
|
|
8
|
+
export declare const SourceKindEnum: z.ZodEnum<["pattern", "type", "lint", "dataflow", "router", "critic", "ensemble"]>;
|
|
9
|
+
export type SourceKind = z.infer<typeof SourceKindEnum>;
|
|
10
|
+
export declare const FindingSchema: z.ZodObject<{
|
|
11
|
+
id: z.ZodString;
|
|
12
|
+
engine: z.ZodEnum<["treesitter", "semgrep", "pyright", "tsc", "eslint", "secret-scan", "python-sinks", "js-sinks", "ai-sinks", "scip", "joern", "embeddings", "pysa", "racerd", "vulrag", "router-150m", "llm-critic"]>;
|
|
13
|
+
file: z.ZodString;
|
|
14
|
+
line: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
col: z.ZodOptional<z.ZodNumber>;
|
|
16
|
+
rule_id: z.ZodString;
|
|
17
|
+
cwe: z.ZodOptional<z.ZodString>;
|
|
18
|
+
severity: z.ZodEnum<["info", "low", "medium", "high", "critical"]>;
|
|
19
|
+
message: z.ZodString;
|
|
20
|
+
evidence: z.ZodOptional<z.ZodObject<{
|
|
21
|
+
snippet: z.ZodOptional<z.ZodString>;
|
|
22
|
+
dataflow: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
23
|
+
file: z.ZodString;
|
|
24
|
+
line: z.ZodNumber;
|
|
25
|
+
label: z.ZodString;
|
|
26
|
+
}, "strip", z.ZodTypeAny, {
|
|
27
|
+
file: string;
|
|
28
|
+
line: number;
|
|
29
|
+
label: string;
|
|
30
|
+
}, {
|
|
31
|
+
file: string;
|
|
32
|
+
line: number;
|
|
33
|
+
label: string;
|
|
34
|
+
}>, "many">>;
|
|
35
|
+
callers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
36
|
+
related_cves: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
37
|
+
}, "strip", z.ZodTypeAny, {
|
|
38
|
+
dataflow?: {
|
|
39
|
+
file: string;
|
|
40
|
+
line: number;
|
|
41
|
+
label: string;
|
|
42
|
+
}[] | undefined;
|
|
43
|
+
snippet?: string | undefined;
|
|
44
|
+
callers?: string[] | undefined;
|
|
45
|
+
related_cves?: string[] | undefined;
|
|
46
|
+
}, {
|
|
47
|
+
dataflow?: {
|
|
48
|
+
file: string;
|
|
49
|
+
line: number;
|
|
50
|
+
label: string;
|
|
51
|
+
}[] | undefined;
|
|
52
|
+
snippet?: string | undefined;
|
|
53
|
+
callers?: string[] | undefined;
|
|
54
|
+
related_cves?: string[] | undefined;
|
|
55
|
+
}>>;
|
|
56
|
+
confidence: z.ZodNumber;
|
|
57
|
+
source: z.ZodEnum<["pattern", "type", "lint", "dataflow", "router", "critic", "ensemble"]>;
|
|
58
|
+
remediation: z.ZodOptional<z.ZodString>;
|
|
59
|
+
}, "strip", z.ZodTypeAny, {
|
|
60
|
+
message: string;
|
|
61
|
+
file: string;
|
|
62
|
+
id: string;
|
|
63
|
+
engine: "treesitter" | "semgrep" | "pyright" | "tsc" | "eslint" | "secret-scan" | "python-sinks" | "js-sinks" | "ai-sinks" | "scip" | "joern" | "embeddings" | "pysa" | "racerd" | "vulrag" | "router-150m" | "llm-critic";
|
|
64
|
+
rule_id: string;
|
|
65
|
+
severity: "info" | "low" | "medium" | "high" | "critical";
|
|
66
|
+
confidence: number;
|
|
67
|
+
source: "type" | "pattern" | "lint" | "dataflow" | "router" | "critic" | "ensemble";
|
|
68
|
+
line?: number | undefined;
|
|
69
|
+
col?: number | undefined;
|
|
70
|
+
cwe?: string | undefined;
|
|
71
|
+
evidence?: {
|
|
72
|
+
dataflow?: {
|
|
73
|
+
file: string;
|
|
74
|
+
line: number;
|
|
75
|
+
label: string;
|
|
76
|
+
}[] | undefined;
|
|
77
|
+
snippet?: string | undefined;
|
|
78
|
+
callers?: string[] | undefined;
|
|
79
|
+
related_cves?: string[] | undefined;
|
|
80
|
+
} | undefined;
|
|
81
|
+
remediation?: string | undefined;
|
|
82
|
+
}, {
|
|
83
|
+
message: string;
|
|
84
|
+
file: string;
|
|
85
|
+
id: string;
|
|
86
|
+
engine: "treesitter" | "semgrep" | "pyright" | "tsc" | "eslint" | "secret-scan" | "python-sinks" | "js-sinks" | "ai-sinks" | "scip" | "joern" | "embeddings" | "pysa" | "racerd" | "vulrag" | "router-150m" | "llm-critic";
|
|
87
|
+
rule_id: string;
|
|
88
|
+
severity: "info" | "low" | "medium" | "high" | "critical";
|
|
89
|
+
confidence: number;
|
|
90
|
+
source: "type" | "pattern" | "lint" | "dataflow" | "router" | "critic" | "ensemble";
|
|
91
|
+
line?: number | undefined;
|
|
92
|
+
col?: number | undefined;
|
|
93
|
+
cwe?: string | undefined;
|
|
94
|
+
evidence?: {
|
|
95
|
+
dataflow?: {
|
|
96
|
+
file: string;
|
|
97
|
+
line: number;
|
|
98
|
+
label: string;
|
|
99
|
+
}[] | undefined;
|
|
100
|
+
snippet?: string | undefined;
|
|
101
|
+
callers?: string[] | undefined;
|
|
102
|
+
related_cves?: string[] | undefined;
|
|
103
|
+
} | undefined;
|
|
104
|
+
remediation?: string | undefined;
|
|
105
|
+
}>;
|
|
106
|
+
export type Finding = z.infer<typeof FindingSchema>;
|
|
107
|
+
/** Stable 16-hex id from engine+file+line+rule — same input = same id (idempotent). */
|
|
108
|
+
export declare function makeFindingId(parts: {
|
|
109
|
+
engine: string;
|
|
110
|
+
file: string;
|
|
111
|
+
line?: number;
|
|
112
|
+
rule_id: string;
|
|
113
|
+
}): string;
|
|
114
|
+
/**
|
|
115
|
+
* Helper: validate a finding (throws on invalid). Engines call this in dev to
|
|
116
|
+
* fail loud; in production we use safeParse + log the violator and drop it.
|
|
117
|
+
*/
|
|
118
|
+
export declare function validateFinding(f: unknown): Finding;
|
|
119
|
+
/** Safe variant: returns null on invalid (engine bug shouldn't kill the gate). */
|
|
120
|
+
export declare function safeValidate(f: unknown): Finding | null;
|
|
121
|
+
//# sourceMappingURL=findings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findings.d.ts","sourceRoot":"","sources":["../src/findings.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,YAAY,0DAAwD,CAAC;AAClF,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAEpD,eAAO,MAAM,cAAc,EAAE,SAAS,QAAQ,EAMpC,CAAC;AAEX,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAEhD;AAED,eAAO,MAAM,UAAU,iNAoBrB,CAAC;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEhD,eAAO,MAAM,cAAc,oFAQzB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAexD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcxB,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAUT;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAEnD;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAGvD"}
|
package/dist/findings.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Finding schema — the single contract every engine produces.
|
|
3
|
+
* Engines do not leak their native shapes; the orchestrator only sees `Finding`.
|
|
4
|
+
*/
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
export const SeverityEnum = z.enum(["info", "low", "medium", "high", "critical"]);
|
|
8
|
+
export const SEVERITY_ORDER = [
|
|
9
|
+
"info",
|
|
10
|
+
"low",
|
|
11
|
+
"medium",
|
|
12
|
+
"high",
|
|
13
|
+
"critical",
|
|
14
|
+
];
|
|
15
|
+
export function severityRank(s) {
|
|
16
|
+
return SEVERITY_ORDER.indexOf(s);
|
|
17
|
+
}
|
|
18
|
+
export const EngineEnum = z.enum([
|
|
19
|
+
// Phase 0
|
|
20
|
+
"treesitter",
|
|
21
|
+
"semgrep",
|
|
22
|
+
"pyright",
|
|
23
|
+
"tsc",
|
|
24
|
+
"eslint",
|
|
25
|
+
"secret-scan",
|
|
26
|
+
"python-sinks",
|
|
27
|
+
"js-sinks",
|
|
28
|
+
"ai-sinks",
|
|
29
|
+
// Phase 1+ (declared so the schema is stable from day 1)
|
|
30
|
+
"scip",
|
|
31
|
+
"joern",
|
|
32
|
+
"embeddings",
|
|
33
|
+
"pysa",
|
|
34
|
+
"racerd",
|
|
35
|
+
"vulrag",
|
|
36
|
+
"router-150m",
|
|
37
|
+
"llm-critic",
|
|
38
|
+
]);
|
|
39
|
+
export const SourceKindEnum = z.enum([
|
|
40
|
+
"pattern", // regex / syntactic match
|
|
41
|
+
"type", // type checker
|
|
42
|
+
"lint", // linter rule
|
|
43
|
+
"dataflow", // taint / data-flow path
|
|
44
|
+
"router", // 150M classifier output
|
|
45
|
+
"critic", // LLM critic verdict
|
|
46
|
+
"ensemble", // multi-source agreement
|
|
47
|
+
]);
|
|
48
|
+
const DataFlowNode = z.object({
|
|
49
|
+
file: z.string(),
|
|
50
|
+
line: z.number().int().positive(),
|
|
51
|
+
label: z.string().max(200),
|
|
52
|
+
});
|
|
53
|
+
const Evidence = z.object({
|
|
54
|
+
snippet: z.string().max(4000).optional(),
|
|
55
|
+
dataflow: z.array(DataFlowNode).max(20).optional(),
|
|
56
|
+
callers: z.array(z.string().max(200)).max(10).optional(),
|
|
57
|
+
related_cves: z.array(z.string().max(40)).max(5).optional(),
|
|
58
|
+
});
|
|
59
|
+
export const FindingSchema = z.object({
|
|
60
|
+
id: z.string().regex(/^[0-9a-f]{16}$/),
|
|
61
|
+
engine: EngineEnum,
|
|
62
|
+
file: z.string().min(1),
|
|
63
|
+
line: z.number().int().positive().optional(),
|
|
64
|
+
col: z.number().int().positive().optional(),
|
|
65
|
+
rule_id: z.string().min(1).max(200),
|
|
66
|
+
cwe: z.string().regex(/^CWE-\d+$/).optional(),
|
|
67
|
+
severity: SeverityEnum,
|
|
68
|
+
message: z.string().min(1).max(2000),
|
|
69
|
+
evidence: Evidence.optional(),
|
|
70
|
+
confidence: z.number().min(0).max(1),
|
|
71
|
+
source: SourceKindEnum,
|
|
72
|
+
remediation: z.string().max(4000).optional(),
|
|
73
|
+
});
|
|
74
|
+
/** Stable 16-hex id from engine+file+line+rule — same input = same id (idempotent). */
|
|
75
|
+
export function makeFindingId(parts) {
|
|
76
|
+
const h = createHash("sha256");
|
|
77
|
+
h.update(parts.engine);
|
|
78
|
+
h.update("\0");
|
|
79
|
+
h.update(parts.file);
|
|
80
|
+
h.update("\0");
|
|
81
|
+
h.update(String(parts.line ?? ""));
|
|
82
|
+
h.update("\0");
|
|
83
|
+
h.update(parts.rule_id);
|
|
84
|
+
return h.digest("hex").slice(0, 16);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Helper: validate a finding (throws on invalid). Engines call this in dev to
|
|
88
|
+
* fail loud; in production we use safeParse + log the violator and drop it.
|
|
89
|
+
*/
|
|
90
|
+
export function validateFinding(f) {
|
|
91
|
+
return FindingSchema.parse(f);
|
|
92
|
+
}
|
|
93
|
+
/** Safe variant: returns null on invalid (engine bug shouldn't kill the gate). */
|
|
94
|
+
export function safeValidate(f) {
|
|
95
|
+
const parsed = FindingSchema.safeParse(f);
|
|
96
|
+
return parsed.success ? parsed.data : null;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=findings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findings.js","sourceRoot":"","sources":["../src/findings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/hooks/claude-code.ts"],"names":[],"mappings":"AA4KA,+CAA+C;AAC/C,wBAAsB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAqD7C"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code hook protocol — stdin/stdout JSON, exit-code controlled.
|
|
3
|
+
*
|
|
4
|
+
* Hook invocation (per Claude Code docs):
|
|
5
|
+
* stdin = JSON {"tool_input": {"file_path": "...", "content": "..."}, ...}
|
|
6
|
+
* stdout = optional advisory text (warnings)
|
|
7
|
+
* exit 0 = allow, exit 2 = block (Claude shows the stderr message to the user
|
|
8
|
+
* and asks the agent to regenerate)
|
|
9
|
+
*
|
|
10
|
+
* This handler:
|
|
11
|
+
* 1. Reads JSON from stdin (no time pressure — Claude waits).
|
|
12
|
+
* 2. Extracts file_path + content from tool_input. Supports both `content`
|
|
13
|
+
* (Write) and `new_string` (Edit) — for Edit, reconstructs the full
|
|
14
|
+
* post-edit file by reading from disk and applying the string swap so
|
|
15
|
+
* syntax/parse engines see a complete file, not a fragment.
|
|
16
|
+
* 3. Calls `runGate`.
|
|
17
|
+
* 4. Writes the remediation prompt to stderr.
|
|
18
|
+
* 5. Exits 0 / 2.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
21
|
+
import { resolve } from "node:path";
|
|
22
|
+
import { runGate } from "../orchestrator.js";
|
|
23
|
+
import { getLogger } from "../util/logger.js";
|
|
24
|
+
const log = getLogger("aegis.hook");
|
|
25
|
+
function readStdinSync() {
|
|
26
|
+
try {
|
|
27
|
+
return readFileSync(0, "utf8");
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
log.warn("stdin read failed", { err: String(err) });
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function parsePayload(raw) {
|
|
35
|
+
if (!raw.trim())
|
|
36
|
+
return {};
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(raw);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
log.warn("hook payload not JSON", { err: String(err), preview: raw.slice(0, 200) });
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Extract the file_path + the FULL post-edit content from a hook payload.
|
|
46
|
+
*
|
|
47
|
+
* For ``Write``: ``tool_input.content`` is the full new file — use as-is.
|
|
48
|
+
* For ``Edit``: ``tool_input.new_string`` is a fragment to swap into an
|
|
49
|
+
* existing file. Read the on-disk file, replace the first occurrence of
|
|
50
|
+
* ``old_string`` with ``new_string`` — that reconstructs what the file
|
|
51
|
+
* WILL look like after the edit. Sending the fragment alone would make
|
|
52
|
+
* syntax/parse engines fire spurious "unbalanced brace" errors.
|
|
53
|
+
*/
|
|
54
|
+
function extractFileAndContent(p, cwd) {
|
|
55
|
+
const ti = p.tool_input ?? p.toolInput ?? {};
|
|
56
|
+
const filePath = ti.file_path ?? ti.filePath ?? ti.path ?? null;
|
|
57
|
+
if (!filePath)
|
|
58
|
+
return { filePath: null, content: null, fullFile: false };
|
|
59
|
+
// Write tool: content is full file.
|
|
60
|
+
if (ti.content !== undefined && ti.content !== null) {
|
|
61
|
+
return { filePath, content: ti.content, fullFile: true };
|
|
62
|
+
}
|
|
63
|
+
// Edit tool: reconstruct full file by applying the swap.
|
|
64
|
+
const newStr = ti.new_string ?? ti.newString ?? null;
|
|
65
|
+
const oldStr = ti.old_string ?? ti.oldString ?? null;
|
|
66
|
+
if (newStr !== null && oldStr !== null) {
|
|
67
|
+
const absPath = resolve(cwd, filePath);
|
|
68
|
+
if (existsSync(absPath)) {
|
|
69
|
+
try {
|
|
70
|
+
const original = readFileSync(absPath, "utf8");
|
|
71
|
+
// Apply the FIRST occurrence (matching the Edit tool's contract).
|
|
72
|
+
const idx = original.indexOf(oldStr);
|
|
73
|
+
if (idx >= 0) {
|
|
74
|
+
const reconstructed = original.slice(0, idx) + newStr + original.slice(idx + oldStr.length);
|
|
75
|
+
return { filePath, content: reconstructed, fullFile: true };
|
|
76
|
+
}
|
|
77
|
+
log.debug("hook: old_string not found in file — falling back to fragment", {
|
|
78
|
+
file: absPath,
|
|
79
|
+
previewLen: oldStr.length,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
log.debug("hook: file read failed — falling back to fragment", {
|
|
84
|
+
file: absPath,
|
|
85
|
+
err: String(err),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Fragment-only fallback. The gate may emit spurious syntax findings,
|
|
90
|
+
// but we don't want to silently allow uninspected content either.
|
|
91
|
+
return { filePath, content: newStr, fullFile: false };
|
|
92
|
+
}
|
|
93
|
+
return { filePath: null, content: null, fullFile: false };
|
|
94
|
+
}
|
|
95
|
+
function formatRemediation(report) {
|
|
96
|
+
const lines = [];
|
|
97
|
+
lines.push(`[aegis] BLOCKED ${report.filePath}`);
|
|
98
|
+
lines.push(` ${report.decision.summary}`);
|
|
99
|
+
const top = report.findings.slice(0, 5);
|
|
100
|
+
for (const f of top) {
|
|
101
|
+
const loc = f.line ? `:${f.line}${f.col ? `:${f.col}` : ""}` : "";
|
|
102
|
+
const cwe = f.cwe ? ` [${f.cwe}]` : "";
|
|
103
|
+
lines.push(` - ${f.engine}/${f.rule_id}${cwe} (${f.severity}, conf=${f.confidence.toFixed(2)}) at ${f.file}${loc}`);
|
|
104
|
+
lines.push(` ${f.message.slice(0, 200)}`);
|
|
105
|
+
if (f.remediation) {
|
|
106
|
+
lines.push(` fix: ${f.remediation.slice(0, 300)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (report.findings.length > top.length) {
|
|
110
|
+
lines.push(` ... and ${report.findings.length - top.length} more.`);
|
|
111
|
+
}
|
|
112
|
+
lines.push("");
|
|
113
|
+
lines.push("Regenerate the file fixing the items above, then save again.");
|
|
114
|
+
return lines.join("\n");
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Drain the telemetry buffer with a short hard cap. The hook runs in the
|
|
118
|
+
* critical path of the agent regenerate loop, so we cap upload latency at
|
|
119
|
+
* 3 s — if the server is slow/unreachable the data stays in-buffer and is
|
|
120
|
+
* picked up on the next process that successfully flushes (the in-memory
|
|
121
|
+
* buffer is per-process, so in practice this means the batch is dropped;
|
|
122
|
+
* acceptable for a single-finding write).
|
|
123
|
+
*/
|
|
124
|
+
async function drainTelemetry() {
|
|
125
|
+
try {
|
|
126
|
+
const { flush } = await import("../telemetry/collector.js");
|
|
127
|
+
await Promise.race([
|
|
128
|
+
flush(),
|
|
129
|
+
new Promise((resolve) => setTimeout(resolve, 3000).unref()),
|
|
130
|
+
]);
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
log.debug("hook telemetry flush failed", { err: String(err) });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** Main entry — call from `bin/aegis hook`. */
|
|
137
|
+
export async function runHook() {
|
|
138
|
+
const raw = readStdinSync();
|
|
139
|
+
const payload = parsePayload(raw);
|
|
140
|
+
const cwd = payload.cwd ?? process.cwd();
|
|
141
|
+
const { filePath, content, fullFile } = extractFileAndContent(payload, cwd);
|
|
142
|
+
if (!filePath || content === null) {
|
|
143
|
+
log.debug("hook: no file_path/content in payload — allowing");
|
|
144
|
+
process.exit(0);
|
|
145
|
+
}
|
|
146
|
+
const projectRoot = payload.project_root ?? payload.projectRoot ?? cwd;
|
|
147
|
+
const absFile = resolve(cwd, filePath);
|
|
148
|
+
let report;
|
|
149
|
+
try {
|
|
150
|
+
report = await runGate({
|
|
151
|
+
filePath: absFile,
|
|
152
|
+
content,
|
|
153
|
+
projectRoot,
|
|
154
|
+
// Containment is OFF for the hook: the agent already chose the
|
|
155
|
+
// file path (cwd is just an opinion, not a security boundary).
|
|
156
|
+
// The hardcoded denylist for ~/.ssh / /etc / etc. STILL applies
|
|
157
|
+
// via safeResolve regardless — that's the actual security gate.
|
|
158
|
+
// Without this, every write to a memory file / dotfile / abs path
|
|
159
|
+
// outside cwd would be blocked with reason="outside_root".
|
|
160
|
+
enforceContainment: false,
|
|
161
|
+
totalTimeoutMs: Number.parseInt(process.env.AEGIS_TOTAL_TIMEOUT_MS ?? "5000", 10),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
// Never block on internal errors — fail OPEN with a warning.
|
|
166
|
+
log.error("hook gate threw", { err: String(err) });
|
|
167
|
+
process.stderr.write(`[aegis] internal error: ${String(err).slice(0, 200)} — allowing\n`);
|
|
168
|
+
process.exit(0);
|
|
169
|
+
}
|
|
170
|
+
if (report.decision.action === "block") {
|
|
171
|
+
process.stderr.write(formatRemediation(report) + "\n");
|
|
172
|
+
if (!fullFile) {
|
|
173
|
+
process.stderr.write(`[aegis] note: validated only the Edit fragment (could not reconstruct full file). ` +
|
|
174
|
+
`Some findings may be spurious — please review before bypassing.\n`);
|
|
175
|
+
}
|
|
176
|
+
await drainTelemetry();
|
|
177
|
+
process.exit(2);
|
|
178
|
+
}
|
|
179
|
+
if (report.decision.action === "warn") {
|
|
180
|
+
process.stderr.write(`[aegis] WARN ${report.filePath}: ${report.decision.summary}\n`);
|
|
181
|
+
await drainTelemetry();
|
|
182
|
+
process.exit(0);
|
|
183
|
+
}
|
|
184
|
+
await drainTelemetry();
|
|
185
|
+
process.exit(0);
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=claude-code.js.map
|