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,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint adapter — uses ESLint as a library (no subprocess), pinned to a small
|
|
3
|
+
* "high-signal for AI-generated code" ruleset.
|
|
4
|
+
*
|
|
5
|
+
* Specifically targets the async-foot-gun bugs that LLMs produce constantly:
|
|
6
|
+
* - no-floating-promises (Promise.then without return/await)
|
|
7
|
+
* - no-misused-promises (Promise in conditions)
|
|
8
|
+
* - require-await
|
|
9
|
+
* - no-unsafe-assignment (TS)
|
|
10
|
+
* - no-unused-vars (catches half-written code)
|
|
11
|
+
*
|
|
12
|
+
* Loads `@typescript-eslint/parser` so it works on .ts/.tsx too.
|
|
13
|
+
*/
|
|
14
|
+
import { basename } from "node:path";
|
|
15
|
+
import { makeFindingId } from "../findings.js";
|
|
16
|
+
import { getLogger } from "../util/logger.js";
|
|
17
|
+
const log = getLogger("aegis.engine.eslint");
|
|
18
|
+
const SUPPORTED = ["javascript", "typescript", "jsx", "tsx"];
|
|
19
|
+
const RULES = {
|
|
20
|
+
// built-in
|
|
21
|
+
"no-unused-vars": "warn",
|
|
22
|
+
"no-undef": "error",
|
|
23
|
+
eqeqeq: "warn",
|
|
24
|
+
"no-eval": "error",
|
|
25
|
+
"no-implied-eval": "error",
|
|
26
|
+
"no-throw-literal": "warn",
|
|
27
|
+
"no-self-compare": "warn",
|
|
28
|
+
"no-constant-condition": "warn",
|
|
29
|
+
"no-unreachable": "warn",
|
|
30
|
+
"no-fallthrough": "warn",
|
|
31
|
+
};
|
|
32
|
+
// TS rules that work WITHOUT type information. The four highest-signal async
|
|
33
|
+
// rules (no-floating-promises, no-misused-promises, no-unsafe-assignment,
|
|
34
|
+
// no-unsafe-call) are type-aware and need a real tsconfig + parserOptions
|
|
35
|
+
// {project: ...}; Phase 1 adds project context and we re-enable them there.
|
|
36
|
+
const TS_RULES = {
|
|
37
|
+
"@typescript-eslint/no-unused-vars": "warn",
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Common globals available in Node + browser + worker + es2024 environments.
|
|
41
|
+
* Without this, `no-undef` floods on every `process`, `setTimeout`, `console`,
|
|
42
|
+
* `Buffer`, etc. — total noise on real Node code.
|
|
43
|
+
*
|
|
44
|
+
* Kept minimal-and-hard-coded to avoid pulling in the 'globals' npm package
|
|
45
|
+
* (which would add another runtime dep for ~20 names).
|
|
46
|
+
*/
|
|
47
|
+
const GLOBALS = {
|
|
48
|
+
// Node + JS runtime
|
|
49
|
+
process: "readonly",
|
|
50
|
+
Buffer: "readonly",
|
|
51
|
+
__dirname: "readonly",
|
|
52
|
+
__filename: "readonly",
|
|
53
|
+
global: "readonly",
|
|
54
|
+
globalThis: "readonly",
|
|
55
|
+
require: "readonly",
|
|
56
|
+
module: "readonly",
|
|
57
|
+
exports: "writable",
|
|
58
|
+
console: "readonly",
|
|
59
|
+
setTimeout: "readonly",
|
|
60
|
+
clearTimeout: "readonly",
|
|
61
|
+
setInterval: "readonly",
|
|
62
|
+
clearInterval: "readonly",
|
|
63
|
+
setImmediate: "readonly",
|
|
64
|
+
clearImmediate: "readonly",
|
|
65
|
+
queueMicrotask: "readonly",
|
|
66
|
+
URL: "readonly",
|
|
67
|
+
URLSearchParams: "readonly",
|
|
68
|
+
TextEncoder: "readonly",
|
|
69
|
+
TextDecoder: "readonly",
|
|
70
|
+
AbortController: "readonly",
|
|
71
|
+
AbortSignal: "readonly",
|
|
72
|
+
fetch: "readonly",
|
|
73
|
+
Headers: "readonly",
|
|
74
|
+
Request: "readonly",
|
|
75
|
+
Response: "readonly",
|
|
76
|
+
performance: "readonly",
|
|
77
|
+
structuredClone: "readonly",
|
|
78
|
+
// Browser (common — included so we don't false-positive on isomorphic code)
|
|
79
|
+
window: "readonly",
|
|
80
|
+
document: "readonly",
|
|
81
|
+
localStorage: "readonly",
|
|
82
|
+
sessionStorage: "readonly",
|
|
83
|
+
navigator: "readonly",
|
|
84
|
+
// Worker
|
|
85
|
+
self: "readonly",
|
|
86
|
+
};
|
|
87
|
+
function langToParser(lang) {
|
|
88
|
+
return lang === "typescript" || lang === "tsx"
|
|
89
|
+
? "@typescript-eslint/parser"
|
|
90
|
+
: "espree";
|
|
91
|
+
}
|
|
92
|
+
function sevMap(s) {
|
|
93
|
+
return s === 2 ? "high" : "medium";
|
|
94
|
+
}
|
|
95
|
+
function confFor(s) {
|
|
96
|
+
return s === 2 ? 0.85 : 0.7;
|
|
97
|
+
}
|
|
98
|
+
let _Linter = null;
|
|
99
|
+
let _tsParser = null;
|
|
100
|
+
let _tsPlugin = null;
|
|
101
|
+
let _attempted = false;
|
|
102
|
+
/**
|
|
103
|
+
* The TS-eslint packages publish a CJS index that, when imported as ESM,
|
|
104
|
+
* wraps the real exports under `.default`. Pick the right shape so flat
|
|
105
|
+
* config sees a real ``plugin.rules`` / parser.parseForESLint``.
|
|
106
|
+
*/
|
|
107
|
+
function unwrapDefault(mod) {
|
|
108
|
+
if (mod &&
|
|
109
|
+
typeof mod === "object" &&
|
|
110
|
+
"default" in mod) {
|
|
111
|
+
return mod.default;
|
|
112
|
+
}
|
|
113
|
+
return mod;
|
|
114
|
+
}
|
|
115
|
+
async function tryLoad() {
|
|
116
|
+
if (_attempted) {
|
|
117
|
+
if (_Linter)
|
|
118
|
+
return { Linter: _Linter, tsParser: _tsParser, tsPlugin: _tsPlugin };
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
_attempted = true;
|
|
122
|
+
try {
|
|
123
|
+
const { Linter } = await import("eslint");
|
|
124
|
+
_Linter = Linter;
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
log.warn("eslint not installed", { err: String(err) });
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const mod = await import("@typescript-eslint/parser");
|
|
132
|
+
_tsParser = unwrapDefault(mod);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
_tsParser = null;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const mod = await import("@typescript-eslint/eslint-plugin");
|
|
139
|
+
_tsPlugin = unwrapDefault(mod);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
_tsPlugin = null;
|
|
143
|
+
}
|
|
144
|
+
return { Linter: _Linter, tsParser: _tsParser, tsPlugin: _tsPlugin };
|
|
145
|
+
}
|
|
146
|
+
export class ESLintEngine {
|
|
147
|
+
name = "eslint";
|
|
148
|
+
languages = SUPPORTED;
|
|
149
|
+
async available() {
|
|
150
|
+
const loaded = await tryLoad();
|
|
151
|
+
return loaded !== null;
|
|
152
|
+
}
|
|
153
|
+
async run(input) {
|
|
154
|
+
const t0 = Date.now();
|
|
155
|
+
const loaded = await tryLoad();
|
|
156
|
+
if (!loaded) {
|
|
157
|
+
return {
|
|
158
|
+
engine: this.name,
|
|
159
|
+
findings: [],
|
|
160
|
+
unavailable: true,
|
|
161
|
+
durationMs: Date.now() - t0,
|
|
162
|
+
reason: "eslint_not_installed",
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const { Linter, tsParser, tsPlugin } = loaded;
|
|
166
|
+
const isTs = input.lang === "typescript" || input.lang === "tsx";
|
|
167
|
+
const parser = langToParser(input.lang);
|
|
168
|
+
// The new flat-config style: pass `config` to `verify` directly.
|
|
169
|
+
const rules = { ...RULES };
|
|
170
|
+
if (isTs && tsPlugin) {
|
|
171
|
+
Object.assign(rules, TS_RULES);
|
|
172
|
+
}
|
|
173
|
+
const linter = new Linter({ configType: "flat" });
|
|
174
|
+
// files glob is REQUIRED for ESLint 9 flat config when the filename is an
|
|
175
|
+
// absolute path outside cwd — without it, verify() returns
|
|
176
|
+
// "No matching configuration found for <path>".
|
|
177
|
+
const config = [
|
|
178
|
+
{
|
|
179
|
+
files: ["**/*.{js,jsx,mjs,cjs,ts,tsx}"],
|
|
180
|
+
languageOptions: {
|
|
181
|
+
ecmaVersion: "latest",
|
|
182
|
+
sourceType: "module",
|
|
183
|
+
globals: GLOBALS,
|
|
184
|
+
...(isTs && tsParser ? { parser: tsParser } : {}),
|
|
185
|
+
...(input.lang === "jsx" || input.lang === "tsx"
|
|
186
|
+
? { parserOptions: { ecmaFeatures: { jsx: true } } }
|
|
187
|
+
: {}),
|
|
188
|
+
},
|
|
189
|
+
...(isTs && tsPlugin
|
|
190
|
+
? { plugins: { "@typescript-eslint": tsPlugin } }
|
|
191
|
+
: {}),
|
|
192
|
+
rules,
|
|
193
|
+
},
|
|
194
|
+
];
|
|
195
|
+
let messages = [];
|
|
196
|
+
try {
|
|
197
|
+
// Pass just the basename — ESLint flat-config glob matching is relative
|
|
198
|
+
// to cwd, so an absolute path outside cwd never matches `**/*.js` and
|
|
199
|
+
// returns "No matching configuration found".
|
|
200
|
+
const filenameForVerify = basename(input.filePath);
|
|
201
|
+
messages = linter.verify(input.content, config, {
|
|
202
|
+
filename: filenameForVerify,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
log.debug("verify threw", { err: String(err) });
|
|
207
|
+
return {
|
|
208
|
+
engine: this.name,
|
|
209
|
+
findings: [],
|
|
210
|
+
unavailable: true,
|
|
211
|
+
durationMs: Date.now() - t0,
|
|
212
|
+
reason: `verify_threw: ${String(err).slice(0, 100)}`,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const findings = [];
|
|
216
|
+
for (const m of messages) {
|
|
217
|
+
const rule = m.ruleId ?? "syntax_error";
|
|
218
|
+
const sev = sevMap(m.severity);
|
|
219
|
+
findings.push({
|
|
220
|
+
id: makeFindingId({
|
|
221
|
+
engine: this.name,
|
|
222
|
+
file: input.filePath,
|
|
223
|
+
line: m.line,
|
|
224
|
+
rule_id: rule,
|
|
225
|
+
}),
|
|
226
|
+
engine: "eslint",
|
|
227
|
+
file: input.filePath,
|
|
228
|
+
line: m.line,
|
|
229
|
+
col: m.column,
|
|
230
|
+
rule_id: rule,
|
|
231
|
+
severity: sev,
|
|
232
|
+
message: m.message.slice(0, 1900),
|
|
233
|
+
confidence: confFor(m.severity),
|
|
234
|
+
source: "lint",
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
engine: this.name,
|
|
239
|
+
findings,
|
|
240
|
+
unavailable: false,
|
|
241
|
+
durationMs: Date.now() - t0,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=eslint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eslint.js","sourceRoot":"","sources":["../../src/engines/eslint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"joern.d.ts","sourceRoot":"","sources":["../../src/engines/joern.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,MAAM,EAAmC,MAAM,YAAY,CAAC;AAgG1E,eAAO,MAAM,WAAW,EAAE,MAA0B,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Joern CPG engine adapter — reads pre-computed findings from cache.
|
|
3
|
+
*
|
|
4
|
+
* The actual CPG build + CPGQL query is in ``src/index/joern.ts``. This
|
|
5
|
+
* module is the realtime fast path: it reads the per-project
|
|
6
|
+
* ``findings.jsonl`` (written by ``aegis index --joern``), filters to the
|
|
7
|
+
* file currently under review, and returns a ``Finding[]``.
|
|
8
|
+
*
|
|
9
|
+
* If the cache is missing the engine reports ``unavailable: true`` with a
|
|
10
|
+
* reason that tells the user how to build it.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync, statSync } from "node:fs";
|
|
13
|
+
import { findJoernDir, findJoernJdk, joernFindingsPath, joernInfoPath, readJoernFindings, } from "../index/joern.js";
|
|
14
|
+
import { getLogger } from "../util/logger.js";
|
|
15
|
+
const log = getLogger("aegis.engine.joern");
|
|
16
|
+
/** Stale-cache warning threshold: if the cached findings.jsonl is older
|
|
17
|
+
* than this, we log a warn so users know to re-run ``aegis index --joern``.
|
|
18
|
+
* Doesn't change correctness — they still get the cached findings. */
|
|
19
|
+
const STALE_WARN_AGE_MS = 24 * 60 * 60 * 1000; // 24 h
|
|
20
|
+
class JoernEngine {
|
|
21
|
+
name = "joern";
|
|
22
|
+
languages = "all";
|
|
23
|
+
availabilityCache = null;
|
|
24
|
+
static AVAILABILITY_TTL_MS = 60_000;
|
|
25
|
+
async available() {
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
if (this.availabilityCache &&
|
|
28
|
+
now - this.availabilityCache.checkedAt < JoernEngine.AVAILABILITY_TTL_MS) {
|
|
29
|
+
return this.availabilityCache.value;
|
|
30
|
+
}
|
|
31
|
+
// Engine is "available" iff joern + JDK 11..21 are installed. Absence
|
|
32
|
+
// of a cache for the current project is NOT unavailability — it just
|
|
33
|
+
// means run() will report "no cache, please run `aegis index --joern`".
|
|
34
|
+
const ok = findJoernDir() !== null && findJoernJdk() !== null;
|
|
35
|
+
this.availabilityCache = { value: ok, checkedAt: now };
|
|
36
|
+
return ok;
|
|
37
|
+
}
|
|
38
|
+
async run(input) {
|
|
39
|
+
const t0 = Date.now();
|
|
40
|
+
if (!input.projectRoot) {
|
|
41
|
+
return {
|
|
42
|
+
engine: this.name,
|
|
43
|
+
findings: [],
|
|
44
|
+
unavailable: false,
|
|
45
|
+
durationMs: Date.now() - t0,
|
|
46
|
+
reason: "no_project_root",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const findingsPath = joernFindingsPath(input.projectRoot);
|
|
50
|
+
const infoPath = joernInfoPath(input.projectRoot);
|
|
51
|
+
if (!existsSync(findingsPath) || !existsSync(infoPath)) {
|
|
52
|
+
return {
|
|
53
|
+
engine: this.name,
|
|
54
|
+
findings: [],
|
|
55
|
+
unavailable: false,
|
|
56
|
+
durationMs: Date.now() - t0,
|
|
57
|
+
reason: "no_cpg_cache — run `aegis index --joern` to build",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Optional stale check — warn but still serve.
|
|
61
|
+
try {
|
|
62
|
+
const age = Date.now() - statSync(findingsPath).mtimeMs;
|
|
63
|
+
if (age > STALE_WARN_AGE_MS) {
|
|
64
|
+
log.debug("joern findings cache is stale", { age_ms: age, path: findingsPath });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// ignore
|
|
69
|
+
}
|
|
70
|
+
const all = readJoernFindings(input.projectRoot);
|
|
71
|
+
// Findings carry POSIX-relative paths. Compare against the file's relative
|
|
72
|
+
// path (relative to projectRoot). On Windows the input.filePath may use
|
|
73
|
+
// backslashes; normalize before compare.
|
|
74
|
+
const relPath = toRelPosix(input.filePath, input.projectRoot);
|
|
75
|
+
// Also match by basename — sometimes Joern emits a path that differs from
|
|
76
|
+
// ours by leading drive-letter or symlink resolution. Basename match is a
|
|
77
|
+
// safe fallback that won't cross-contaminate (same basename in two dirs
|
|
78
|
+
// is rare in real projects).
|
|
79
|
+
const baseName = relPath.split("/").pop() ?? relPath;
|
|
80
|
+
const fileFindings = all.filter((f) => f.file === relPath || f.file === baseName || f.file.endsWith("/" + baseName));
|
|
81
|
+
return {
|
|
82
|
+
engine: this.name,
|
|
83
|
+
findings: fileFindings,
|
|
84
|
+
unavailable: false,
|
|
85
|
+
durationMs: Date.now() - t0,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function toRelPosix(filePath, projectRoot) {
|
|
90
|
+
const fp = filePath.replace(/\\/g, "/");
|
|
91
|
+
const root = projectRoot.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
92
|
+
if (fp.toLowerCase().startsWith(root.toLowerCase() + "/")) {
|
|
93
|
+
return fp.slice(root.length + 1);
|
|
94
|
+
}
|
|
95
|
+
return fp;
|
|
96
|
+
}
|
|
97
|
+
export const joernEngine = new JoernEngine();
|
|
98
|
+
//# sourceMappingURL=joern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"joern.js","sourceRoot":"","sources":["../../src/engines/joern.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic JavaScript / TypeScript dangerous-sinks scanner — no external binary.
|
|
3
|
+
*
|
|
4
|
+
* Covers the textbook patterns every JS/Node security linter flags:
|
|
5
|
+
*
|
|
6
|
+
* Code execution / RCE (CWE-95)
|
|
7
|
+
* - eval(<non-literal>)
|
|
8
|
+
* - new Function(<...>)
|
|
9
|
+
* - setTimeout/setInterval(<string>, ...)
|
|
10
|
+
* XSS (CWE-79)
|
|
11
|
+
* - element.innerHTML / outerHTML = <non-literal>
|
|
12
|
+
* - document.write / writeln(<non-literal>)
|
|
13
|
+
* - React dangerouslySetInnerHTML
|
|
14
|
+
* Command injection (CWE-78)
|
|
15
|
+
* - child_process.exec / execSync(<non-literal>)
|
|
16
|
+
* - child_process.spawn / spawnSync({ shell: true })
|
|
17
|
+
* Path traversal (CWE-22)
|
|
18
|
+
* - fs.{read,write,unlink}File(req.params.x)
|
|
19
|
+
* SSRF (CWE-918)
|
|
20
|
+
* - axios/fetch/got/http.get(req.params.x)
|
|
21
|
+
* Weak crypto (CWE-327 / CWE-338)
|
|
22
|
+
* - crypto.createHash("md5"|"sha1")
|
|
23
|
+
* - Math.random() in security-keyword context
|
|
24
|
+
* JWT misuse (CWE-347 / CWE-798)
|
|
25
|
+
* - jwt.verify(token, secret, { algorithms: ["none"] })
|
|
26
|
+
* - jwt.sign(payload, "<short-hardcoded-secret>")
|
|
27
|
+
* - jwt.verify without algorithms allowlist
|
|
28
|
+
* Open redirect (CWE-601)
|
|
29
|
+
* - res.redirect(req.query.x | req.body.x)
|
|
30
|
+
* CORS misconfig (CWE-942)
|
|
31
|
+
* - Access-Control-Allow-Origin: * AND Access-Control-Allow-Credentials: true
|
|
32
|
+
* Prototype pollution (CWE-1321)
|
|
33
|
+
* - lodash _.merge/set/setWith/defaultsDeep with req.body
|
|
34
|
+
* - Object.assign(prototype, ...)
|
|
35
|
+
*
|
|
36
|
+
* Same architecture as python-sinks: per-rule regex + isDangerous predicate
|
|
37
|
+
* + optional fileGate; severity HIGH or CRITICAL, confidence 0.75; routes
|
|
38
|
+
* the LLM critic for refinement.
|
|
39
|
+
*/
|
|
40
|
+
import { Engine, EngineRunInput, EngineRunResult } from "./types.js";
|
|
41
|
+
import { Lang } from "../lang.js";
|
|
42
|
+
interface SinkRule {
|
|
43
|
+
id: string;
|
|
44
|
+
cwe: string;
|
|
45
|
+
severity: "high" | "critical";
|
|
46
|
+
what: string;
|
|
47
|
+
pattern: RegExp;
|
|
48
|
+
isDangerous: (firstArg: string, allArgs: string, fullContent?: string) => boolean;
|
|
49
|
+
fileGate?: (fullContent: string) => boolean;
|
|
50
|
+
remediation: string;
|
|
51
|
+
}
|
|
52
|
+
/** A JS arg "looks tainted" if it is NOT a pure string/number literal. */
|
|
53
|
+
declare function looksTainted(arg: string): boolean;
|
|
54
|
+
/** First positional arg from a parenthesized call body. */
|
|
55
|
+
declare function firstPositionalArg(body: string): string;
|
|
56
|
+
declare function containsTaintSource(s: string): boolean;
|
|
57
|
+
export declare class JsSinksEngine implements Engine {
|
|
58
|
+
readonly name: "js-sinks";
|
|
59
|
+
readonly languages: ReadonlyArray<Lang>;
|
|
60
|
+
available(): Promise<boolean>;
|
|
61
|
+
run(input: EngineRunInput): Promise<EngineRunResult>;
|
|
62
|
+
}
|
|
63
|
+
export declare const _testing: {
|
|
64
|
+
RULES: readonly SinkRule[];
|
|
65
|
+
looksTainted: typeof looksTainted;
|
|
66
|
+
firstPositionalArg: typeof firstPositionalArg;
|
|
67
|
+
containsTaintSource: typeof containsTaintSource;
|
|
68
|
+
};
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=js-sinks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"js-sinks.d.ts","sourceRoot":"","sources":["../../src/engines/js-sinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAErE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGlC,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IAClF,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,0EAA0E;AAC1E,iBAAS,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAgB1C;AAED,2DAA2D;AAC3D,iBAAS,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAkBhD;AAOD,iBAAS,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAE/C;AA0RD,qBAAa,aAAc,YAAW,MAAM;IAC1C,QAAQ,CAAC,IAAI,EAAG,UAAU,CAAU;IACpC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,IAAI,CAAC,CAAgC;IAEjE,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAI7B,GAAG,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CA6D3D;AAED,eAAO,MAAM,QAAQ;;;;;CAKpB,CAAC"}
|