@clear-capabilities/agentic-security-scanner 0.172.1 → 0.173.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 +15 -0
- package/bin/agentic-security.js +37 -0
- package/dist/119.index.js +197 -0
- package/dist/128.index.js +129 -0
- package/dist/1293.index.js +5689 -0
- package/dist/1351.index.js +133 -0
- package/dist/1387.index.js +271 -0
- package/dist/1437.index.js +23 -0
- package/dist/1445.index.js +110 -0
- package/dist/1505.index.js +151 -0
- package/dist/1557.index.js +293 -0
- package/dist/1621.index.js +311 -0
- package/dist/1751.index.js +160 -0
- package/dist/1765.index.js +727 -0
- package/dist/1861.index.js +690 -0
- package/dist/1898.index.js +116 -0
- package/dist/1906.index.js +433 -0
- package/dist/1920.index.js +1 -291
- package/dist/208.index.js +1631 -0
- package/dist/2199.index.js +0 -239
- package/dist/2223.index.js +159 -0
- package/dist/2530.index.js +152 -0
- package/dist/2565.index.js +116 -0
- package/dist/2574.index.js +127 -0
- package/dist/2620.index.js +345 -0
- package/dist/2773.index.js +98 -0
- package/dist/2775.index.js +32 -0
- package/dist/2966.index.js +171 -0
- package/dist/2972.index.js +87 -0
- package/dist/2991.index.js +131 -0
- package/dist/3063.index.js +833 -0
- package/dist/3075.index.js +227 -0
- package/dist/3121.index.js +297 -0
- package/dist/3314.index.js +263 -0
- package/dist/3480.index.js +1822 -0
- package/dist/3481.index.js +24 -0
- package/dist/3506.index.js +268 -0
- package/dist/3533.index.js +591 -0
- package/dist/3653.index.js +450 -0
- package/dist/3822.index.js +131 -0
- package/dist/3868.index.js +292 -0
- package/dist/3917.index.js +152 -0
- package/dist/3918.index.js +192 -0
- package/dist/4090.index.js +288 -0
- package/dist/4121.index.js +1190 -0
- package/dist/4142.index.js +0 -239
- package/dist/4336.index.js +436 -0
- package/dist/4387.index.js +532 -0
- package/dist/4413.index.js +302 -0
- package/dist/4476.index.js +4510 -0
- package/dist/4491.index.js +133 -0
- package/dist/4538.index.js +185 -0
- package/dist/4684.index.js +319 -0
- package/dist/4740.index.js +165 -0
- package/dist/4780.index.js +209 -0
- package/dist/4802.index.js +225 -0
- package/dist/4812.index.js +379 -0
- package/dist/4835.index.js +126 -0
- package/dist/494.index.js +226 -0
- package/dist/4991.index.js +549 -0
- package/dist/5199.index.js +305 -0
- package/dist/5216.index.js +109 -0
- package/dist/5439.index.js +301 -0
- package/dist/5443.index.js +97 -0
- package/dist/5496.index.js +377 -0
- package/dist/550.index.js +2 -1
- package/dist/5801.index.js +588 -0
- package/dist/5858.index.js +223 -0
- package/dist/5933.index.js +143 -0
- package/dist/601.index.js +23 -0
- package/dist/6173.index.js +273 -0
- package/dist/6291.index.js +1871 -0
- package/dist/6312.index.js +666 -0
- package/dist/634.index.js +16182 -0
- package/dist/644.index.js +87 -0
- package/dist/6508.index.js +283 -0
- package/dist/6560.index.js +645 -0
- package/dist/6576.index.js +422 -0
- package/dist/6579.index.js +271 -0
- package/dist/6745.index.js +305 -0
- package/dist/6818.index.js +21642 -0
- package/dist/6972.index.js +167 -0
- package/dist/6995.index.js +163 -0
- package/dist/7249.index.js +191 -0
- package/dist/7372.index.js +1197 -0
- package/dist/7546.index.js +541 -0
- package/dist/76.index.js +448 -0
- package/dist/7879.index.js +163 -0
- package/dist/8025.index.js +130 -0
- package/dist/8054.index.js +248 -0
- package/dist/8097.index.js +201 -0
- package/dist/8126.index.js +191 -0
- package/dist/8178.index.js +78 -0
- package/dist/818.index.js +212 -0
- package/dist/8382.index.js +93 -0
- package/dist/8408.index.js +261 -0
- package/dist/8540.index.js +174 -0
- package/dist/8635.index.js +802 -0
- package/dist/8688.index.js +410 -0
- package/dist/8699.index.js +779 -0
- package/dist/8891.index.js +457 -0
- package/dist/8916.index.js +885 -0
- package/dist/9146.index.js +170 -0
- package/dist/9168.index.js +841 -0
- package/dist/9292.index.js +369 -0
- package/dist/9382.index.js +135 -11
- package/dist/9422.index.js +866 -0
- package/dist/9440.index.js +453 -0
- package/dist/9479.index.js +152 -0
- package/dist/9481.index.js +220 -0
- package/dist/9539.index.js +100 -0
- package/dist/9547.index.js +103 -0
- package/dist/9560.index.js +41 -61
- package/dist/9692.index.js +90 -0
- package/dist/9800.index.js +213 -1
- package/dist/9899.index.js +138 -0
- package/dist/9981.index.js +206 -0
- package/dist/999.index.js +117 -0
- package/dist/agentic-security.mjs +27 -27
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +28 -3
- package/src/context-intelligence/candidates.js +2 -1
- package/src/context-intelligence/capabilities.js +2 -1
- package/src/context-intelligence/cli.js +2 -0
- package/src/context-intelligence/config.js +3 -1
- package/src/context-intelligence/controls.js +49 -1
- package/src/context-intelligence/counterexample.js +200 -0
- package/src/context-intelligence/cross-route.js +111 -0
- package/src/context-intelligence/findings.js +166 -0
- package/src/context-intelligence/invariants.js +197 -0
- package/src/context-intelligence/queue.js +6 -1
- package/src/context-intelligence/reachability.js +2 -2
- package/src/context-intelligence/review.js +68 -4
- package/src/dataflow/access-paths.js +11 -0
- package/src/dataflow/analysis-limits.js +66 -0
- package/src/dataflow/engine.js +272 -20
- package/src/dataflow/service-edges.js +174 -0
- package/src/dataflow/summaries.js +7 -1
- package/src/discovery/judge.js +2 -1
- package/src/engine.js +82 -14
- package/src/fix/apply-fix-service.js +55 -8
- package/src/fix/batch-journal.js +84 -0
- package/src/fix/exploit-replay.js +51 -0
- package/src/fix/footprint.js +65 -0
- package/src/fix/patch-policy.js +49 -0
- package/src/fix/secret-persistence.js +39 -0
- package/src/fix/test-commands.js +84 -0
- package/src/fix/verification-adapters.js +97 -0
- package/src/fix/verification-record.js +243 -0
- package/src/fix/verify-patch.js +89 -0
- package/src/ir/CLAUDE.md +5 -0
- package/src/ir/callgraph.js +23 -1
- package/src/ir/module-resolver.js +315 -0
- package/src/ir/parser-js.js +21 -2
- package/src/ir/semantic-graph.js +245 -0
- package/src/lineage/build-attested-correlation.js +63 -0
- package/src/lineage/deployment-identity.js +76 -0
- package/src/mcp/tools.js +13 -34
- package/src/pipeline/canonical-output.js +64 -0
- package/src/pipeline/detector-runner.js +7 -0
- package/src/pipeline/scan-health.js +14 -0
- package/src/pipeline/supervised-analyzers.js +125 -0
- package/src/pipeline/supervised-child.js +17 -0
- package/src/pipeline/telemetry.js +79 -0
- package/src/posture/accuracy-scorecard.js +98 -9
- package/src/posture/artifact-registry.js +5 -0
- package/src/posture/assurance/evidence-registry.js +215 -0
- package/src/posture/assurance/release-package-verify.js +135 -0
- package/src/posture/assurance/release-package.js +118 -0
- package/src/posture/assurance/release-verdict.js +94 -0
- package/src/posture/assurance/waiver.js +200 -0
- package/src/posture/compliance-truth.js +100 -0
- package/src/posture/evaluation/scan-child.js +3 -1
- package/src/posture/evaluation/why-missed.js +3 -1
- package/src/posture/exploitability-evidence.js +141 -0
- package/src/posture/fix-verify.js +4 -2
- package/src/posture/fleet/exposure-ranking.js +115 -0
- package/src/posture/fleet/service-chain-graph.js +150 -0
- package/src/posture/graph-reconciliation.js +154 -0
- package/src/posture/provenance/coverage.js +103 -0
- package/src/posture/provenance/git-evidence.js +84 -0
- package/src/posture/provenance/origin-resolver.js +7 -1
- package/src/posture/provenance/predicate-replay.js +14 -4
- package/src/posture/runtime-correlation.js +61 -15
- package/src/posture/runtime-explanation.js +116 -0
- package/src/posture/runtime-trace-ingest.js +194 -0
- package/src/posture/scan-checkpoint.js +38 -10
- package/src/posture/waf-ingest.js +7 -2
- package/src/report/index.js +4 -0
- package/src/report/oscal.js +38 -0
- package/src/report/perspectives.js +150 -0
- package/src/sast/_comment-strip.js +8 -1
- package/src/sast/ruby.js +26 -0
- package/src/util/string-memo.js +38 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export const id = 2773;
|
|
2
|
+
export const ids = [2773];
|
|
3
|
+
export const modules = {
|
|
4
|
+
|
|
5
|
+
/***/ 2773:
|
|
6
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
7
|
+
|
|
8
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
9
|
+
/* harmony export */ persistLanguageArtifacts: () => (/* binding */ persistLanguageArtifacts)
|
|
10
|
+
/* harmony export */ });
|
|
11
|
+
/* unused harmony exports LANGUAGE_ANALYSIS_FILE, LANGUAGE_BOM_FILE, LANGUAGE_ARTIFACT_SCHEMA, hasLanguageContent, buildLanguageArtifacts */
|
|
12
|
+
/* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77598);
|
|
13
|
+
/* harmony import */ var _posture_state_dir_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69259);
|
|
14
|
+
/* harmony import */ var _posture_integrity_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(37455);
|
|
15
|
+
/* harmony import */ var _context_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70634);
|
|
16
|
+
// Haskell and Nix analysis metadata as registered state artifacts (X-016).
|
|
17
|
+
//
|
|
18
|
+
// An ordinary scan already leaves last-scan.json. Two things a reviewer, auditor or retention policy needs about the
|
|
19
|
+
// Haskell/Nix side were not artifacts of their own: WHAT analysed the sources (parser/resolver outcomes, capability
|
|
20
|
+
// status, limitations, optional modes) and WHICH components the language BOM recorded. They are written beside
|
|
21
|
+
// last-scan.json as `language-analysis.json` and `language-bom.json`, each with a detached signature, registered in
|
|
22
|
+
// posture/artifact-registry.js (so export, retention, legal hold and `reset` all see them), and described by a record
|
|
23
|
+
// that carries sha256 digests of every input and of the artifact bodies. No source text is ever written: paths and
|
|
24
|
+
// digests only, so the file is metadata, not a copy of customer code.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
const LANGUAGE_ANALYSIS_FILE = 'language-analysis.json';
|
|
32
|
+
const LANGUAGE_BOM_FILE = 'language-bom.json';
|
|
33
|
+
const LANGUAGE_ARTIFACT_SCHEMA = 'agentic-security/language-analysis@1';
|
|
34
|
+
|
|
35
|
+
const sha256 = (s) => node_crypto__WEBPACK_IMPORTED_MODULE_0__.createHash('sha256').update(s).digest('hex');
|
|
36
|
+
function canonical(v) {
|
|
37
|
+
if (Array.isArray(v)) return `[${v.map(canonical).join(',')}]`;
|
|
38
|
+
if (v && typeof v === 'object') return `{${Object.keys(v).sort().map((k) => `${JSON.stringify(k)}:${canonical(v[k])}`).join(',')}}`;
|
|
39
|
+
return JSON.stringify(v === undefined ? null : v);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** True when the scan has anything Haskell/Nix to record. */
|
|
43
|
+
function hasLanguageContent(scan) {
|
|
44
|
+
const lc = scan && scan.scanHealth && scan.scanHealth.languageCoverage;
|
|
45
|
+
const files = lc && lc.totals && Number(lc.totals.files || lc.totals.analyzed || 0);
|
|
46
|
+
return !!((lc && (files > 0 || (lc.capabilities && Object.keys(lc.capabilities).length))) || (scan && scan.languageBom && (scan.languageBom.components || []).length));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The analysis record for `scan`, plus the BOM body. `inputs` is [{path, sha256}] over the Haskell/Nix sources and
|
|
51
|
+
* manifests (digests only). Pure: no I/O.
|
|
52
|
+
*/
|
|
53
|
+
function buildLanguageArtifacts(scan, inputs = []) {
|
|
54
|
+
const lc = (scan && scan.scanHealth && scan.scanHealth.languageCoverage) || null;
|
|
55
|
+
const bom = (scan && scan.languageBom) || null;
|
|
56
|
+
const bridges = (scan && scan.languageBridges) || null;
|
|
57
|
+
const bomBody = bom ? JSON.stringify(bom, null, 2) : null;
|
|
58
|
+
const record = {
|
|
59
|
+
schema: LANGUAGE_ARTIFACT_SCHEMA,
|
|
60
|
+
classification: { class: 'scan-metadata', containsSourceText: false, containsSecrets: false },
|
|
61
|
+
inputs: inputs.map((i) => ({ path: String(i.path), sha256: String(i.sha256) })).sort((a, b) => (a.path < b.path ? -1 : 1)),
|
|
62
|
+
analysis: lc ? { totals: lc.totals, byKind: lc.byKind, conditions: lc.conditions, limitations: lc.limitations, capabilities: lc.capabilities, optionalModes: lc.optionalModes } : null,
|
|
63
|
+
bom: bom ? { file: LANGUAGE_BOM_FILE, sha256: sha256(bomBody), components: (bom.components || []).length } : null,
|
|
64
|
+
bridges: bridges ? { sha256: sha256(canonical(bridges)) } : null,
|
|
65
|
+
};
|
|
66
|
+
return { record, bomBody };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Writes the artifacts under the project's state directory, each with a `.sig`. Returns {written:[{name, sha256}], skipped}.
|
|
71
|
+
* Never throws and never blocks a scan; a project that disallows state writes gets `skipped: 'state-writes-disabled'`.
|
|
72
|
+
*/
|
|
73
|
+
function persistLanguageArtifacts(root, scan) {
|
|
74
|
+
if (!hasLanguageContent(scan)) return { written: [], skipped: 'no-language-content' };
|
|
75
|
+
let inputs = [];
|
|
76
|
+
try {
|
|
77
|
+
const proj = (0,_context_js__WEBPACK_IMPORTED_MODULE_3__.loadLanguageProject)(root);
|
|
78
|
+
inputs = [...Object.entries(proj.files), ...Object.entries(proj.manifests)].map(([path, text]) => ({ path, sha256: sha256(text) }));
|
|
79
|
+
} catch { /* the record then says it has no input digests */ }
|
|
80
|
+
const { record, bomBody } = buildLanguageArtifacts(scan, inputs);
|
|
81
|
+
const out = [];
|
|
82
|
+
const put = (name, body) => {
|
|
83
|
+
const fp = (0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_1__.statePath)(root, name);
|
|
84
|
+
if (!(0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_1__.safeWriteState)(fp, body)) return false;
|
|
85
|
+
try { (0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_1__.safeWriteState)(`${fp}.sig`, (0,_posture_integrity_js__WEBPACK_IMPORTED_MODULE_2__/* .signLastScan */ .lU)(body)); } catch { /* the signature is best effort, as for last-scan.json */ }
|
|
86
|
+
out.push({ name, sha256: sha256(body) });
|
|
87
|
+
return true;
|
|
88
|
+
};
|
|
89
|
+
const body = JSON.stringify(record, null, 2);
|
|
90
|
+
if (!put(LANGUAGE_ANALYSIS_FILE, body)) return { written: [], skipped: 'state-writes-disabled' };
|
|
91
|
+
if (bomBody) put(LANGUAGE_BOM_FILE, bomBody);
|
|
92
|
+
return { written: out, skipped: null };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
/***/ })
|
|
97
|
+
|
|
98
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const id = 2775;
|
|
2
|
+
export const ids = [2775];
|
|
3
|
+
export const modules = {
|
|
4
|
+
|
|
5
|
+
/***/ 72775:
|
|
6
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
7
|
+
|
|
8
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
9
|
+
/* harmony export */ correlateImageVulnerabilities: () => (/* binding */ correlateImageVulnerabilities)
|
|
10
|
+
/* harmony export */ });
|
|
11
|
+
// PRD 5 P0 §4 — Container Vulnerability Analysis. "Reuse current SCA
|
|
12
|
+
// semantics where technically valid": this module does nothing but hand an
|
|
13
|
+
// image's OS + application package inventory to the engine's existing
|
|
14
|
+
// OSV/KEV/EPSS query path (`queryOSV`, imported via `src/sca/index.js`),
|
|
15
|
+
// exactly as every other SCA finding in this codebase gets its CVE data.
|
|
16
|
+
// `queryOSVFn` is injected (not imported directly) so this module's own
|
|
17
|
+
// tests never make a real network call and stay independent of engine.js's
|
|
18
|
+
// load cost; the CLI wiring passes the real `queryOSV`.
|
|
19
|
+
async function correlateImageVulnerabilities({ inventory, fileContents, queryOSVFn }) {
|
|
20
|
+
if (typeof queryOSVFn !== 'function') throw new TypeError('correlateImageVulnerabilities requires queryOSVFn(components, allFileContents)');
|
|
21
|
+
if (!inventory || !Array.isArray(inventory.osPackages) || !Array.isArray(inventory.appPackages)) {
|
|
22
|
+
throw new TypeError('correlateImageVulnerabilities requires an inventory with osPackages/appPackages arrays');
|
|
23
|
+
}
|
|
24
|
+
const components = [...inventory.osPackages, ...inventory.appPackages];
|
|
25
|
+
const vulnerabilities = await queryOSVFn(components, fileContents || {});
|
|
26
|
+
return { components, vulnerabilities };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
/***/ })
|
|
31
|
+
|
|
32
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
export const id = 2966;
|
|
2
|
+
export const ids = [2966];
|
|
3
|
+
export const modules = {
|
|
4
|
+
|
|
5
|
+
/***/ 72966:
|
|
6
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
7
|
+
|
|
8
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
9
|
+
/* harmony export */ queryFindingsMemory: () => (/* binding */ queryFindingsMemory)
|
|
10
|
+
/* harmony export */ });
|
|
11
|
+
/* unused harmony export _internals */
|
|
12
|
+
/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(73024);
|
|
13
|
+
/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(76760);
|
|
14
|
+
/* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(69259);
|
|
15
|
+
// Findings memory — natural-language Q&A over the institutional knowledge
|
|
16
|
+
// the scanner has accumulated. Backs the MCP query_findings_memory tool.
|
|
17
|
+
//
|
|
18
|
+
// Sources searched, in this order:
|
|
19
|
+
//
|
|
20
|
+
// 1. .agentic-security/last-scan.json current findings
|
|
21
|
+
// 2. .agentic-security/triage-memory.jsonl past wont-fix / FP decisions
|
|
22
|
+
// 3. .agentic-security/scan-history/*.json prior scans
|
|
23
|
+
// 4. .agentic-security/AGENTS.md continual-learning narrative
|
|
24
|
+
//
|
|
25
|
+
// Naive keyword matching for v1. Each match has a `score` (count of query
|
|
26
|
+
// terms matched) and a `source` ('finding' | 'triage' | 'history' |
|
|
27
|
+
// 'agents-md'). Returns top-10 by score.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
function _read(scanRoot, name) {
|
|
35
|
+
try { return node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync((0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.statePath)(scanRoot, name), 'utf8'); } catch { return null; }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function _readJson(scanRoot, name) {
|
|
39
|
+
const raw = _read(scanRoot, name);
|
|
40
|
+
if (!raw) return null;
|
|
41
|
+
try { return JSON.parse(raw); } catch { return null; }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function _terms(query) {
|
|
45
|
+
return String(query || '').toLowerCase().split(/\s+/).filter(t => t.length >= 2);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function _score(haystack, terms) {
|
|
49
|
+
const lower = String(haystack || '').toLowerCase();
|
|
50
|
+
let s = 0;
|
|
51
|
+
for (const t of terms) if (lower.includes(t)) s++;
|
|
52
|
+
return s;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function _findingHaystack(f) {
|
|
56
|
+
return [f.vuln, f.family, f.file, f.severity, f.description, f.cwe, f.id]
|
|
57
|
+
.filter(Boolean).join(' | ');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function _truncate(s, n = 160) {
|
|
61
|
+
return String(s || '').replace(/\s+/g, ' ').slice(0, n);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Run a natural-language query over the scanner's accumulated memory.
|
|
66
|
+
*/
|
|
67
|
+
function queryFindingsMemory(scanRoot, query) {
|
|
68
|
+
const terms = _terms(query);
|
|
69
|
+
if (!terms.length) return { results: [], count: 0 };
|
|
70
|
+
|
|
71
|
+
const results = [];
|
|
72
|
+
|
|
73
|
+
// 1. Current findings.
|
|
74
|
+
const scan = _readJson(scanRoot, 'last-scan.json');
|
|
75
|
+
if (scan && Array.isArray(scan.findings)) {
|
|
76
|
+
for (const f of scan.findings) {
|
|
77
|
+
const hay = _findingHaystack(f);
|
|
78
|
+
const score = _score(hay, terms);
|
|
79
|
+
if (!score) continue;
|
|
80
|
+
results.push({
|
|
81
|
+
source: 'finding',
|
|
82
|
+
score,
|
|
83
|
+
finding_id: f.id || null,
|
|
84
|
+
severity: f.severity,
|
|
85
|
+
family: f.family,
|
|
86
|
+
file: f.file,
|
|
87
|
+
line: f.line,
|
|
88
|
+
snippet: _truncate(f.vuln || f.description || f.family),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 2. Triage memory (past decisions).
|
|
94
|
+
const triageRaw = _read(scanRoot, 'triage-memory.jsonl');
|
|
95
|
+
if (triageRaw) {
|
|
96
|
+
const lines = triageRaw.split('\n').filter(Boolean);
|
|
97
|
+
for (const ln of lines) {
|
|
98
|
+
let entry; try { entry = JSON.parse(ln); } catch { continue; }
|
|
99
|
+
const hay = [entry.decision, entry.reason, entry.family, entry.vuln, entry.file].join(' ');
|
|
100
|
+
const score = _score(hay, terms);
|
|
101
|
+
if (!score) continue;
|
|
102
|
+
results.push({
|
|
103
|
+
source: 'triage',
|
|
104
|
+
score,
|
|
105
|
+
decision: entry.decision,
|
|
106
|
+
at: entry.at,
|
|
107
|
+
family: entry.family,
|
|
108
|
+
snippet: _truncate(entry.reason || entry.vuln),
|
|
109
|
+
bucket: entry.bucket,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 3. Scan history.
|
|
115
|
+
try {
|
|
116
|
+
const histDir = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.statePath)(scanRoot, 'scan-history');
|
|
117
|
+
if (node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(histDir)) {
|
|
118
|
+
const files = node_fs__WEBPACK_IMPORTED_MODULE_0__.readdirSync(histDir).filter(f => f.endsWith('.json')).slice(-10);
|
|
119
|
+
for (const f of files) {
|
|
120
|
+
try {
|
|
121
|
+
const hist = JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(node_path__WEBPACK_IMPORTED_MODULE_1__.join(histDir, f), 'utf8'));
|
|
122
|
+
if (!Array.isArray(hist.findings)) continue;
|
|
123
|
+
for (const x of hist.findings.slice(0, 50)) {
|
|
124
|
+
const hay = _findingHaystack(x);
|
|
125
|
+
const score = _score(hay, terms);
|
|
126
|
+
if (!score) continue;
|
|
127
|
+
results.push({
|
|
128
|
+
source: 'history',
|
|
129
|
+
score,
|
|
130
|
+
from: f.replace(/\.json$/, ''),
|
|
131
|
+
severity: x.severity,
|
|
132
|
+
family: x.family,
|
|
133
|
+
file: x.file,
|
|
134
|
+
snippet: _truncate(x.vuln || x.description),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
} catch {}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} catch {}
|
|
141
|
+
|
|
142
|
+
// 4. AGENTS.md narrative.
|
|
143
|
+
const agents = _read(scanRoot, 'AGENTS.md');
|
|
144
|
+
if (agents) {
|
|
145
|
+
const sections = agents.split(/^##\s+/m);
|
|
146
|
+
for (const sec of sections) {
|
|
147
|
+
const score = _score(sec, terms);
|
|
148
|
+
if (!score) continue;
|
|
149
|
+
const title = sec.split('\n')[0] || '';
|
|
150
|
+
results.push({
|
|
151
|
+
source: 'agents-md',
|
|
152
|
+
score,
|
|
153
|
+
title: _truncate(title, 80),
|
|
154
|
+
snippet: _truncate(sec.replace(title, ''), 200),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Top-10 by score, ties broken by source priority (finding > triage >
|
|
160
|
+
// history > agents-md so live data wins).
|
|
161
|
+
const PRI = { finding: 4, triage: 3, history: 2, 'agents-md': 1 };
|
|
162
|
+
results.sort((a, b) => (b.score - a.score) || (PRI[b.source] - PRI[a.source]));
|
|
163
|
+
return { results: results.slice(0, 10), count: results.length };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const _internals = { _terms, _score, _findingHaystack };
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
/***/ })
|
|
170
|
+
|
|
171
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export const id = 2972;
|
|
2
|
+
export const ids = [2972];
|
|
3
|
+
export const modules = {
|
|
4
|
+
|
|
5
|
+
/***/ 72972:
|
|
6
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
7
|
+
|
|
8
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
9
|
+
/* harmony export */ checkSecretLive: () => (/* binding */ checkSecretLive)
|
|
10
|
+
/* harmony export */ });
|
|
11
|
+
/* unused harmony export _internal */
|
|
12
|
+
// Live-secret validation (#22) — label a detected secret live | dead | unknown.
|
|
13
|
+
//
|
|
14
|
+
// "This Stripe/GitHub key is LIVE and was committed 40 commits ago" is a P0 the
|
|
15
|
+
// vibecoder must rotate now; "you have a high-entropy string" is noise. This
|
|
16
|
+
// closes that gap for the providers with a cheap, read-only "whoami" check.
|
|
17
|
+
//
|
|
18
|
+
// STRICTLY opt-in (a --validate-secrets flag / AGENTIC_SECURITY_VALIDATE_SECRETS)
|
|
19
|
+
// and OFFLINE-DEGRADING: any network error, timeout, or unrecognized provider
|
|
20
|
+
// yields 'unknown' — never a false 'dead'. No runtime cloud calls by default,
|
|
21
|
+
// per the scanner's no-network-by-default convention. The request builder is
|
|
22
|
+
// pure (no I/O) so it's testable without hitting a provider.
|
|
23
|
+
|
|
24
|
+
// Map a detected secret to a read-only validation request, or null when we have
|
|
25
|
+
// no safe check for that provider. Only providers whose token is a self-
|
|
26
|
+
// contained bearer/token credential (no signing, no extra params) are covered.
|
|
27
|
+
function buildLiveCheckRequest(secret) {
|
|
28
|
+
const val = (secret && (secret.match || secret.value || secret.secret || secret.token)) || '';
|
|
29
|
+
if (typeof val !== 'string' || val.length < 8) return null;
|
|
30
|
+
|
|
31
|
+
// GitHub PAT / OAuth token → GET /user (200 = live, 401 = dead).
|
|
32
|
+
if (/^gh[posru]_[A-Za-z0-9]{20,}$/.test(val) || /^github_pat_[A-Za-z0-9_]{20,}$/.test(val)) {
|
|
33
|
+
return { provider: 'github', method: 'GET', url: 'https://api.github.com/user',
|
|
34
|
+
headers: { Authorization: `token ${val}`, 'User-Agent': 'agentic-security', Accept: 'application/vnd.github+json' } };
|
|
35
|
+
}
|
|
36
|
+
// Stripe secret key → GET /v1/account (200 = live, 401 = dead).
|
|
37
|
+
if (/^sk_live_[A-Za-z0-9]{16,}$/.test(val) || /^rk_live_[A-Za-z0-9]{16,}$/.test(val)) {
|
|
38
|
+
return { provider: 'stripe', method: 'GET', url: 'https://api.stripe.com/v1/account',
|
|
39
|
+
headers: { Authorization: `Bearer ${val}` } };
|
|
40
|
+
}
|
|
41
|
+
// OpenAI key → GET /v1/models.
|
|
42
|
+
if (/^sk-[A-Za-z0-9]{20,}$/.test(val) && !/^sk_live_/.test(val)) {
|
|
43
|
+
return { provider: 'openai', method: 'GET', url: 'https://api.openai.com/v1/models',
|
|
44
|
+
headers: { Authorization: `Bearer ${val}` } };
|
|
45
|
+
}
|
|
46
|
+
// SendGrid key → GET /v3/scopes.
|
|
47
|
+
if (/^SG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}$/.test(val)) {
|
|
48
|
+
return { provider: 'sendgrid', method: 'GET', url: 'https://api.sendgrid.com/v3/scopes',
|
|
49
|
+
headers: { Authorization: `Bearer ${val}` } };
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Classify an HTTP status into a liveness verdict. 200-2xx = live; 401/403 =
|
|
55
|
+
// dead (rejected credential); anything else = unknown (rate-limit, 5xx, etc. —
|
|
56
|
+
// we don't know, so don't claim dead).
|
|
57
|
+
function classifyStatus(status) {
|
|
58
|
+
if (status >= 200 && status < 300) return 'live';
|
|
59
|
+
if (status === 401 || status === 403) return 'dead';
|
|
60
|
+
return 'unknown';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Perform the validation. Returns { verdict: 'live'|'dead'|'unknown', provider }.
|
|
64
|
+
// Offline-degrading: on any error/timeout, verdict is 'unknown'.
|
|
65
|
+
async function checkSecretLive(secret, { timeoutMs = 4000 } = {}) {
|
|
66
|
+
const req = buildLiveCheckRequest(secret);
|
|
67
|
+
if (!req) return { verdict: 'unknown', provider: null };
|
|
68
|
+
const ctrl = new AbortController();
|
|
69
|
+
const t = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
70
|
+
try {
|
|
71
|
+
const r = await fetch(req.url, { method: req.method, headers: req.headers, signal: ctrl.signal });
|
|
72
|
+
return { verdict: classifyStatus(r.status), provider: req.provider };
|
|
73
|
+
} catch {
|
|
74
|
+
return { verdict: 'unknown', provider: req.provider };
|
|
75
|
+
} finally {
|
|
76
|
+
clearTimeout(t);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Pure surfaces exposed for tests (no network) — kept off the public API so the
|
|
81
|
+
// dead-module guard doesn't flag them; `checkSecretLive` is the wired entry.
|
|
82
|
+
const _internal = { buildLiveCheckRequest, classifyStatus };
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
/***/ })
|
|
86
|
+
|
|
87
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export const id = 2991;
|
|
2
|
+
export const ids = [2991];
|
|
3
|
+
export const modules = {
|
|
4
|
+
|
|
5
|
+
/***/ 2991:
|
|
6
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
7
|
+
|
|
8
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
9
|
+
/* harmony export */ diffScans: () => (/* binding */ diffScans),
|
|
10
|
+
/* harmony export */ renderDiff: () => (/* binding */ renderDiff)
|
|
11
|
+
/* harmony export */ });
|
|
12
|
+
/* unused harmony export summarizeDiff */
|
|
13
|
+
// Scan-result baseline comparison.
|
|
14
|
+
//
|
|
15
|
+
// Distinct from agentic-security-diff (which runs two SCANNER VERSIONS
|
|
16
|
+
// against the same code). This module diffs TWO SCAN RESULTS, regardless
|
|
17
|
+
// of scanner version — i.e., it compares findings between yesterday's
|
|
18
|
+
// scan and today's scan to surface what was introduced / what was fixed.
|
|
19
|
+
//
|
|
20
|
+
// Keys per-finding on `stableId` when present (refactor-stable), else on
|
|
21
|
+
// `(file, line, family)`. Output:
|
|
22
|
+
// {
|
|
23
|
+
// added: [...], // present in current, absent in previous
|
|
24
|
+
// removed: [...], // present in previous, absent in current
|
|
25
|
+
// changed: [...], // same key, different severity / verdict
|
|
26
|
+
// unchanged: <count>
|
|
27
|
+
// }
|
|
28
|
+
|
|
29
|
+
function _keyOf(f) {
|
|
30
|
+
if (!f || typeof f !== 'object') return '';
|
|
31
|
+
if (f.stableId) return `sid:${f.stableId}`;
|
|
32
|
+
return `pos:${f.file || '?'}:${f.line || 0}:${f.family || f.vuln || '?'}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function _indexBy(findings, keyFn) {
|
|
36
|
+
const m = new Map();
|
|
37
|
+
if (!Array.isArray(findings)) return m;
|
|
38
|
+
for (const f of findings) {
|
|
39
|
+
const k = keyFn(f);
|
|
40
|
+
if (!k) continue;
|
|
41
|
+
if (!m.has(k)) m.set(k, f);
|
|
42
|
+
}
|
|
43
|
+
return m;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function diffScans(previous, current) {
|
|
47
|
+
const prevFindings = (previous && Array.isArray(previous.findings)) ? previous.findings : [];
|
|
48
|
+
const currFindings = (current && Array.isArray(current.findings)) ? current.findings : [];
|
|
49
|
+
const prevIdx = _indexBy(prevFindings, _keyOf);
|
|
50
|
+
const currIdx = _indexBy(currFindings, _keyOf);
|
|
51
|
+
|
|
52
|
+
const added = [], removed = [], changed = [];
|
|
53
|
+
let unchanged = 0;
|
|
54
|
+
for (const [k, c] of currIdx) {
|
|
55
|
+
const p = prevIdx.get(k);
|
|
56
|
+
if (!p) { added.push(c); continue; }
|
|
57
|
+
const sevChanged = p.severity !== c.severity;
|
|
58
|
+
const verdictChanged = p.mitigationVerdict !== c.mitigationVerdict;
|
|
59
|
+
const validatorChanged = p.validator_verdict !== c.validator_verdict;
|
|
60
|
+
if (sevChanged || verdictChanged || validatorChanged) {
|
|
61
|
+
changed.push({ before: p, after: c, fields: {
|
|
62
|
+
severity: sevChanged ? [p.severity, c.severity] : null,
|
|
63
|
+
mitigationVerdict: verdictChanged ? [p.mitigationVerdict, c.mitigationVerdict] : null,
|
|
64
|
+
validator_verdict: validatorChanged ? [p.validator_verdict, c.validator_verdict] : null,
|
|
65
|
+
}});
|
|
66
|
+
} else {
|
|
67
|
+
unchanged++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
for (const [k, p] of prevIdx) {
|
|
71
|
+
if (!currIdx.has(k)) removed.push(p);
|
|
72
|
+
}
|
|
73
|
+
return { added, removed, changed, unchanged };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function summarizeDiff(diff) {
|
|
77
|
+
const bySev = { critical: { added: 0, removed: 0 }, high: { added: 0, removed: 0 }, medium: { added: 0, removed: 0 }, low: { added: 0, removed: 0 } };
|
|
78
|
+
for (const f of diff.added) if (bySev[f.severity]) bySev[f.severity].added++;
|
|
79
|
+
for (const f of diff.removed) if (bySev[f.severity]) bySev[f.severity].removed++;
|
|
80
|
+
return {
|
|
81
|
+
addedCount: diff.added.length,
|
|
82
|
+
removedCount: diff.removed.length,
|
|
83
|
+
changedCount: diff.changed.length,
|
|
84
|
+
unchangedCount: diff.unchanged,
|
|
85
|
+
bySeverity: bySev,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function renderDiff(diff, opts = {}) {
|
|
90
|
+
const color = opts.color !== false;
|
|
91
|
+
const C = color ? { RED: '\x1b[31m', GREEN: '\x1b[32m', YELLOW: '\x1b[33m', DIM: '\x1b[2m', BOLD: '\x1b[1m', RESET: '\x1b[0m' } : { RED:'', GREEN:'', YELLOW:'', DIM:'', BOLD:'', RESET:'' };
|
|
92
|
+
const sum = summarizeDiff(diff);
|
|
93
|
+
const out = [];
|
|
94
|
+
out.push('');
|
|
95
|
+
out.push(`${C.BOLD}Scan-result diff${C.RESET}`);
|
|
96
|
+
out.push(` ${C.RED}+${sum.addedCount} added${C.RESET} ${C.GREEN}-${sum.removedCount} removed${C.RESET} ${C.YELLOW}~${sum.changedCount} changed${C.RESET} ${C.DIM}${sum.unchangedCount} unchanged${C.RESET}`);
|
|
97
|
+
out.push('');
|
|
98
|
+
if (diff.added.length) {
|
|
99
|
+
out.push(`${C.RED}${C.BOLD}Added (${diff.added.length})${C.RESET}`);
|
|
100
|
+
for (const f of diff.added.slice(0, 25)) {
|
|
101
|
+
out.push(` ${C.RED}+${C.RESET} [${(f.severity || '').toUpperCase()}] ${(f.vuln || '').slice(0, 60)} ${C.DIM}${f.file || '?'}:${f.line || 0}${C.RESET}`);
|
|
102
|
+
}
|
|
103
|
+
if (diff.added.length > 25) out.push(` ${C.DIM}... and ${diff.added.length - 25} more${C.RESET}`);
|
|
104
|
+
out.push('');
|
|
105
|
+
}
|
|
106
|
+
if (diff.removed.length) {
|
|
107
|
+
out.push(`${C.GREEN}${C.BOLD}Removed (${diff.removed.length})${C.RESET}`);
|
|
108
|
+
for (const f of diff.removed.slice(0, 25)) {
|
|
109
|
+
out.push(` ${C.GREEN}-${C.RESET} [${(f.severity || '').toUpperCase()}] ${(f.vuln || '').slice(0, 60)} ${C.DIM}${f.file || '?'}:${f.line || 0}${C.RESET}`);
|
|
110
|
+
}
|
|
111
|
+
if (diff.removed.length > 25) out.push(` ${C.DIM}... and ${diff.removed.length - 25} more${C.RESET}`);
|
|
112
|
+
out.push('');
|
|
113
|
+
}
|
|
114
|
+
if (diff.changed.length) {
|
|
115
|
+
out.push(`${C.YELLOW}${C.BOLD}Changed (${diff.changed.length})${C.RESET}`);
|
|
116
|
+
for (const c of diff.changed.slice(0, 15)) {
|
|
117
|
+
const sevDelta = c.fields.severity ? `${c.fields.severity[0]} → ${c.fields.severity[1]}` : '';
|
|
118
|
+
const verdictDelta = c.fields.mitigationVerdict ? `verdict ${c.fields.mitigationVerdict[0]} → ${c.fields.mitigationVerdict[1]}` : '';
|
|
119
|
+
const validatorDelta = c.fields.validator_verdict ? `validator ${c.fields.validator_verdict[0]} → ${c.fields.validator_verdict[1]}` : '';
|
|
120
|
+
const delta = [sevDelta, verdictDelta, validatorDelta].filter(Boolean).join('; ');
|
|
121
|
+
out.push(` ${C.YELLOW}~${C.RESET} ${(c.after.vuln || '').slice(0, 50)} ${C.DIM}${c.after.file || '?'}:${c.after.line || 0}${C.RESET} ${delta}`);
|
|
122
|
+
}
|
|
123
|
+
out.push('');
|
|
124
|
+
}
|
|
125
|
+
return out.join('\n');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
/***/ })
|
|
130
|
+
|
|
131
|
+
};
|