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,617 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pysa (Pyre Security Analyzer) — Python taint analysis via WSL.
|
|
3
|
+
*
|
|
4
|
+
* Pysa is Meta's interprocedural data-flow / taint analyzer for Python.
|
|
5
|
+
* Per the research (Du 2024 Vul-RAG, Macroscope 2026) it's the best-in-class
|
|
6
|
+
* OSS path to PROVING CWE-89 / 78 / 79 / 918 etc. via source→sink reachability —
|
|
7
|
+
* not heuristic pattern matching but actual dataflow.
|
|
8
|
+
*
|
|
9
|
+
* **Why this module exists on Windows:**
|
|
10
|
+
* Pyre is OCaml-based; the official wheels are Linux + macOS only. Per the
|
|
11
|
+
* Pyre docs: *"On Windows we have successfully gotten pyre to work through
|
|
12
|
+
* WSL, but do not officially support it."* So we bridge: aegis-v2 runs on
|
|
13
|
+
* Windows, Pyre runs inside WSL Ubuntu, paths are translated `C:\foo` →
|
|
14
|
+
* `/mnt/c/foo`. On Linux/macOS hosts the bridge layer is bypassed and we
|
|
15
|
+
* call Pyre directly.
|
|
16
|
+
*
|
|
17
|
+
* **Architecture: batch + cache.**
|
|
18
|
+
* Mirrors `src/index/joern.ts`. `aegis index --pysa` builds taint summaries
|
|
19
|
+
* (slow, 30s-2min depending on project size) + writes `findings.jsonl` to
|
|
20
|
+
* `~/.aegis/pysa/<sha256(root)[:16]>/`. The `PysaEngine` reads that cache
|
|
21
|
+
* at gate-time in milliseconds. Re-run `aegis index --pysa` after big
|
|
22
|
+
* changes; small per-file edits don't require a rebuild.
|
|
23
|
+
*
|
|
24
|
+
* **Auto-bootstrap.**
|
|
25
|
+
* Pysa needs a `.pyre_configuration` file in the project root (Pyre's
|
|
26
|
+
* config) AND a `taint.config` file with source/sink/sanitizer definitions.
|
|
27
|
+
* If absent we write a minimal default `.pyre_configuration` to a SCRATCH
|
|
28
|
+
* directory under the cache (NEVER into the user's project — that would
|
|
29
|
+
* silently mutate their repo). We point Pyre at the project via that
|
|
30
|
+
* scratch dir's `source_directories` list.
|
|
31
|
+
*
|
|
32
|
+
* **Env overrides:**
|
|
33
|
+
* AEGIS_PYSA_WSL_DISTRO — defaults to "Ubuntu"
|
|
34
|
+
* AEGIS_PYSA_PYRE_BIN — explicit path to pyre inside WSL
|
|
35
|
+
* (default: ~/pyre-venv/bin/pyre)
|
|
36
|
+
* AEGIS_PYSA_TIMEOUT_MS — analyze timeout (default 600_000 = 10 min)
|
|
37
|
+
* AEGIS_PYSA_FORCE_LINUX — set when running on Linux/macOS to skip the
|
|
38
|
+
* WSL bridge entirely
|
|
39
|
+
*/
|
|
40
|
+
import { createHash } from "node:crypto";
|
|
41
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
42
|
+
import { homedir } from "node:os";
|
|
43
|
+
import { join, resolve as resolvePath } from "node:path";
|
|
44
|
+
import { FindingSchema, makeFindingId } from "../findings.js";
|
|
45
|
+
import { getLogger } from "../util/logger.js";
|
|
46
|
+
import { run as spawnRun } from "../util/subprocess.js";
|
|
47
|
+
const log = getLogger("aegis.pysa");
|
|
48
|
+
// Bump on any taint.config / output-shape change to invalidate cached findings.
|
|
49
|
+
const PYSA_TAINT_CONFIG_VERSION = "v1";
|
|
50
|
+
const DEFAULT_PYSA_TIMEOUT_MS = 10 * 60 * 1000;
|
|
51
|
+
const DEFAULT_WSL_DISTRO = "Ubuntu";
|
|
52
|
+
const DEFAULT_PYRE_BIN = "~/pyre-venv/bin/pyre";
|
|
53
|
+
// ── Discovery: WSL and Pyre ──────────────────────────────────────────────
|
|
54
|
+
function expandHome(p) {
|
|
55
|
+
if (!p.startsWith("~"))
|
|
56
|
+
return p;
|
|
57
|
+
return resolvePath(homedir(), p.slice(2));
|
|
58
|
+
}
|
|
59
|
+
/** Locate wsl.exe on Windows. Returns null on non-Windows OR when WSL
|
|
60
|
+
* is not installed. Does NOT validate that a distro is present — that
|
|
61
|
+
* check belongs to ``probePyreInWsl``. */
|
|
62
|
+
export function findWsl() {
|
|
63
|
+
if (process.platform !== "win32")
|
|
64
|
+
return null;
|
|
65
|
+
if (process.env.AEGIS_PYSA_FORCE_LINUX === "1")
|
|
66
|
+
return null;
|
|
67
|
+
const candidates = [
|
|
68
|
+
process.env.AEGIS_PYSA_WSL_PATH,
|
|
69
|
+
`${process.env.SystemRoot ?? "C:\\Windows"}\\System32\\wsl.exe`,
|
|
70
|
+
"C:\\Windows\\System32\\wsl.exe",
|
|
71
|
+
].filter((p) => typeof p === "string" && p.length > 0);
|
|
72
|
+
for (const c of candidates) {
|
|
73
|
+
if (existsSync(c)) {
|
|
74
|
+
return { wslExe: c, distro: process.env.AEGIS_PYSA_WSL_DISTRO ?? DEFAULT_WSL_DISTRO };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
/** Best-effort probe: does pyre exist + run? Cached per-process to avoid
|
|
80
|
+
* paying the ~1-2 s wsl-roundtrip on every engine.available() call. */
|
|
81
|
+
let _pyreProbeCache;
|
|
82
|
+
const PYRE_PROBE_TTL_MS = 60_000;
|
|
83
|
+
let _pyreProbeAt = 0;
|
|
84
|
+
export async function probePyre() {
|
|
85
|
+
const now = Date.now();
|
|
86
|
+
if (_pyreProbeCache !== undefined && now - _pyreProbeAt < PYRE_PROBE_TTL_MS) {
|
|
87
|
+
return _pyreProbeCache;
|
|
88
|
+
}
|
|
89
|
+
_pyreProbeAt = now;
|
|
90
|
+
const pyreBin = process.env.AEGIS_PYSA_PYRE_BIN ?? DEFAULT_PYRE_BIN;
|
|
91
|
+
if (process.platform === "win32" && process.env.AEGIS_PYSA_FORCE_LINUX !== "1") {
|
|
92
|
+
const wsl = findWsl();
|
|
93
|
+
if (!wsl) {
|
|
94
|
+
_pyreProbeCache = null;
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const cmd = `${pyreBin} --version 2>/dev/null || echo NOT_FOUND`;
|
|
98
|
+
const res = await spawnRun(wsl.wslExe, {
|
|
99
|
+
args: ["-d", wsl.distro, "--", "bash", "-c", cmd],
|
|
100
|
+
timeoutMs: 15_000,
|
|
101
|
+
});
|
|
102
|
+
if (res.exitCode !== 0) {
|
|
103
|
+
log.debug("WSL pyre probe failed", { rc: res.exitCode, stderr: res.stderr.slice(0, 200) });
|
|
104
|
+
_pyreProbeCache = null;
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
// Pyre's --version prints e.g. "Client version: 0.9.25" — does NOT
|
|
108
|
+
// contain the word "pyre" and doesn't start with a digit. Match
|
|
109
|
+
// anything that has "version" + a semver-shaped token, OR a bare
|
|
110
|
+
// semver line, OR a line containing "pyre".
|
|
111
|
+
const versionLine = res.stdout.split(/\r?\n/).find((l) => {
|
|
112
|
+
const t = l.trim();
|
|
113
|
+
if (!t)
|
|
114
|
+
return false;
|
|
115
|
+
if (t.includes("NOT_FOUND"))
|
|
116
|
+
return false;
|
|
117
|
+
if (t.toLowerCase().includes("pyre"))
|
|
118
|
+
return true;
|
|
119
|
+
if (/^\d+\.\d+/.test(t))
|
|
120
|
+
return true;
|
|
121
|
+
if (/version[:\s]+\d+\.\d+/i.test(t))
|
|
122
|
+
return true;
|
|
123
|
+
return false;
|
|
124
|
+
});
|
|
125
|
+
if (!versionLine || versionLine.includes("NOT_FOUND")) {
|
|
126
|
+
_pyreProbeCache = null;
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const probe = {
|
|
130
|
+
ok: true,
|
|
131
|
+
pyreBin,
|
|
132
|
+
version: versionLine.trim(),
|
|
133
|
+
usesWsl: true,
|
|
134
|
+
};
|
|
135
|
+
_pyreProbeCache = probe;
|
|
136
|
+
return probe;
|
|
137
|
+
}
|
|
138
|
+
// Linux/macOS native — invoke pyre directly.
|
|
139
|
+
const expanded = expandHome(pyreBin);
|
|
140
|
+
const res = await spawnRun(expanded, { args: ["--version"], timeoutMs: 8_000 });
|
|
141
|
+
if (res.exitCode !== 0) {
|
|
142
|
+
_pyreProbeCache = null;
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
_pyreProbeCache = {
|
|
146
|
+
ok: true,
|
|
147
|
+
pyreBin: expanded,
|
|
148
|
+
version: res.stdout.trim() || res.stderr.trim() || "unknown",
|
|
149
|
+
usesWsl: false,
|
|
150
|
+
};
|
|
151
|
+
return _pyreProbeCache;
|
|
152
|
+
}
|
|
153
|
+
/** Test-only probe-cache reset. */
|
|
154
|
+
export function _resetPyreProbeCacheForTests() {
|
|
155
|
+
_pyreProbeCache = undefined;
|
|
156
|
+
_pyreProbeAt = 0;
|
|
157
|
+
}
|
|
158
|
+
// ── Cache paths ──────────────────────────────────────────────────────────
|
|
159
|
+
function aegisHome() {
|
|
160
|
+
return process.env.AEGIS_HOME ?? resolvePath(homedir(), ".aegis");
|
|
161
|
+
}
|
|
162
|
+
function cacheDirFor(projectRoot) {
|
|
163
|
+
const h = createHash("sha256").update(resolvePath(projectRoot)).digest("hex").slice(0, 16);
|
|
164
|
+
return join(aegisHome(), "pysa", h);
|
|
165
|
+
}
|
|
166
|
+
export function pysaFindingsPath(projectRoot) {
|
|
167
|
+
return join(cacheDirFor(projectRoot), "findings.jsonl");
|
|
168
|
+
}
|
|
169
|
+
export function pysaInfoPath(projectRoot) {
|
|
170
|
+
return join(cacheDirFor(projectRoot), "info.json");
|
|
171
|
+
}
|
|
172
|
+
/** Read cached findings. Used by ``PysaEngine.run``. */
|
|
173
|
+
export function readPysaFindings(projectRoot) {
|
|
174
|
+
const p = pysaFindingsPath(projectRoot);
|
|
175
|
+
if (!existsSync(p))
|
|
176
|
+
return [];
|
|
177
|
+
try {
|
|
178
|
+
const out = [];
|
|
179
|
+
for (const line of readFileSync(p, "utf8").split(/\r?\n/)) {
|
|
180
|
+
if (!line.trim())
|
|
181
|
+
continue;
|
|
182
|
+
const parsed = FindingSchema.safeParse(JSON.parse(line));
|
|
183
|
+
if (parsed.success)
|
|
184
|
+
out.push(parsed.data);
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
log.warn("pysa findings cache unreadable", { err: String(err) });
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// ── Path translation: Win → WSL ──────────────────────────────────────────
|
|
194
|
+
/** Convert a Windows path (``C:\Users\foo``) to a WSL POSIX path
|
|
195
|
+
* (``/mnt/c/Users/foo``). Idempotent on already-POSIX paths. */
|
|
196
|
+
export function winToWslPath(p) {
|
|
197
|
+
if (p.startsWith("/"))
|
|
198
|
+
return p; // already POSIX
|
|
199
|
+
// C:\Users\foo → /mnt/c/Users/foo
|
|
200
|
+
const m = p.match(/^([A-Za-z]):[\\/](.*)$/);
|
|
201
|
+
if (!m)
|
|
202
|
+
return p.replace(/\\/g, "/");
|
|
203
|
+
const drive = m[1].toLowerCase();
|
|
204
|
+
const rest = m[2].replace(/\\/g, "/");
|
|
205
|
+
return `/mnt/${drive}/${rest}`;
|
|
206
|
+
}
|
|
207
|
+
// ── Bootstrap config ─────────────────────────────────────────────────────
|
|
208
|
+
/** Minimal Pyre configuration. We write it to a SCRATCH directory inside
|
|
209
|
+
* the cache (never into the user's repo) and point ``source_directories``
|
|
210
|
+
* at the actual project.
|
|
211
|
+
*
|
|
212
|
+
* ``excludes`` is a list of regex patterns Pyre uses to skip files. We
|
|
213
|
+
* exclude the obvious noise (node_modules/, .venv, __pycache__, dist/,
|
|
214
|
+
* build/, .git/, .aegis/) so Pysa focuses on the user's source. Without
|
|
215
|
+
* this, Pyre defaults to walking everything and the scan either takes
|
|
216
|
+
* 10x longer OR produces only findings from third-party packages. */
|
|
217
|
+
function bootstrapPyreConfig(projectRootWsl) {
|
|
218
|
+
return JSON.stringify({
|
|
219
|
+
source_directories: [projectRootWsl],
|
|
220
|
+
strict: false,
|
|
221
|
+
taint_models_path: ["taint_models"],
|
|
222
|
+
excludes: [
|
|
223
|
+
".*/node_modules/.*",
|
|
224
|
+
".*/\\.venv/.*",
|
|
225
|
+
".*/venv/.*",
|
|
226
|
+
".*/__pycache__/.*",
|
|
227
|
+
".*/\\.git/.*",
|
|
228
|
+
".*/\\.aegis/.*",
|
|
229
|
+
".*/dist/.*",
|
|
230
|
+
".*/build/.*",
|
|
231
|
+
".*/\\.pyre/.*",
|
|
232
|
+
".*/\\.next/.*",
|
|
233
|
+
],
|
|
234
|
+
}, null, 2);
|
|
235
|
+
}
|
|
236
|
+
/** Minimal Pysa taint config. Defines a small but useful set of sources
|
|
237
|
+
* (Flask/Django/FastAPI request inputs, env vars, file reads) and sinks
|
|
238
|
+
* (SQL execute, OS command, eval/exec, file write, HTTP outbound). Real
|
|
239
|
+
* production configs are much richer — this is the seed that catches the
|
|
240
|
+
* most common CWE-78/89/79/918 patterns Pysa is famous for. */
|
|
241
|
+
function bootstrapTaintConfig() {
|
|
242
|
+
return JSON.stringify({
|
|
243
|
+
sources: [
|
|
244
|
+
{ name: "UserControlled", comment: "Direct user-controllable input." },
|
|
245
|
+
{ name: "Cookies", comment: "HTTP cookie value." },
|
|
246
|
+
{ name: "Headers", comment: "HTTP header value." },
|
|
247
|
+
],
|
|
248
|
+
sinks: [
|
|
249
|
+
{ name: "SQL", comment: "Executed as SQL." },
|
|
250
|
+
{ name: "RemoteCodeExecution", comment: "Eval/exec/etc." },
|
|
251
|
+
{ name: "Command", comment: "Shell command." },
|
|
252
|
+
{ name: "FileWrite", comment: "Filesystem write." },
|
|
253
|
+
{ name: "RequestSend", comment: "Outbound HTTP." },
|
|
254
|
+
],
|
|
255
|
+
features: [],
|
|
256
|
+
rules: [
|
|
257
|
+
{
|
|
258
|
+
name: "UserControlled to SQL",
|
|
259
|
+
code: 5001,
|
|
260
|
+
sources: ["UserControlled", "Cookies", "Headers"],
|
|
261
|
+
sinks: ["SQL"],
|
|
262
|
+
message_format: "User-controlled data flows into SQL query (CWE-89).",
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: "UserControlled to RCE",
|
|
266
|
+
code: 5002,
|
|
267
|
+
sources: ["UserControlled", "Cookies", "Headers"],
|
|
268
|
+
sinks: ["RemoteCodeExecution"],
|
|
269
|
+
message_format: "User-controlled data reaches eval/exec (CWE-94/95).",
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: "UserControlled to Command",
|
|
273
|
+
code: 5003,
|
|
274
|
+
sources: ["UserControlled", "Cookies", "Headers"],
|
|
275
|
+
sinks: ["Command"],
|
|
276
|
+
message_format: "User-controlled data reaches OS command (CWE-78).",
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: "UserControlled to RequestSend",
|
|
280
|
+
code: 5004,
|
|
281
|
+
sources: ["UserControlled", "Cookies", "Headers"],
|
|
282
|
+
sinks: ["RequestSend"],
|
|
283
|
+
message_format: "User-controlled data flows into outbound HTTP (CWE-918 SSRF).",
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
}, null, 2);
|
|
287
|
+
}
|
|
288
|
+
/** Minimal stub models — Pysa needs explicit source/sink annotations on
|
|
289
|
+
* common library calls. This is a tiny seed (Flask/FastAPI/sqlite3/os) —
|
|
290
|
+
* real configs ship hundreds. Enough to make the rules above fire on
|
|
291
|
+
* canonical patterns. */
|
|
292
|
+
function bootstrapTaintModels() {
|
|
293
|
+
// CRITICAL: Pysa model signatures must EXACTLY match the implementation
|
|
294
|
+
// signature in typeshed. The `__` prefix marks positional-only — required
|
|
295
|
+
// when typeshed declares the parameter as nameless. Without this, every
|
|
296
|
+
// model fails verification and Pysa silently emits zero issues.
|
|
297
|
+
//
|
|
298
|
+
// Verified against pyre-check 0.9.25 / Python 3.12 typeshed. If you bump
|
|
299
|
+
// the Pyre version and these stop matching, run `pyre analyze` WITHOUT
|
|
300
|
+
// ``--no-verify`` and read the error message — Pyre prints the exact
|
|
301
|
+
// implementation signature you need to match.
|
|
302
|
+
return [
|
|
303
|
+
"# Auto-generated by aegis-v2 — stdlib-only taint stubs.",
|
|
304
|
+
"# Extend by adding more .pysa files to <project>/taint_models if your",
|
|
305
|
+
"# project uses framework sources (Flask request, FastAPI Body, etc.) —",
|
|
306
|
+
"# those models only verify when the framework is importable.",
|
|
307
|
+
"",
|
|
308
|
+
"# === Built-in stdlib sources ===",
|
|
309
|
+
"def input(__prompt = ...) -> TaintSource[UserControlled]: ...",
|
|
310
|
+
"",
|
|
311
|
+
"# os.getenv is a free function — straightforward shape.",
|
|
312
|
+
"def os.getenv(key, default = ...) -> TaintSource[UserControlled]: ...",
|
|
313
|
+
"",
|
|
314
|
+
"# === sqlite3 sinks (stdlib) ===",
|
|
315
|
+
"# Cursor.execute signature in typeshed:",
|
|
316
|
+
"# (_Self_sqlite3_Cursor__, str, SupportsLenAndGetItem[Any] | Mapping[str, Any]=...) -> _Self_sqlite3_Cursor__",
|
|
317
|
+
"# All 3 args positional-only. We name the SQL arg to mark it as the sink.",
|
|
318
|
+
"def sqlite3.Cursor.execute(__self, __sql: TaintSink[SQL], __parameters = ...): ...",
|
|
319
|
+
"def sqlite3.Cursor.executemany(__self, __sql: TaintSink[SQL], __parameters): ...",
|
|
320
|
+
"def sqlite3.Cursor.executescript(__self, __sql_script: TaintSink[SQL]): ...",
|
|
321
|
+
"def sqlite3.Connection.execute(__self, __sql: TaintSink[SQL], __parameters = ...): ...",
|
|
322
|
+
"def sqlite3.Connection.executemany(__self, __sql: TaintSink[SQL], __parameters): ...",
|
|
323
|
+
"def sqlite3.Connection.executescript(__self, __sql_script: TaintSink[SQL]): ...",
|
|
324
|
+
"",
|
|
325
|
+
"# === OS command sinks (stdlib) ===",
|
|
326
|
+
"# os.system and os.popen take named params in typeshed — drop the __ prefix.",
|
|
327
|
+
"def os.system(command: TaintSink[Command]): ...",
|
|
328
|
+
"def os.popen(cmd: TaintSink[Command], mode = ..., buffering = ...): ...",
|
|
329
|
+
"",
|
|
330
|
+
"# === Eval / exec sinks (stdlib) — first arg is positional-only ===",
|
|
331
|
+
"def eval(__source: TaintSink[RemoteCodeExecution], __globals = ..., __locals = ...): ...",
|
|
332
|
+
"def exec(__source: TaintSink[RemoteCodeExecution], __globals = ..., __locals = ...): ...",
|
|
333
|
+
"",
|
|
334
|
+
].join("\n");
|
|
335
|
+
}
|
|
336
|
+
const CODE_TO_CWE = {
|
|
337
|
+
5001: { cwe: "CWE-89", title: "Pysa taint: User input flows into SQL execute" },
|
|
338
|
+
5002: { cwe: "CWE-94", title: "Pysa taint: User input flows into eval/exec" },
|
|
339
|
+
5003: { cwe: "CWE-78", title: "Pysa taint: User input flows into OS command" },
|
|
340
|
+
5004: { cwe: "CWE-918", title: "Pysa taint: User input flows into outbound HTTP (SSRF)" },
|
|
341
|
+
};
|
|
342
|
+
/** Convert a Pysa qualified callable name (``tests.fixtures.foo.bar``) to
|
|
343
|
+
* a project-relative POSIX path (``tests/fixtures/foo.py``).
|
|
344
|
+
*
|
|
345
|
+
* We do NOT know which dot is a module separator and which is a method
|
|
346
|
+
* accessor (``mod.Class.method``), so the heuristic is: take the FIRST
|
|
347
|
+
* existing prefix as the module path, treat the remainder as in-file
|
|
348
|
+
* symbols. Worst case we strip too much (one ".py" file per symbol) —
|
|
349
|
+
* still better than emitting "*" which is meaningless. */
|
|
350
|
+
function callableToPath(callable, projectRoot) {
|
|
351
|
+
if (!callable)
|
|
352
|
+
return "(unknown)";
|
|
353
|
+
const parts = callable.split(".");
|
|
354
|
+
// Try progressively shorter prefixes until we find one that exists as
|
|
355
|
+
// a .py file on disk.
|
|
356
|
+
for (let i = parts.length; i >= 1; i--) {
|
|
357
|
+
const candidate = parts.slice(0, i).join("/") + ".py";
|
|
358
|
+
const abs = resolvePath(projectRoot, candidate);
|
|
359
|
+
if (existsSync(abs))
|
|
360
|
+
return candidate;
|
|
361
|
+
}
|
|
362
|
+
// Fallback: assume first 3 segments are dirs + last is file.
|
|
363
|
+
if (parts.length >= 2) {
|
|
364
|
+
return parts.slice(0, parts.length - 1).join("/") + ".py";
|
|
365
|
+
}
|
|
366
|
+
return callable;
|
|
367
|
+
}
|
|
368
|
+
function parsePysaOutput(rawJson, projectRoot) {
|
|
369
|
+
const out = [];
|
|
370
|
+
const seen = new Set();
|
|
371
|
+
for (const rawLine of rawJson.split(/\r?\n/)) {
|
|
372
|
+
const line = rawLine.trim();
|
|
373
|
+
if (!line)
|
|
374
|
+
continue;
|
|
375
|
+
let env;
|
|
376
|
+
try {
|
|
377
|
+
env = JSON.parse(line);
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
// skip non-JSON lines (Pyre sometimes emits banner text before JSON)
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (env.kind !== "issue" || !env.data)
|
|
384
|
+
continue;
|
|
385
|
+
const issue = env.data;
|
|
386
|
+
const code = issue.code ?? 0;
|
|
387
|
+
const cweInfo = CODE_TO_CWE[code];
|
|
388
|
+
if (!cweInfo)
|
|
389
|
+
continue;
|
|
390
|
+
const rawFilename = (issue.filename ?? "").replace(/\\/g, "/");
|
|
391
|
+
// Pysa sometimes uses "*" for "any file containing this callable" when
|
|
392
|
+
// the callable has been inlined / proxied. Recover via the callable.
|
|
393
|
+
const rel = rawFilename === "*" || !rawFilename
|
|
394
|
+
? callableToPath(issue.callable ?? "", projectRoot)
|
|
395
|
+
: toRelPosix(rawFilename, projectRoot);
|
|
396
|
+
const lineNo = typeof issue.line === "number" && issue.line > 0 ? issue.line : undefined;
|
|
397
|
+
const dedup = `${rel}::${lineNo ?? "?"}::${cweInfo.cwe}::${code}`;
|
|
398
|
+
if (seen.has(dedup))
|
|
399
|
+
continue;
|
|
400
|
+
seen.add(dedup);
|
|
401
|
+
const ruleId = `pysa.${code}`;
|
|
402
|
+
const finding = {
|
|
403
|
+
id: makeFindingId({
|
|
404
|
+
engine: "pysa",
|
|
405
|
+
file: rel,
|
|
406
|
+
...(lineNo !== undefined ? { line: lineNo } : {}),
|
|
407
|
+
rule_id: ruleId,
|
|
408
|
+
}),
|
|
409
|
+
engine: "pysa",
|
|
410
|
+
file: rel,
|
|
411
|
+
...(lineNo !== undefined ? { line: lineNo } : {}),
|
|
412
|
+
rule_id: ruleId,
|
|
413
|
+
cwe: cweInfo.cwe,
|
|
414
|
+
severity: "high",
|
|
415
|
+
// Taint reachability is real proof of a path — higher than Joern's
|
|
416
|
+
// pattern-match confidence but still LLM-critic should refine before
|
|
417
|
+
// we BLOCK on it alone. (SourceKindEnum doesn't have a separate
|
|
418
|
+
// "taint" tag yet; "dataflow" is the closest semantic match — both
|
|
419
|
+
// are reachability-based vs the syntactic "pattern".)
|
|
420
|
+
confidence: 0.8,
|
|
421
|
+
source: "dataflow",
|
|
422
|
+
message: `${cweInfo.title}. ${String(issue.message ?? "").slice(0, 300)}`,
|
|
423
|
+
evidence: {
|
|
424
|
+
snippet: String(issue.callable ?? "").slice(0, 200) || undefined,
|
|
425
|
+
},
|
|
426
|
+
};
|
|
427
|
+
const parsed = FindingSchema.safeParse(finding);
|
|
428
|
+
if (parsed.success)
|
|
429
|
+
out.push(parsed.data);
|
|
430
|
+
}
|
|
431
|
+
return out;
|
|
432
|
+
}
|
|
433
|
+
function toRelPosix(filePath, projectRoot) {
|
|
434
|
+
const fp = filePath.replace(/\\/g, "/");
|
|
435
|
+
const root = projectRoot.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
436
|
+
// Match either Windows path under root OR a /mnt/c/-translated path (Pysa
|
|
437
|
+
// sees the WSL path; we map back).
|
|
438
|
+
const rootWsl = winToWslPath(projectRoot).replace(/\/+$/, "");
|
|
439
|
+
if (fp.toLowerCase().startsWith(root.toLowerCase() + "/"))
|
|
440
|
+
return fp.slice(root.length + 1);
|
|
441
|
+
if (fp.toLowerCase().startsWith(rootWsl.toLowerCase() + "/"))
|
|
442
|
+
return fp.slice(rootWsl.length + 1);
|
|
443
|
+
return fp;
|
|
444
|
+
}
|
|
445
|
+
// ── Build entry point ────────────────────────────────────────────────────
|
|
446
|
+
/** Run Pysa on the project. Builds the bootstrap config in a SCRATCH dir
|
|
447
|
+
* (NEVER touches the user's repo), invokes ``pyre analyze``, parses the
|
|
448
|
+
* resulting taint-output.json, writes findings.jsonl into the per-project
|
|
449
|
+
* cache. Returns a structured report. Never throws — failures produce
|
|
450
|
+
* ``{ok:false, reason}``. */
|
|
451
|
+
export async function buildPysaTaint(projectRoot, opts) {
|
|
452
|
+
const t0 = Date.now();
|
|
453
|
+
const dir = cacheDirFor(projectRoot);
|
|
454
|
+
const findingsPath = pysaFindingsPath(projectRoot);
|
|
455
|
+
const infoPath = pysaInfoPath(projectRoot);
|
|
456
|
+
const force = opts?.force ?? false;
|
|
457
|
+
const probe = await probePyre();
|
|
458
|
+
if (!probe) {
|
|
459
|
+
return {
|
|
460
|
+
ok: false,
|
|
461
|
+
cacheDir: dir,
|
|
462
|
+
findingsPath,
|
|
463
|
+
findingsCount: 0,
|
|
464
|
+
durationMs: Date.now() - t0,
|
|
465
|
+
reason: process.platform === "win32"
|
|
466
|
+
? "Pyre/Pysa not available — WSL or pyre-check inside WSL missing. Install: wsl --install, then in Ubuntu: python3 -m venv ~/pyre-venv && source ~/pyre-venv/bin/activate && pip install pyre-check"
|
|
467
|
+
: "pyre not found on PATH. Install: pip install pyre-check (or set AEGIS_PYSA_PYRE_BIN).",
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
// Cache freshness
|
|
471
|
+
if (!force && existsSync(infoPath) && existsSync(findingsPath)) {
|
|
472
|
+
try {
|
|
473
|
+
const info = JSON.parse(readFileSync(infoPath, "utf8"));
|
|
474
|
+
if (info.config_version === PYSA_TAINT_CONFIG_VERSION) {
|
|
475
|
+
const n = (readFileSync(findingsPath, "utf8").match(/\n/g) ?? []).length;
|
|
476
|
+
log.info("pysa cache hit", { dir, age_ms: Date.now() - info.built_at, n });
|
|
477
|
+
return {
|
|
478
|
+
ok: true,
|
|
479
|
+
cacheDir: dir,
|
|
480
|
+
findingsPath,
|
|
481
|
+
findingsCount: n,
|
|
482
|
+
durationMs: Date.now() - t0,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
// fall through to rebuild
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// Build scratch config directory under the cache.
|
|
491
|
+
// CRITICAL: Pysa expects the ``taint.config`` file to live INSIDE the
|
|
492
|
+
// ``--taint-models-path`` directory alongside the .pysa stub files —
|
|
493
|
+
// not at the project / scratch root. Per Pysa's source, it scans the
|
|
494
|
+
// models dir for any ``.config`` file and rejects the run if none is
|
|
495
|
+
// present (error: "No `.config` was found in the taint directories").
|
|
496
|
+
const scratch = join(dir, "scratch");
|
|
497
|
+
const taintModelsDir = join(scratch, "taint_models");
|
|
498
|
+
mkdirSync(scratch, { recursive: true });
|
|
499
|
+
mkdirSync(taintModelsDir, { recursive: true });
|
|
500
|
+
// The Pyre config's `source_directories` MUST be expressed in whatever
|
|
501
|
+
// path style Pyre will see — if we go through WSL it must be /mnt/c/...,
|
|
502
|
+
// on Linux native it's the absolute path verbatim.
|
|
503
|
+
const projectRootForPyre = probe.usesWsl ? winToWslPath(projectRoot) : resolvePath(projectRoot);
|
|
504
|
+
writeFileSync(join(scratch, ".pyre_configuration"), bootstrapPyreConfig(projectRootForPyre), "utf8");
|
|
505
|
+
writeFileSync(join(taintModelsDir, "taint.config"), bootstrapTaintConfig(), "utf8");
|
|
506
|
+
writeFileSync(join(taintModelsDir, "general.pysa"), bootstrapTaintModels(), "utf8");
|
|
507
|
+
// The scratch dir we'll cd into inside WSL.
|
|
508
|
+
const scratchForPyre = probe.usesWsl ? winToWslPath(scratch) : scratch;
|
|
509
|
+
const outDir = join(scratch, "pysa-output");
|
|
510
|
+
mkdirSync(outDir, { recursive: true });
|
|
511
|
+
const outDirForPyre = probe.usesWsl ? winToWslPath(outDir) : outDir;
|
|
512
|
+
const wsl = probe.usesWsl ? findWsl() : null;
|
|
513
|
+
// Compose the analyze command. ``--save-results-to <dir>`` writes
|
|
514
|
+
// ``taint-output.json`` (the JSON we parse) and a metadata file. We
|
|
515
|
+
// intentionally do NOT pass ``--rule`` flags — Pysa runs all rules
|
|
516
|
+
// defined in our scratch ``taint.config`` automatically, which is
|
|
517
|
+
// exactly the four we want (5001..5004). Pysa's ``--rule`` accepts
|
|
518
|
+
// ONE integer at a time and the only way to limit to a subset is to
|
|
519
|
+
// repeat the flag; not worth it when the config already enumerates
|
|
520
|
+
// the rules we care about.
|
|
521
|
+
// No --no-verify: we want Pysa to FAIL LOUD if a model stops matching
|
|
522
|
+
// typeshed after a Pyre upgrade. Silent verification dropping is exactly
|
|
523
|
+
// the failure mode we hit while debugging V2-11.
|
|
524
|
+
const pyreCommand = [
|
|
525
|
+
probe.pyreBin,
|
|
526
|
+
"--noninteractive",
|
|
527
|
+
"analyze",
|
|
528
|
+
"--save-results-to",
|
|
529
|
+
`"${outDirForPyre}"`,
|
|
530
|
+
"--taint-models-path",
|
|
531
|
+
`"${winToWslPath(taintModelsDir)}"`,
|
|
532
|
+
].join(" ");
|
|
533
|
+
const remoteScript = `cd "${scratchForPyre}" && ${pyreCommand}`;
|
|
534
|
+
log.info("pysa: invoking analyze", { usesWsl: probe.usesWsl, distro: wsl?.distro });
|
|
535
|
+
const t1 = Date.now();
|
|
536
|
+
const res = probe.usesWsl && wsl
|
|
537
|
+
? await spawnRun(wsl.wslExe, {
|
|
538
|
+
args: ["-d", wsl.distro, "--", "bash", "-c", remoteScript],
|
|
539
|
+
timeoutMs: opts?.timeoutMs ?? DEFAULT_PYSA_TIMEOUT_MS,
|
|
540
|
+
maxBufferBytes: 50 * 1024 * 1024,
|
|
541
|
+
})
|
|
542
|
+
: await spawnRun("bash", {
|
|
543
|
+
args: ["-c", remoteScript],
|
|
544
|
+
timeoutMs: opts?.timeoutMs ?? DEFAULT_PYSA_TIMEOUT_MS,
|
|
545
|
+
maxBufferBytes: 50 * 1024 * 1024,
|
|
546
|
+
});
|
|
547
|
+
if (res.timedOut) {
|
|
548
|
+
return {
|
|
549
|
+
ok: false,
|
|
550
|
+
cacheDir: dir,
|
|
551
|
+
findingsPath,
|
|
552
|
+
findingsCount: 0,
|
|
553
|
+
durationMs: Date.now() - t0,
|
|
554
|
+
reason: `pyre analyze timed out after ${opts?.timeoutMs ?? DEFAULT_PYSA_TIMEOUT_MS}ms`,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
// Pyre returns rc=0 even when issues are found. Non-zero means the
|
|
558
|
+
// analysis itself failed (bad config, syntax error in the project,
|
|
559
|
+
// missing taint stubs, etc).
|
|
560
|
+
if (res.exitCode !== 0) {
|
|
561
|
+
return {
|
|
562
|
+
ok: false,
|
|
563
|
+
cacheDir: dir,
|
|
564
|
+
findingsPath,
|
|
565
|
+
findingsCount: 0,
|
|
566
|
+
durationMs: Date.now() - t0,
|
|
567
|
+
reason: `pyre analyze failed (rc=${res.exitCode}): ${res.stderr.slice(-500) || res.stdout.slice(-500)}`,
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
// Read taint-output.json
|
|
571
|
+
const taintOutputPath = join(outDir, "taint-output.json");
|
|
572
|
+
if (!existsSync(taintOutputPath)) {
|
|
573
|
+
return {
|
|
574
|
+
ok: false,
|
|
575
|
+
cacheDir: dir,
|
|
576
|
+
findingsPath,
|
|
577
|
+
findingsCount: 0,
|
|
578
|
+
durationMs: Date.now() - t0,
|
|
579
|
+
reason: `pyre analyze finished but ${taintOutputPath} not produced`,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
const findings = parsePysaOutput(readFileSync(taintOutputPath, "utf8"), projectRoot);
|
|
583
|
+
writeFileSync(findingsPath, findings.map((f) => JSON.stringify(f)).join("\n") + (findings.length ? "\n" : ""), "utf8");
|
|
584
|
+
const info = {
|
|
585
|
+
built_at: Date.now(),
|
|
586
|
+
config_version: PYSA_TAINT_CONFIG_VERSION,
|
|
587
|
+
pyre_version: probe.version,
|
|
588
|
+
...(probe.usesWsl && wsl ? { wsl_distro: wsl.distro } : {}),
|
|
589
|
+
n_findings: findings.length,
|
|
590
|
+
files_analyzed: new Set(findings.map((f) => f.file)).size,
|
|
591
|
+
};
|
|
592
|
+
writeFileSync(infoPath, JSON.stringify(info, null, 2), "utf8");
|
|
593
|
+
log.info("pysa: analyze done", {
|
|
594
|
+
findings: findings.length,
|
|
595
|
+
analyze_ms: Date.now() - t1,
|
|
596
|
+
total_ms: Date.now() - t0,
|
|
597
|
+
});
|
|
598
|
+
return {
|
|
599
|
+
ok: true,
|
|
600
|
+
cacheDir: dir,
|
|
601
|
+
findingsPath,
|
|
602
|
+
findingsCount: findings.length,
|
|
603
|
+
durationMs: Date.now() - t0,
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
// Test-friendly exports
|
|
607
|
+
export const _testing = {
|
|
608
|
+
winToWslPath,
|
|
609
|
+
parsePysaOutput,
|
|
610
|
+
toRelPosix,
|
|
611
|
+
bootstrapPyreConfig,
|
|
612
|
+
bootstrapTaintConfig,
|
|
613
|
+
bootstrapTaintModels,
|
|
614
|
+
CODE_TO_CWE,
|
|
615
|
+
PYSA_TAINT_CONFIG_VERSION,
|
|
616
|
+
};
|
|
617
|
+
//# sourceMappingURL=pysa.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pysa.js","sourceRoot":"","sources":["../../src/index/pysa.ts"],"names":[],"mappings":"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"}
|