project-athena 0.4.0 → 0.5.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/CHANGELOG.md +31 -0
- package/README.md +10 -0
- package/dist/{activity-BKW7G4I5.js → activity-XCHKBZTG.js} +4 -4
- package/dist/{agents-DFSNEOP5.js → agents-HUJ4JXIB.js} +4 -4
- package/dist/{ai-ASL4FFQ7.js → ai-LGTLQKL4.js} +3 -3
- package/dist/{analyze-ZY3MXBOI.js → analyze-AP4XJT7D.js} +7 -7
- package/dist/{chunk-ZB7LC6FP.js → chunk-3KFOI2X4.js} +2 -2
- package/dist/{chunk-MPG3IEGB.js → chunk-44EGCEB2.js} +23 -14
- package/dist/{chunk-GGXPG6OE.js → chunk-4FETKOY3.js} +8 -7
- package/dist/chunk-4IIBVTBS.js +76 -0
- package/dist/{chunk-LEMPCNAY.js → chunk-56EXAGTM.js} +3 -3
- package/dist/{chunk-DZDI2HRT.js → chunk-6OSQMXUP.js} +5 -5
- package/dist/{chunk-OLPSEQS5.js → chunk-AKOI4VTD.js} +10 -1
- package/dist/{chunk-FVCDMQUQ.js → chunk-C25FDXOO.js} +133 -23
- package/dist/{chunk-7DZK6WSQ.js → chunk-DH5AGULY.js} +2 -2
- package/dist/{chunk-TDNZ2RYA.js → chunk-F65QINMR.js} +2 -2
- package/dist/{chunk-WVQC2EGO.js → chunk-GU3ZX2J7.js} +2 -2
- package/dist/{chunk-UAZLPNVC.js → chunk-GYQG4Q2N.js} +4 -4
- package/dist/chunk-IDODPGTX.js +595 -0
- package/dist/chunk-KJYSNZXS.js +934 -0
- package/dist/{chunk-3XSDI5WF.js → chunk-L2EHJCH4.js} +5 -5
- package/dist/{chunk-65TSRDQ3.js → chunk-P4TRQMES.js} +2 -2
- package/dist/{chunk-KHMBNT66.js → chunk-PZPMWCXS.js} +5 -5
- package/dist/{chunk-XDCKZ5TV.js → chunk-R5AX7VST.js} +2 -2
- package/dist/{chunk-QLW657NT.js → chunk-RQKGT2EK.js} +2 -2
- package/dist/{chunk-VSYBC3VY.js → chunk-UMWX4UTK.js} +2 -2
- package/dist/{chunk-RXNRMIZP.js → chunk-WUIC65QO.js} +2 -2
- package/dist/{chunk-JCGDR4UV.js → chunk-WWQ5NKEC.js} +3 -1
- package/dist/{chunk-RVTNSNGK.js → chunk-X7EYOAEX.js} +2 -2
- package/dist/chunk-Y6SDMZET.js +278 -0
- package/dist/{clean-MDBYH62P.js → clean-LQOG56AM.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/{context-PLPCAPCO.js → context-G5YHY3L2.js} +8 -8
- package/dist/{doctor-BX677KRE.js → doctor-L34QJ7WR.js} +8 -8
- package/dist/{event-TNT6CQZR.js → event-6YW6TGFI.js} +3 -3
- package/dist/{git-hook-YZNZGAAP.js → git-hook-Y7Y262F7.js} +2 -2
- package/dist/{init-WQCAOJ2R.js → init-427QR5AS.js} +7 -7
- package/dist/{mcp-U7EPH43L.js → mcp-DWNWRGJW.js} +51 -14
- package/dist/{memory-VSVNLEJC.js → memory-XBZCYQLW.js} +3 -3
- package/dist/{open-UJZRYF4E.js → open-LJMOB6U2.js} +33 -31
- package/dist/{program-CWJNGCTZ.js → program-DCP526LU.js} +46 -32
- package/dist/{review-OOKXSYMP.js → review-VEJBPACX.js} +10 -9
- package/dist/{rules-4FXJUCXB.js → rules-M4DLLXBL.js} +4 -4
- package/dist/scan-JVKCVHPD.js +939 -0
- package/dist/{security-LME3XFL3.js → security-JTJN3E7K.js} +13 -11
- package/dist/{staged-check-X24C3YRD.js → staged-check-PQSLGQBW.js} +11 -11
- package/dist/{status-RR57ZNGI.js → status-PO27BSOZ.js} +4 -4
- package/dist/{sync-KFJANRXF.js → sync-GVO2VHPI.js} +10 -10
- package/dist/{watch-CY7QHIYN.js → watch-TEHFO47V.js} +10 -10
- package/package.json +3 -1
- package/dist/chunk-SJ4DQFIJ.js +0 -379
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
runSecurityScanWithFindings,
|
|
4
|
+
saveScan
|
|
5
|
+
} from "./chunk-4IIBVTBS.js";
|
|
6
|
+
import "./chunk-IDODPGTX.js";
|
|
7
|
+
import "./chunk-UMWX4UTK.js";
|
|
8
|
+
import "./chunk-Y6SDMZET.js";
|
|
6
9
|
import {
|
|
7
10
|
c,
|
|
8
11
|
dim,
|
|
@@ -22,21 +25,20 @@ import {
|
|
|
22
25
|
import "./chunk-KBO7XRBW.js";
|
|
23
26
|
import {
|
|
24
27
|
projectSession
|
|
25
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-GU3ZX2J7.js";
|
|
26
29
|
import {
|
|
27
30
|
RATED_SEVERITIES,
|
|
28
31
|
SEVERITY_ORDER,
|
|
29
32
|
analyzeProject,
|
|
30
33
|
meetsThreshold,
|
|
31
|
-
saveScan,
|
|
32
34
|
unratedFindings
|
|
33
|
-
} from "./chunk-
|
|
34
|
-
import "./chunk-ELKFRTZT.js";
|
|
35
|
+
} from "./chunk-44EGCEB2.js";
|
|
35
36
|
import "./chunk-PVCIGUIT.js";
|
|
36
|
-
import "./chunk-
|
|
37
|
+
import "./chunk-ELKFRTZT.js";
|
|
38
|
+
import "./chunk-AKOI4VTD.js";
|
|
37
39
|
import "./chunk-SVTEJYMS.js";
|
|
38
40
|
import "./chunk-QCC4VJUH.js";
|
|
39
|
-
import "./chunk-
|
|
41
|
+
import "./chunk-WWQ5NKEC.js";
|
|
40
42
|
import "./chunk-2DY4PUR4.js";
|
|
41
43
|
import "./chunk-IQQBAWSM.js";
|
|
42
44
|
import {
|
|
@@ -125,7 +127,7 @@ async function securityCommand(opts) {
|
|
|
125
127
|
try {
|
|
126
128
|
model = await loadModel(root, opts.signal);
|
|
127
129
|
sp.update("Scanning files for secrets...");
|
|
128
|
-
scan = await
|
|
130
|
+
scan = (await runSecurityScanWithFindings(root, model, { signal: opts.signal, skipAudit: opts.noAudit, onTool: (tool) => sp.update(`Running ${tool}...`) })).scan;
|
|
129
131
|
sp.succeed(`Security scan finished in ${(scan.durationMs / 1e3).toFixed(1)}s`);
|
|
130
132
|
} catch (err) {
|
|
131
133
|
sp.stop();
|
|
@@ -174,4 +176,4 @@ export {
|
|
|
174
176
|
printScan,
|
|
175
177
|
securityCommand
|
|
176
178
|
};
|
|
177
|
-
//# sourceMappingURL=security-
|
|
179
|
+
//# sourceMappingURL=security-JTJN3E7K.js.map
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
HISTORY_SECTIONS
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-PZPMWCXS.js";
|
|
5
|
+
import "./chunk-GU3ZX2J7.js";
|
|
6
6
|
import "./chunk-GR4DL2VM.js";
|
|
7
7
|
import {
|
|
8
8
|
planKnowledge
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-4FETKOY3.js";
|
|
10
|
+
import "./chunk-2R6PXY6Z.js";
|
|
10
11
|
import {
|
|
11
12
|
REAL_ENV_NAMES,
|
|
12
13
|
SCAN_FILE,
|
|
13
14
|
analyzeProject,
|
|
14
15
|
redactDeep
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import {
|
|
17
|
-
mapLimit
|
|
18
|
-
} from "./chunk-ELKFRTZT.js";
|
|
19
|
-
import "./chunk-2R6PXY6Z.js";
|
|
16
|
+
} from "./chunk-44EGCEB2.js";
|
|
20
17
|
import {
|
|
21
18
|
readLocalConfig
|
|
22
19
|
} from "./chunk-PVCIGUIT.js";
|
|
20
|
+
import {
|
|
21
|
+
mapLimit
|
|
22
|
+
} from "./chunk-ELKFRTZT.js";
|
|
23
23
|
import {
|
|
24
24
|
git,
|
|
25
25
|
gitInfo,
|
|
26
26
|
isGitRepo
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-AKOI4VTD.js";
|
|
28
28
|
import "./chunk-SVTEJYMS.js";
|
|
29
29
|
import "./chunk-QCC4VJUH.js";
|
|
30
|
-
import "./chunk-
|
|
30
|
+
import "./chunk-WWQ5NKEC.js";
|
|
31
31
|
import "./chunk-YRIS7ZS6.js";
|
|
32
32
|
import "./chunk-GHFRQAYT.js";
|
|
33
33
|
import "./chunk-INT3IGMQ.js";
|
|
@@ -499,4 +499,4 @@ async function checkStaged(rootInput, opts = {}) {
|
|
|
499
499
|
export {
|
|
500
500
|
checkStaged
|
|
501
501
|
};
|
|
502
|
-
//# sourceMappingURL=staged-check-
|
|
502
|
+
//# sourceMappingURL=staged-check-PQSLGQBW.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildStatus
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3KFOI2X4.js";
|
|
5
5
|
import {
|
|
6
6
|
bullet,
|
|
7
7
|
c,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import "./chunk-KBO7XRBW.js";
|
|
21
21
|
import "./chunk-GR4DL2VM.js";
|
|
22
22
|
import "./chunk-ELKFRTZT.js";
|
|
23
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-AKOI4VTD.js";
|
|
24
24
|
import "./chunk-673EPYYH.js";
|
|
25
25
|
import "./chunk-FQEGEVLY.js";
|
|
26
26
|
import "./chunk-2DY4PUR4.js";
|
|
@@ -33,7 +33,7 @@ import "./chunk-EUPETUWA.js";
|
|
|
33
33
|
async function statusCommand(opts) {
|
|
34
34
|
const root = await requireProjectRoot(opts);
|
|
35
35
|
const report = await buildStatus(root, opts.signal);
|
|
36
|
-
const memory = await (await import("./memory-
|
|
36
|
+
const memory = await (await import("./memory-XBZCYQLW.js")).memorySummary(root);
|
|
37
37
|
if (isJson()) {
|
|
38
38
|
json(memory ? { ...report, memory: { total: memory.total, unreviewed: memory.unreviewed, stale: memory.stale } } : report);
|
|
39
39
|
return;
|
|
@@ -78,4 +78,4 @@ async function statusCommand(opts) {
|
|
|
78
78
|
export {
|
|
79
79
|
statusCommand
|
|
80
80
|
};
|
|
81
|
-
//# sourceMappingURL=status-
|
|
81
|
+
//# sourceMappingURL=status-PO27BSOZ.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
confirm
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-DH5AGULY.js";
|
|
5
5
|
import {
|
|
6
6
|
bullet,
|
|
7
7
|
c,
|
|
@@ -28,18 +28,18 @@ import {
|
|
|
28
28
|
planSync,
|
|
29
29
|
staleForCheck,
|
|
30
30
|
summarizePlan
|
|
31
|
-
} from "./chunk-
|
|
32
|
-
import "./chunk-
|
|
31
|
+
} from "./chunk-PZPMWCXS.js";
|
|
32
|
+
import "./chunk-GU3ZX2J7.js";
|
|
33
33
|
import "./chunk-GR4DL2VM.js";
|
|
34
|
-
import "./chunk-
|
|
35
|
-
import "./chunk-MPG3IEGB.js";
|
|
36
|
-
import "./chunk-ELKFRTZT.js";
|
|
34
|
+
import "./chunk-4FETKOY3.js";
|
|
37
35
|
import "./chunk-2R6PXY6Z.js";
|
|
36
|
+
import "./chunk-44EGCEB2.js";
|
|
38
37
|
import "./chunk-PVCIGUIT.js";
|
|
39
|
-
import "./chunk-
|
|
38
|
+
import "./chunk-ELKFRTZT.js";
|
|
39
|
+
import "./chunk-AKOI4VTD.js";
|
|
40
40
|
import "./chunk-SVTEJYMS.js";
|
|
41
41
|
import "./chunk-QCC4VJUH.js";
|
|
42
|
-
import "./chunk-
|
|
42
|
+
import "./chunk-WWQ5NKEC.js";
|
|
43
43
|
import "./chunk-YRIS7ZS6.js";
|
|
44
44
|
import "./chunk-GHFRQAYT.js";
|
|
45
45
|
import "./chunk-INT3IGMQ.js";
|
|
@@ -183,7 +183,7 @@ async function syncCommand(opts) {
|
|
|
183
183
|
return EXIT.OK;
|
|
184
184
|
}
|
|
185
185
|
async function stagedCheckCommand(root, opts) {
|
|
186
|
-
const { checkStaged } = await import("./staged-check-
|
|
186
|
+
const { checkStaged } = await import("./staged-check-PQSLGQBW.js");
|
|
187
187
|
const sp = spinner("Checking staged knowledge...");
|
|
188
188
|
let r;
|
|
189
189
|
try {
|
|
@@ -215,4 +215,4 @@ export {
|
|
|
215
215
|
printPlan,
|
|
216
216
|
syncCommand
|
|
217
217
|
};
|
|
218
|
-
//# sourceMappingURL=sync-
|
|
218
|
+
//# sourceMappingURL=sync-GVO2VHPI.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
watchProject
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-6OSQMXUP.js";
|
|
5
|
+
import "./chunk-X7EYOAEX.js";
|
|
6
6
|
import {
|
|
7
7
|
c,
|
|
8
8
|
dim,
|
|
@@ -15,18 +15,18 @@ import {
|
|
|
15
15
|
requireProjectRoot
|
|
16
16
|
} from "./chunk-XFN7UZCS.js";
|
|
17
17
|
import "./chunk-KBO7XRBW.js";
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-PZPMWCXS.js";
|
|
19
|
+
import "./chunk-GU3ZX2J7.js";
|
|
20
20
|
import "./chunk-GR4DL2VM.js";
|
|
21
|
-
import "./chunk-
|
|
22
|
-
import "./chunk-MPG3IEGB.js";
|
|
23
|
-
import "./chunk-ELKFRTZT.js";
|
|
21
|
+
import "./chunk-4FETKOY3.js";
|
|
24
22
|
import "./chunk-2R6PXY6Z.js";
|
|
23
|
+
import "./chunk-44EGCEB2.js";
|
|
25
24
|
import "./chunk-PVCIGUIT.js";
|
|
26
|
-
import "./chunk-
|
|
25
|
+
import "./chunk-ELKFRTZT.js";
|
|
26
|
+
import "./chunk-AKOI4VTD.js";
|
|
27
27
|
import "./chunk-SVTEJYMS.js";
|
|
28
28
|
import "./chunk-QCC4VJUH.js";
|
|
29
|
-
import "./chunk-
|
|
29
|
+
import "./chunk-WWQ5NKEC.js";
|
|
30
30
|
import "./chunk-YRIS7ZS6.js";
|
|
31
31
|
import "./chunk-GHFRQAYT.js";
|
|
32
32
|
import "./chunk-INT3IGMQ.js";
|
|
@@ -120,4 +120,4 @@ async function watchCommand(opts) {
|
|
|
120
120
|
export {
|
|
121
121
|
watchCommand
|
|
122
122
|
};
|
|
123
|
-
//# sourceMappingURL=watch-
|
|
123
|
+
//# sourceMappingURL=watch-TEHFO47V.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "project-athena",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Athena — local-first project intelligence layer for AI coding agents",
|
|
5
5
|
"author": "Sabarivasan",
|
|
6
6
|
"license": "MIT",
|
|
@@ -68,6 +68,8 @@
|
|
|
68
68
|
"@types/react": "^19.3.0",
|
|
69
69
|
"@types/react-dom": "^19.3.0",
|
|
70
70
|
"@vitejs/plugin-react": "^6.1.1",
|
|
71
|
+
"ajv": "^8.20.0",
|
|
72
|
+
"ajv-formats": "^3.0.1",
|
|
71
73
|
"codemirror": "^6.0.2",
|
|
72
74
|
"dompurify": "^3.4.15",
|
|
73
75
|
"jsdom": "^30.1.0",
|
package/dist/chunk-SJ4DQFIJ.js
DELETED
|
@@ -1,379 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
whichExecutable
|
|
4
|
-
} from "./chunk-VSYBC3VY.js";
|
|
5
|
-
import {
|
|
6
|
-
scanFilesForSecrets,
|
|
7
|
-
secretFingerprinter
|
|
8
|
-
} from "./chunk-MPG3IEGB.js";
|
|
9
|
-
import {
|
|
10
|
-
walkProject
|
|
11
|
-
} from "./chunk-ELKFRTZT.js";
|
|
12
|
-
import {
|
|
13
|
-
loadConfig
|
|
14
|
-
} from "./chunk-2DY4PUR4.js";
|
|
15
|
-
|
|
16
|
-
// src/services/security.ts
|
|
17
|
-
import { execFile } from "child_process";
|
|
18
|
-
import { promises as fs } from "fs";
|
|
19
|
-
import path from "path";
|
|
20
|
-
var AuditToolError = class extends Error {
|
|
21
|
-
};
|
|
22
|
-
var asRecord = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
23
|
-
var isObject = (v) => Boolean(v) && typeof v === "object" && !Array.isArray(v);
|
|
24
|
-
var sev = (s) => {
|
|
25
|
-
const v = String(s ?? "").toLowerCase();
|
|
26
|
-
if (v === "critical") return "critical";
|
|
27
|
-
if (v === "high") return "high";
|
|
28
|
-
if (v === "moderate" || v === "medium") return "moderate";
|
|
29
|
-
if (v === "low" || v === "info") return "low";
|
|
30
|
-
return "unrated";
|
|
31
|
-
};
|
|
32
|
-
function parseJsonObject(stdout, tool) {
|
|
33
|
-
const text = stdout.trim();
|
|
34
|
-
if (!text) throw new AuditToolError(`${tool} produced no output`);
|
|
35
|
-
const json = JSON.parse(text);
|
|
36
|
-
if (!isObject(json)) throw new AuditToolError(`${tool} produced unexpected output`);
|
|
37
|
-
return json;
|
|
38
|
-
}
|
|
39
|
-
function throwIfToolError(json, tool) {
|
|
40
|
-
if (json.error === void 0) return;
|
|
41
|
-
const e = asRecord(json.error);
|
|
42
|
-
const parts = [e.code, e.summary ?? e.message].filter((x) => typeof x === "string" && x.trim());
|
|
43
|
-
const text = parts.length ? parts.join(": ") : typeof json.error === "string" ? json.error : "unknown error";
|
|
44
|
-
throw new AuditToolError(`${tool} reported an error: ${text.split("\n")[0].slice(0, 200)}`);
|
|
45
|
-
}
|
|
46
|
-
function parseAdvisories(advisories) {
|
|
47
|
-
const out = [];
|
|
48
|
-
for (const [key, raw] of Object.entries(advisories)) {
|
|
49
|
-
const a = asRecord(raw);
|
|
50
|
-
const versions = [...new Set((Array.isArray(a.findings) ? a.findings : []).map((f) => asRecord(f).version).filter((v) => typeof v === "string"))];
|
|
51
|
-
const patched = typeof a.patched_versions === "string" ? a.patched_versions.trim() : "";
|
|
52
|
-
out.push({
|
|
53
|
-
package: String(a.module_name ?? "unknown"),
|
|
54
|
-
severity: sev(a.severity),
|
|
55
|
-
title: String(a.title ?? `Vulnerable dependency: ${String(a.module_name ?? key)}`),
|
|
56
|
-
id: typeof a.github_advisory_id === "string" ? a.github_advisory_id : Array.isArray(a.cves) && typeof a.cves[0] === "string" ? a.cves[0] : String(a.id ?? key),
|
|
57
|
-
url: typeof a.url === "string" ? a.url : void 0,
|
|
58
|
-
vulnerableRange: typeof a.vulnerable_versions === "string" ? a.vulnerable_versions + (versions.length ? ` (installed ${versions.join(", ")})` : "") : void 0,
|
|
59
|
-
fixAvailable: Boolean(patched) && patched !== "<0.0.0"
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
return out;
|
|
63
|
-
}
|
|
64
|
-
function parseNpmAudit(stdout) {
|
|
65
|
-
const json = parseJsonObject(stdout, "npm audit");
|
|
66
|
-
throwIfToolError(json, "npm audit");
|
|
67
|
-
if (!isObject(json.vulnerabilities)) {
|
|
68
|
-
if (isObject(json.advisories)) return parseAdvisories(json.advisories);
|
|
69
|
-
throw new AuditToolError('npm audit output has no "vulnerabilities" report');
|
|
70
|
-
}
|
|
71
|
-
const out = [];
|
|
72
|
-
for (const [name, raw] of Object.entries(json.vulnerabilities)) {
|
|
73
|
-
const v = asRecord(raw);
|
|
74
|
-
const via = Array.isArray(v.via) ? v.via : [];
|
|
75
|
-
const advisory = via.find((x) => x && typeof x === "object");
|
|
76
|
-
const viaNames = via.filter((x) => typeof x === "string");
|
|
77
|
-
out.push({
|
|
78
|
-
package: name,
|
|
79
|
-
severity: sev(v.severity),
|
|
80
|
-
title: String(advisory?.title ?? (viaNames.length ? `Depends on vulnerable ${viaNames.join(", ")}` : `Vulnerable dependency: ${name}`)),
|
|
81
|
-
id: advisory?.source !== void 0 ? String(advisory.source) : void 0,
|
|
82
|
-
url: typeof advisory?.url === "string" ? advisory.url : void 0,
|
|
83
|
-
vulnerableRange: typeof v.range === "string" ? v.range : void 0,
|
|
84
|
-
fixAvailable: Boolean(v.fixAvailable)
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
return out;
|
|
88
|
-
}
|
|
89
|
-
function parsePnpmAudit(stdout) {
|
|
90
|
-
const json = parseJsonObject(stdout, "pnpm audit");
|
|
91
|
-
throwIfToolError(json, "pnpm audit");
|
|
92
|
-
if (!isObject(json.advisories)) throw new AuditToolError('pnpm audit output has no "advisories" report');
|
|
93
|
-
return parseAdvisories(json.advisories);
|
|
94
|
-
}
|
|
95
|
-
function parsePipAudit(stdout) {
|
|
96
|
-
const text = stdout.trim();
|
|
97
|
-
if (!text) throw new AuditToolError("pip-audit produced no output");
|
|
98
|
-
const json = JSON.parse(text);
|
|
99
|
-
const deps = isObject(json) && Array.isArray(json.dependencies) ? json.dependencies : Array.isArray(json) ? json : null;
|
|
100
|
-
if (!deps) throw new AuditToolError('pip-audit output has no "dependencies" list');
|
|
101
|
-
const out = [];
|
|
102
|
-
for (const raw of deps) {
|
|
103
|
-
const d = asRecord(raw);
|
|
104
|
-
for (const vRaw of Array.isArray(d.vulns) ? d.vulns : []) {
|
|
105
|
-
const v = asRecord(vRaw);
|
|
106
|
-
const aliases = Array.isArray(v.aliases) ? v.aliases.filter((a) => typeof a === "string") : [];
|
|
107
|
-
const id = typeof v.id === "string" ? v.id : void 0;
|
|
108
|
-
out.push({
|
|
109
|
-
package: String(d.name ?? "unknown"),
|
|
110
|
-
severity: "unrated",
|
|
111
|
-
// pip-audit does not report severity
|
|
112
|
-
title: String(v.description ?? id ?? "Known vulnerability").split("\n")[0].slice(0, 300),
|
|
113
|
-
id: id ?? aliases[0],
|
|
114
|
-
url: id ? `https://osv.dev/vulnerability/${id}` : void 0,
|
|
115
|
-
vulnerableRange: typeof d.version === "string" ? `installed ${d.version}` : void 0,
|
|
116
|
-
fixAvailable: Array.isArray(v.fix_versions) && v.fix_versions.length > 0
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return out;
|
|
121
|
-
}
|
|
122
|
-
function splitJsonStream(text) {
|
|
123
|
-
const out = [];
|
|
124
|
-
let depth = 0;
|
|
125
|
-
let inString = false;
|
|
126
|
-
let escaped = false;
|
|
127
|
-
let start = -1;
|
|
128
|
-
for (let i = 0; i < text.length; i++) {
|
|
129
|
-
const ch = text[i];
|
|
130
|
-
if (inString) {
|
|
131
|
-
if (escaped) escaped = false;
|
|
132
|
-
else if (ch === "\\") escaped = true;
|
|
133
|
-
else if (ch === '"') inString = false;
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
if (ch === '"') {
|
|
137
|
-
if (depth > 0) inString = true;
|
|
138
|
-
} else if (ch === "{") {
|
|
139
|
-
if (depth === 0) start = i;
|
|
140
|
-
depth++;
|
|
141
|
-
} else if (ch === "}" && depth > 0) {
|
|
142
|
-
depth--;
|
|
143
|
-
if (depth === 0) {
|
|
144
|
-
out.push(JSON.parse(text.slice(start, i + 1)));
|
|
145
|
-
start = -1;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if (depth !== 0) throw new AuditToolError("truncated JSON output");
|
|
150
|
-
return out;
|
|
151
|
-
}
|
|
152
|
-
var REACH_RANK = { required: 0, imported: 1, called: 2 };
|
|
153
|
-
function parseGovulncheck(stdout) {
|
|
154
|
-
const messages = splitJsonStream(stdout).map(asRecord);
|
|
155
|
-
if (!messages.some((m) => isObject(m.config))) throw new AuditToolError("govulncheck produced no JSON report");
|
|
156
|
-
const osvs = /* @__PURE__ */ new Map();
|
|
157
|
-
for (const m of messages) {
|
|
158
|
-
const osv = asRecord(m.osv);
|
|
159
|
-
if (typeof osv.id === "string") osvs.set(osv.id, osv);
|
|
160
|
-
}
|
|
161
|
-
const found = /* @__PURE__ */ new Map();
|
|
162
|
-
for (const m of messages) {
|
|
163
|
-
const f = asRecord(m.finding);
|
|
164
|
-
if (typeof f.osv !== "string") continue;
|
|
165
|
-
const trace = (Array.isArray(f.trace) ? f.trace : []).map(asRecord);
|
|
166
|
-
const reach = trace.some((t) => typeof t.function === "string") ? "called" : trace.some((t) => typeof t.package === "string") ? "imported" : "required";
|
|
167
|
-
const first = trace[0] ?? {};
|
|
168
|
-
const prev = found.get(f.osv);
|
|
169
|
-
if (prev && REACH_RANK[prev.reach] >= REACH_RANK[reach]) continue;
|
|
170
|
-
found.set(f.osv, {
|
|
171
|
-
module: typeof first.module === "string" ? first.module : prev?.module,
|
|
172
|
-
version: typeof first.version === "string" ? first.version : prev?.version,
|
|
173
|
-
fixed: typeof f.fixed_version === "string" ? f.fixed_version : prev?.fixed,
|
|
174
|
-
reach
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
const label = { called: "called by your code", imported: "package imported, vulnerable symbol not called", required: "module required, package not imported" };
|
|
178
|
-
return [...found].map(([id, f]) => {
|
|
179
|
-
const osv = osvs.get(id) ?? {};
|
|
180
|
-
const affected = Array.isArray(osv.affected) ? asRecord(osv.affected[0]) : {};
|
|
181
|
-
return {
|
|
182
|
-
package: f.module ?? String(asRecord(affected.package).name ?? "unknown"),
|
|
183
|
-
severity: "unrated",
|
|
184
|
-
// the Go vulnerability database does not assign severities
|
|
185
|
-
title: `${String(osv.summary ?? id)} (${label[f.reach]})`,
|
|
186
|
-
id,
|
|
187
|
-
url: `https://pkg.go.dev/vuln/${id}`,
|
|
188
|
-
vulnerableRange: f.version ? `installed ${f.version}` : void 0,
|
|
189
|
-
fixAvailable: Boolean(f.fixed)
|
|
190
|
-
};
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
function parseCargoAudit(stdout) {
|
|
194
|
-
const json = parseJsonObject(stdout, "cargo audit");
|
|
195
|
-
if (!isObject(json.vulnerabilities)) throw new AuditToolError('cargo audit output has no "vulnerabilities" report');
|
|
196
|
-
const list = Array.isArray(json.vulnerabilities.list) ? json.vulnerabilities.list : [];
|
|
197
|
-
return list.map((raw) => {
|
|
198
|
-
const v = asRecord(raw);
|
|
199
|
-
const advisory = asRecord(v.advisory);
|
|
200
|
-
const pkg = asRecord(v.package);
|
|
201
|
-
return {
|
|
202
|
-
package: String(pkg.name ?? "unknown"),
|
|
203
|
-
severity: sev(asRecord(advisory.cvss).severity ?? advisory.severity),
|
|
204
|
-
title: String(advisory.title ?? "Known vulnerability"),
|
|
205
|
-
id: typeof advisory.id === "string" ? advisory.id : void 0,
|
|
206
|
-
url: typeof advisory.url === "string" ? advisory.url : void 0
|
|
207
|
-
};
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
function parseComposerAudit(stdout) {
|
|
211
|
-
const json = parseJsonObject(stdout, "composer audit");
|
|
212
|
-
if (json.advisories === void 0) throw new AuditToolError('composer audit output has no "advisories" report');
|
|
213
|
-
const advisories = asRecord(json.advisories);
|
|
214
|
-
const out = [];
|
|
215
|
-
for (const [pkg, raw] of Object.entries(advisories)) {
|
|
216
|
-
for (const aRaw of Array.isArray(raw) ? raw : Object.values(asRecord(raw))) {
|
|
217
|
-
const a = asRecord(aRaw);
|
|
218
|
-
out.push({
|
|
219
|
-
package: pkg,
|
|
220
|
-
severity: sev(a.severity),
|
|
221
|
-
title: String(a.title ?? "Known vulnerability"),
|
|
222
|
-
id: typeof a.cve === "string" ? a.cve : typeof a.advisoryId === "string" ? a.advisoryId : void 0,
|
|
223
|
-
url: typeof a.link === "string" ? a.link : void 0,
|
|
224
|
-
vulnerableRange: typeof a.affectedVersions === "string" ? a.affectedVersions : void 0
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
return out;
|
|
229
|
-
}
|
|
230
|
-
var MAX_PIP_TARGETS = 10;
|
|
231
|
-
var REQUIREMENTS_FILE = /(^|\/)requirements[\w.-]*\.txt$/i;
|
|
232
|
-
async function planPipAudit(ctx) {
|
|
233
|
-
const common = ["--format", "json", "--progress-spinner", "off"];
|
|
234
|
-
const reqs = ctx.files.filter((f) => REQUIREMENTS_FILE.test(f)).sort();
|
|
235
|
-
const reqDirs = new Set(reqs.map((f) => path.posix.dirname(f)));
|
|
236
|
-
const out = reqs.map((f) => ({ target: f, args: ["-r", f, ...common] }));
|
|
237
|
-
for (const f of ctx.files.filter((x) => x === "pyproject.toml" || x.endsWith("/pyproject.toml")).sort()) {
|
|
238
|
-
const dir = path.posix.dirname(f);
|
|
239
|
-
if (reqDirs.has(dir)) continue;
|
|
240
|
-
const text = await fs.readFile(path.join(ctx.root, f), "utf8").catch(() => "");
|
|
241
|
-
if (!/^\s*\[project\]\s*$/m.test(text)) continue;
|
|
242
|
-
out.push({ target: f, args: [...common, dir === "." ? "." : `./${dir}`] });
|
|
243
|
-
}
|
|
244
|
-
if (!out.length) return "no requirements file to audit (pip-audit needs requirements*.txt or a PEP 621 pyproject.toml; the active Python environment is never audited)";
|
|
245
|
-
return out.slice(0, MAX_PIP_TARGETS);
|
|
246
|
-
}
|
|
247
|
-
var single = (args) => async () => [{ args }];
|
|
248
|
-
var RUNNERS = [
|
|
249
|
-
{
|
|
250
|
-
tool: "npm audit",
|
|
251
|
-
ecosystem: "npm",
|
|
252
|
-
command: "npm",
|
|
253
|
-
applies: (_m, has) => has("package-lock.json") || has("npm-shrinkwrap.json"),
|
|
254
|
-
plan: single(["audit", "--json", "--audit-level=low"]),
|
|
255
|
-
parse: parseNpmAudit
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
tool: "pnpm audit",
|
|
259
|
-
ecosystem: "npm",
|
|
260
|
-
command: "pnpm",
|
|
261
|
-
applies: (_m, has) => has("pnpm-lock.yaml"),
|
|
262
|
-
plan: single(["audit", "--json"]),
|
|
263
|
-
parse: parsePnpmAudit
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
tool: "pip-audit",
|
|
267
|
-
ecosystem: "python",
|
|
268
|
-
command: "pip-audit",
|
|
269
|
-
applies: (m, _has, ctx) => m.manifests.some((x) => x.ecosystem === "python") || ctx.files.some((f) => REQUIREMENTS_FILE.test(f)),
|
|
270
|
-
plan: planPipAudit,
|
|
271
|
-
parse: parsePipAudit
|
|
272
|
-
},
|
|
273
|
-
{
|
|
274
|
-
tool: "govulncheck",
|
|
275
|
-
ecosystem: "go",
|
|
276
|
-
command: "govulncheck",
|
|
277
|
-
applies: (_m, has) => has("go.mod"),
|
|
278
|
-
plan: single(["-format=json", "./..."]),
|
|
279
|
-
parse: parseGovulncheck
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
tool: "cargo audit",
|
|
283
|
-
ecosystem: "cargo",
|
|
284
|
-
command: "cargo",
|
|
285
|
-
applies: (_m, has) => has("Cargo.lock"),
|
|
286
|
-
plan: single(["audit", "--json"]),
|
|
287
|
-
parse: parseCargoAudit
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
tool: "composer audit",
|
|
291
|
-
ecosystem: "composer",
|
|
292
|
-
command: "composer",
|
|
293
|
-
applies: (_m, has) => has("composer.lock"),
|
|
294
|
-
plan: single(["audit", "--format=json"]),
|
|
295
|
-
parse: parseComposerAudit
|
|
296
|
-
}
|
|
297
|
-
];
|
|
298
|
-
function run(command, args, cwd, timeoutMs) {
|
|
299
|
-
return new Promise((resolve) => {
|
|
300
|
-
execFile(command, args, { cwd, timeout: timeoutMs, maxBuffer: 32 * 1024 * 1024, windowsHide: true, env: { ...process.env, NO_COLOR: "1", npm_config_color: "false" } }, (error, stdout, stderr) => {
|
|
301
|
-
const timedOut = Boolean(error && (error.code === "ETIMEDOUT" || error.killed));
|
|
302
|
-
resolve({ stdout: String(stdout ?? ""), stderr: String(stderr ?? ""), timedOut, error: error ?? null });
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
async function runTool(runner, inv, root, timeoutMs) {
|
|
307
|
-
const t = Date.now();
|
|
308
|
-
const base = { tool: runner.tool, ecosystem: runner.ecosystem, ...inv.target ? { target: inv.target } : {} };
|
|
309
|
-
const r = await run(runner.command, inv.args, root, timeoutMs);
|
|
310
|
-
if (r.timedOut) return { ...base, status: "timeout", message: `Timed out after ${timeoutMs / 1e3}s`, durationMs: Date.now() - t, findings: [] };
|
|
311
|
-
try {
|
|
312
|
-
const findings = runner.parse(r.stdout, r.stderr);
|
|
313
|
-
return { ...base, status: "ok", durationMs: Date.now() - t, findings };
|
|
314
|
-
} catch (err) {
|
|
315
|
-
const reason = err instanceof AuditToolError ? err.message : `Could not parse output: ${r.stderr.trim().split("\n")[0] || r.error?.message || err.message || "unreadable output"}`;
|
|
316
|
-
return { ...base, status: "failed", message: reason.slice(0, 300), durationMs: Date.now() - t, findings: [] };
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
async function runSecurityScan(root, model, opts = {}) {
|
|
320
|
-
const started = Date.now();
|
|
321
|
-
const timeoutMs = opts.timeoutMs ?? 12e4;
|
|
322
|
-
const { config } = await loadConfig(root);
|
|
323
|
-
const walk = await walkProject(root, { config, signal: opts.signal });
|
|
324
|
-
const ctx = { root, files: walk.files.map((f) => f.path) };
|
|
325
|
-
const files = new Set(model.manifests.map((m) => m.path));
|
|
326
|
-
for (const pm of model.packageManagers) for (const e of pm.provenance.evidence) files.add(e.file);
|
|
327
|
-
for (const f of ctx.files) files.add(f);
|
|
328
|
-
const has = (file) => files.has(file);
|
|
329
|
-
const tools = [];
|
|
330
|
-
if (!opts.skipAudit) {
|
|
331
|
-
for (const runner of RUNNERS) {
|
|
332
|
-
if (!runner.applies(model, has, ctx)) continue;
|
|
333
|
-
opts.signal?.throwIfAborted();
|
|
334
|
-
const t = Date.now();
|
|
335
|
-
opts.onTool?.(runner.tool);
|
|
336
|
-
const bin = await whichExecutable(runner.command);
|
|
337
|
-
if (!bin) {
|
|
338
|
-
tools.push({ tool: runner.tool, ecosystem: runner.ecosystem, status: "unavailable", message: `${runner.command} is not installed or not on PATH`, durationMs: Date.now() - t, findings: [] });
|
|
339
|
-
continue;
|
|
340
|
-
}
|
|
341
|
-
const plan = await runner.plan(ctx);
|
|
342
|
-
if (typeof plan === "string") {
|
|
343
|
-
tools.push({ tool: runner.tool, ecosystem: runner.ecosystem, status: "unavailable", message: plan, durationMs: Date.now() - t, findings: [] });
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
for (const inv of plan) {
|
|
347
|
-
opts.signal?.throwIfAborted();
|
|
348
|
-
tools.push(await runTool(runner, inv, root, timeoutMs));
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
const counts = { critical: 0, high: 0, moderate: 0, low: 0, unrated: 0 };
|
|
353
|
-
for (const t of tools) for (const f of t.findings) counts[f.severity]++;
|
|
354
|
-
const secrets = await scanProjectSecrets(root, walk.files, opts.signal);
|
|
355
|
-
const scan = {
|
|
356
|
-
schemaVersion: 1,
|
|
357
|
-
scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
358
|
-
durationMs: Date.now() - started,
|
|
359
|
-
tools,
|
|
360
|
-
counts,
|
|
361
|
-
secrets: {
|
|
362
|
-
count: secrets.findings.length,
|
|
363
|
-
files: [...new Set(secrets.findings.map((s) => s.file))].slice(0, 50),
|
|
364
|
-
skippedLongLines: secrets.skippedLongLines,
|
|
365
|
-
...secrets.truncated ? { truncated: true } : {}
|
|
366
|
-
}
|
|
367
|
-
};
|
|
368
|
-
return scan;
|
|
369
|
-
}
|
|
370
|
-
async function scanProjectSecrets(root, files, signal) {
|
|
371
|
-
const fingerprint = await secretFingerprinter(root);
|
|
372
|
-
const read = async (rel) => fs.readFile(path.join(root, rel), "utf8").catch(() => null);
|
|
373
|
-
return scanFilesForSecrets(files, read, { fingerprint, signal });
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
export {
|
|
377
|
-
runSecurityScan
|
|
378
|
-
};
|
|
379
|
-
//# sourceMappingURL=chunk-SJ4DQFIJ.js.map
|