@clear-capabilities/agentic-security-scanner 0.160.0 → 0.162.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 +38 -0
- package/bin/agentic-security.js +11 -2
- package/dist/1919.index.js +1 -1
- package/dist/2432.index.js +10 -1
- package/dist/4113.index.js +60 -9
- package/dist/5613.index.js +500 -0
- package/dist/6239.index.js +469 -0
- package/dist/7033.index.js +4 -1
- package/dist/7838.index.js +39 -3
- package/dist/8714.index.js +1 -1
- package/dist/8798.index.js +2 -2
- package/dist/9207.index.js +5 -0
- package/dist/9560.index.js +205 -62
- package/dist/{1905.index.js → 9999.index.js} +265 -8
- package/dist/agentic-security.mjs +12 -12
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +14 -9
- package/src/dataflow/engine.js +20 -4
- package/src/discovery/CLAUDE.md +9 -0
- package/src/discovery/index.js +28 -0
- package/src/discovery/memory.js +7 -5
- package/src/engine.js +29 -8
- package/src/fix/apply-fix-service.js +33 -3
- package/src/language/CLAUDE.md +6 -1
- package/src/language/aibom.js +4 -1
- package/src/language/fix-lifecycle.js +9 -0
- package/src/language/haskell-ir.js +47 -7
- package/src/language/haskell-sca.js +87 -7
- package/src/language/haskell-supply.js +175 -6
- package/src/language/haskell-web.js +200 -18
- package/src/language/nix-fix.js +1 -1
- package/src/language/resolved-pass.js +4 -1
- package/src/language/witness.js +1 -1
- package/src/mcp/CLAUDE.md +43 -4
- package/src/mcp/tools.js +185 -43
- package/src/posture/CLAUDE.md +174 -0
- package/src/posture/assurance/baseline-inventory.js +218 -0
- package/src/posture/assurance/baseline.js +322 -0
- package/src/posture/assurance/bounded-io.js +145 -0
- package/src/posture/assurance/config.js +299 -0
- package/src/posture/assurance/contracts.js +315 -0
- package/src/posture/assurance/identity.js +62 -0
- package/src/posture/assurance/migrations.js +259 -0
- package/src/posture/assurance/schema-kit.js +111 -0
- package/src/posture/assurance/verification-record.js +258 -0
- package/src/posture/attestation.js +5 -0
- package/src/posture/autopilot.js +28 -1
- package/src/posture/blast-radius.js +15 -1
- package/src/posture/evidence-bundle.js +93 -7
- package/src/posture/execution-proof.js +11 -1
- package/src/posture/fix-verify-loop.js +20 -6
- package/src/posture/fix-verify.js +37 -2
- package/src/posture/oracles/adapters.js +360 -0
- package/src/posture/oracles/conformance.js +275 -0
- package/src/posture/oracles/oracle.js +433 -0
- package/src/posture/oracles/registry.js +76 -0
- package/src/posture/oracles/scenario-classes.js +223 -0
- package/src/posture/remediation-ledger.js +10 -1
- package/src/posture/replay/replay.js +249 -0
- package/src/posture/verification/advisory-state.js +71 -0
- package/src/posture/verification/emit.js +257 -0
- package/src/posture/verification/hypothesis-promotion.js +211 -0
- package/src/posture/verification/patch-negative.js +214 -0
- package/src/posture/verification/patch-promotion.js +105 -0
- package/src/posture/verification/projection.js +183 -0
- package/src/posture/verification/repair-records.js +124 -0
- package/src/posture/verifier.js +16 -0
- package/src/report/index.js +49 -0
- package/src/sandbox/CLAUDE.md +41 -0
- package/src/sandbox/backend-namespace.js +7 -0
- package/src/sandbox/backend-userspace.js +68 -29
- package/src/sandbox/control-probes.js +215 -0
- package/src/sandbox/supervise.js +201 -0
- package/src/sandbox/trust-boundary.js +113 -0
- package/src/sandbox/trust-domains.js +171 -0
- package/src/sast/pipeline.js +51 -4
package/dist/9560.index.js
CHANGED
|
@@ -637,12 +637,13 @@ __webpack_require__.a(__webpack_module__, async (__webpack_handle_async_dependen
|
|
|
637
637
|
/* harmony import */ var _posture_integrity_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(71130);
|
|
638
638
|
/* harmony import */ var _posture_state_dir_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(31174);
|
|
639
639
|
/* harmony import */ var _posture_cache_economics_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(58752);
|
|
640
|
-
/* harmony import */ var
|
|
640
|
+
/* harmony import */ var _redact_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(83468);
|
|
641
641
|
/* harmony import */ var _report_index_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(75917);
|
|
642
|
-
/* harmony import */ var
|
|
643
|
-
/* harmony import */ var
|
|
644
|
-
/* harmony import */ var
|
|
645
|
-
/* harmony import */ var
|
|
642
|
+
/* harmony import */ var _posture_verification_projection_js__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(51864);
|
|
643
|
+
/* harmony import */ var _posture_provenance_schema_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(34594);
|
|
644
|
+
/* harmony import */ var _dataflow_tools_js__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(20095);
|
|
645
|
+
/* harmony import */ var _posture_agents_memory_js__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(79907);
|
|
646
|
+
/* harmony import */ var _posture_cve_lookup_js__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(71364);
|
|
646
647
|
var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([_report_index_js__WEBPACK_IMPORTED_MODULE_12__]);
|
|
647
648
|
_report_index_js__WEBPACK_IMPORTED_MODULE_12__ = (__webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__)[0];
|
|
648
649
|
// MCP tool implementations — PRD Feature 2, hardened against the OWASP MCP
|
|
@@ -673,6 +674,8 @@ _report_index_js__WEBPACK_IMPORTED_MODULE_12__ = (__webpack_async_dependencies__
|
|
|
673
674
|
|
|
674
675
|
|
|
675
676
|
|
|
677
|
+
// X-206: the one projection of the verification record, shared with the JSON report, the text report and the autopilot response.
|
|
678
|
+
|
|
676
679
|
// Git-origin provenance (Finding Provenance M0/M1). Distinct from
|
|
677
680
|
// `finding.provenance` (AI-authorship) and from an SCA entry's `provenance`
|
|
678
681
|
// (Sigstore/SLSA attestation) — see report/index.js's import comment.
|
|
@@ -996,7 +999,7 @@ function _maybeOffload(sessionRoot, toolName, items) {
|
|
|
996
999
|
//
|
|
997
1000
|
// Exported under the `_internals` convention this codebase already uses
|
|
998
1001
|
// (see posture/poc-inprocess.js). Not part of the MCP tool surface.
|
|
999
|
-
const _internals = { _confine, isReservedWrite: _isReservedWritePath };
|
|
1002
|
+
const _internals = { _confine, isReservedWrite: _isReservedWritePath, checkMultiFileTargets: _checkMultiFileTargets, multiFilePlanDigest: _multiFilePlanDigest };
|
|
1000
1003
|
|
|
1001
1004
|
const scan_diff = {
|
|
1002
1005
|
name: 'scan_diff',
|
|
@@ -1057,7 +1060,7 @@ const scan_diff = {
|
|
|
1057
1060
|
// Haskell and Nix results depend on imported modules and manifests, so the files asked about are scanned together
|
|
1058
1061
|
// with their import closure (read from disk, bounded). Findings are still reported only for the requested files.
|
|
1059
1062
|
const wantSet = new Set(Object.keys(fileContents));
|
|
1060
|
-
const { withLanguageContext } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__,
|
|
1063
|
+
const { withLanguageContext } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 52603));
|
|
1061
1064
|
const lc = withLanguageContext(sessionRoot, fileContents, {});
|
|
1062
1065
|
const result = await (0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_10__/* .withStateWritesDisabled */ .Ao)(() =>
|
|
1063
1066
|
runScan(sessionRoot, { network: false, fileContents: lc.fileContents, depFileContents: lc.depFileContents, deep: true, deepInCi: true }));
|
|
@@ -1085,7 +1088,7 @@ const scan_diff = {
|
|
|
1085
1088
|
// scanning), left as-is rather than silently claimed fixed here.
|
|
1086
1089
|
const findings = (0,_report_index_js__WEBPACK_IMPORTED_MODULE_12__.normalizeFindings)(result.scan)
|
|
1087
1090
|
.filter(f => wantSet.has(String(f.file || '').replace(/\\/g, '/')) && (sevRank[f.severity] ?? 0) >= min)
|
|
1088
|
-
.map(f => (0,
|
|
1091
|
+
.map(f => (0,_redact_js__WEBPACK_IMPORTED_MODULE_16__/* .redactFinding */ .lE)({
|
|
1089
1092
|
id: f.id, severity: f.severity, file: f.file, line: f.line,
|
|
1090
1093
|
title: f.vuln, cwe: f.cwe,
|
|
1091
1094
|
description: f.description, remediation: f.remediation,
|
|
@@ -1148,7 +1151,7 @@ const query_taint = {
|
|
|
1148
1151
|
const hay = [f.description, f.title, f.vuln, f.snippet, JSON.stringify(f.trace || '')].join(' ').toLowerCase();
|
|
1149
1152
|
return hay.includes(srcL) && hay.includes(sinkL);
|
|
1150
1153
|
});
|
|
1151
|
-
const page = all.slice(off, off + lim).map(f => (0,
|
|
1154
|
+
const page = all.slice(off, off + lim).map(f => (0,_redact_js__WEBPACK_IMPORTED_MODULE_16__/* .redactFinding */ .lE)({
|
|
1152
1155
|
id: f.id, severity: f.severity, file: f.file, line: f.line,
|
|
1153
1156
|
title: f.title || f.vuln, description: f.description,
|
|
1154
1157
|
trace: f.trace || null,
|
|
@@ -1186,7 +1189,7 @@ const explain_finding = {
|
|
|
1186
1189
|
if (!scan) throw new Error(`No usable scan state (${status}).`);
|
|
1187
1190
|
const f = _findById(scan, finding_id);
|
|
1188
1191
|
if (!f) throw new Error(`Finding not found: ${finding_id}`);
|
|
1189
|
-
const redacted = (0,
|
|
1192
|
+
const redacted = (0,_redact_js__WEBPACK_IMPORTED_MODULE_16__/* .redactFinding */ .lE)({
|
|
1190
1193
|
id: f.id, severity: f.severity, file: f.file, line: f.line,
|
|
1191
1194
|
title: f.title || f.vuln, cwe: f.cwe,
|
|
1192
1195
|
description: f.description, remediation: f.remediation,
|
|
@@ -1243,17 +1246,164 @@ const explain_finding = {
|
|
|
1243
1246
|
// logins/CODEOWNERS lines) through this MCP surface because this call
|
|
1244
1247
|
// passed no options object at all, silently defeating their policy at
|
|
1245
1248
|
// this one output boundary while report/index.js honoured it.
|
|
1246
|
-
findingProvenance: f.findingProvenance ? (0,
|
|
1249
|
+
findingProvenance: f.findingProvenance ? (0,_posture_provenance_schema_js__WEBPACK_IMPORTED_MODULE_14__/* .redactFindingProvenance */ .As)(f.findingProvenance, {
|
|
1247
1250
|
pseudonymize: process.env.AGENTIC_SECURITY_PSEUDONYMIZE_AUTHORS === '1',
|
|
1248
1251
|
}) : null,
|
|
1252
|
+
// X-206: the same verification projection the JSON and text reports carry; absent when the finding has no record.
|
|
1253
|
+
...(f.verificationRecord && typeof f.verificationRecord === 'object'
|
|
1254
|
+
? { verificationRecord: f.verificationRecord, ...(0,_posture_verification_projection_js__WEBPACK_IMPORTED_MODULE_13__/* .verificationFields */ .ze)(f.verificationRecord, { replay: f.verificationReplay }) }
|
|
1255
|
+
: {}),
|
|
1249
1256
|
};
|
|
1250
1257
|
},
|
|
1251
1258
|
};
|
|
1252
1259
|
|
|
1260
|
+
// FR-307/FR-1002/FR-1003: the high-impact-change approval gate (approval evidence, approver identity, separation of duties),
|
|
1261
|
+
// shared by the caller-patch branch and the multi-file plan branch so the two cannot drift. Returns a refusal result, or null
|
|
1262
|
+
// when the change is not high-impact or is properly approved.
|
|
1263
|
+
function _highImpactApprovalRefusal(ctx, materialClassification, fixMeta) {
|
|
1264
|
+
if (!materialClassification.highImpactCategories.length) return null;
|
|
1265
|
+
const cats = materialClassification.highImpactCategories.join(', ');
|
|
1266
|
+
const approval = fixMeta && typeof fixMeta === 'object' ? fixMeta.approval : null;
|
|
1267
|
+
const hasApprovalEvidence = !!(approval && typeof approval === 'object' &&
|
|
1268
|
+
typeof approval.approvedBy === 'string' && approval.approvedBy.trim().length > 0 &&
|
|
1269
|
+
typeof approval.reason === 'string' && approval.reason.trim().length > 0);
|
|
1270
|
+
if (!hasApprovalEvidence) {
|
|
1271
|
+
return {
|
|
1272
|
+
_meta: META, applied: false,
|
|
1273
|
+
reason: `high-impact change (${cats}) requires approval evidence — pass fixMeta.approval: {approvedBy, reason} — before it can be applied`,
|
|
1274
|
+
materialClassification,
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
const approverRegistry = (0,_fix_approver_registry_js__WEBPACK_IMPORTED_MODULE_7__.loadApproverRegistry)(ctx.sessionRoot);
|
|
1278
|
+
const requiredRoles = (0,_fix_approver_registry_js__WEBPACK_IMPORTED_MODULE_7__/* .requiredRolesFor */ .K)(approverRegistry, materialClassification.highImpactCategories);
|
|
1279
|
+
const identityCheck = (0,_fix_approver_registry_js__WEBPACK_IMPORTED_MODULE_7__.verifyApprover)(approverRegistry, approval.approvedBy, requiredRoles);
|
|
1280
|
+
if (!identityCheck.verified) {
|
|
1281
|
+
return { _meta: META, applied: false, reason: `high-impact change (${cats}) approval rejected: ${identityCheck.reason}`, materialClassification };
|
|
1282
|
+
}
|
|
1283
|
+
// FR-1003: separation-of-duties, same no-op-unless-configured gate as apply-fix-service.js's own copy; see approver-registry.js.
|
|
1284
|
+
const sodCheck = (0,_fix_approver_registry_js__WEBPACK_IMPORTED_MODULE_7__.checkSeparationOfDuties)(approverRegistry, fixMeta?.author, approval.approvedBy);
|
|
1285
|
+
if (!sodCheck.ok) {
|
|
1286
|
+
return { _meta: META, applied: false, reason: `high-impact change (${cats}) approval rejected: ${sodCheck.reason}`, materialClassification };
|
|
1287
|
+
}
|
|
1288
|
+
return null;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// ─── Multi-file plans (apply_fix with plan_digest) ───────────────────────────
|
|
1292
|
+
// A NixOS option fix can edit several files at once (every definition at the winning priority). apply_fix never accepts file
|
|
1293
|
+
// contents for this: it RECOMPUTES the plan from the signed finding and the live tree, and writes only if that plan's digest is
|
|
1294
|
+
// the one synthesize_fix returned. The digest binds the finding, every file path, the exact pre-image and the exact post-image
|
|
1295
|
+
// of each edit, so a changed tree, a changed finding, or an altered digest all refuse. The write itself is the language
|
|
1296
|
+
// lifecycle's all-or-nothing writeManyWithBackup (one backup per file, one history group, honest rollback).
|
|
1297
|
+
const MULTI_FILE_MAX = 8;
|
|
1298
|
+
const _sha256 = (t) => node_crypto__WEBPACK_IMPORTED_MODULE_3__.createHash('sha256').update(String(t), 'utf8').digest('hex');
|
|
1299
|
+
function _multiFilePlanDigest(finding, edits) {
|
|
1300
|
+
const canon = edits.map((e) => ({ file: String(e.file), before: _sha256(e.before), after: _sha256(e.after) })).sort((a, b) => (a.file < b.file ? -1 : a.file > b.file ? 1 : 0));
|
|
1301
|
+
return _sha256(JSON.stringify({ v: 'mf-plan/1', id: finding.id || null, stableId: finding.stableId || null, edits: canon }));
|
|
1302
|
+
}
|
|
1303
|
+
// Every file of a multi-file plan gets the SAME checks a single-file apply_fix target gets: root-relative lexical form,
|
|
1304
|
+
// confinement (no symlink leaf, no symlinked parent, no escape), reserved-write list, and existence. Returns {ok, reason, abs}.
|
|
1305
|
+
function _checkMultiFileTargets(sessionRoot, edits) {
|
|
1306
|
+
if (!Array.isArray(edits) || edits.length < 2) return { ok: false, reason: 'a multi-file plan needs at least two edits' };
|
|
1307
|
+
if (edits.length > MULTI_FILE_MAX) return { ok: false, reason: `a multi-file plan may edit at most ${MULTI_FILE_MAX} files` };
|
|
1308
|
+
const rootReal = node_fs__WEBPACK_IMPORTED_MODULE_0__.realpathSync(node_path__WEBPACK_IMPORTED_MODULE_2__.resolve(sessionRoot));
|
|
1309
|
+
const seen = new Set();
|
|
1310
|
+
const abs = {};
|
|
1311
|
+
for (const e of edits) {
|
|
1312
|
+
const rel = e && e.file;
|
|
1313
|
+
if (typeof rel !== 'string' || !rel || rel.includes('\0') || rel.includes('\\') || node_path__WEBPACK_IMPORTED_MODULE_2__.isAbsolute(rel) || rel.split('/').some((seg) => seg === '..' || seg === '.' || seg === '')) {
|
|
1314
|
+
return { ok: false, reason: `path-escape refused: plan file "${node_path__WEBPACK_IMPORTED_MODULE_2__.isAbsolute(String(rel)) ? '(absolute path)' : String(rel).slice(0, 120)}" is not a clean root-relative path` };
|
|
1315
|
+
}
|
|
1316
|
+
if (seen.has(rel)) return { ok: false, reason: `the same file appears twice in the plan: ${rel}` };
|
|
1317
|
+
seen.add(rel);
|
|
1318
|
+
let real;
|
|
1319
|
+
try { real = _confine(sessionRoot, rel, 'plan file'); }
|
|
1320
|
+
catch (err) { return { ok: false, reason: `path-escape refused: ${String(err.message).split(rootReal).join('<root>')}` }; }
|
|
1321
|
+
if (node_path__WEBPACK_IMPORTED_MODULE_2__.relative(rootReal, real).split(node_path__WEBPACK_IMPORTED_MODULE_2__.sep).join('/') !== rel) return { ok: false, reason: `path-escape refused: plan file "${rel}" resolves through a symbolic link` };
|
|
1322
|
+
if (_isReservedWritePath(sessionRoot, real)) return { ok: false, reason: `reserved path refused: ${rel}` };
|
|
1323
|
+
if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(real)) return { ok: false, reason: `plan file not found: ${rel}` };
|
|
1324
|
+
abs[rel] = real;
|
|
1325
|
+
}
|
|
1326
|
+
return { ok: true, abs };
|
|
1327
|
+
}
|
|
1328
|
+
const _scrubRoot = (sessionRoot, text) => {
|
|
1329
|
+
let out = String(text);
|
|
1330
|
+
const roots = new Set([node_path__WEBPACK_IMPORTED_MODULE_2__.resolve(sessionRoot)]);
|
|
1331
|
+
try { roots.add(node_fs__WEBPACK_IMPORTED_MODULE_0__.realpathSync(node_path__WEBPACK_IMPORTED_MODULE_2__.resolve(sessionRoot))); } catch { /* root vanished: the plain path is still scrubbed */ }
|
|
1332
|
+
for (const r of roots) out = out.split(r).join('<root>');
|
|
1333
|
+
return (0,_redact_js__WEBPACK_IMPORTED_MODULE_16__/* .redactString */ .rd)(out);
|
|
1334
|
+
};
|
|
1335
|
+
const _gateSummary = (g) => (g ? {
|
|
1336
|
+
path: g.path ? g.path.ok : undefined, syntax: g.syntax ? g.syntax.ok : undefined,
|
|
1337
|
+
rescan: g.rescan ? { ok: g.rescan.ok, originalGone: g.rescan.originalGone, newMediumOrHigher: g.rescan.newMediumOrHigher } : undefined,
|
|
1338
|
+
effective: g.effective ? { ok: g.effective.ok, ran: g.effective.ran === true, detail: g.effective.detail } : undefined,
|
|
1339
|
+
compile: g.compile ? { ran: g.compile.ran === true, ok: g.compile.ok } : undefined,
|
|
1340
|
+
} : null);
|
|
1341
|
+
|
|
1342
|
+
async function _applyMultiFilePlan({ f, planDigest, dryRun, fixMeta, ctx, status }) {
|
|
1343
|
+
const refuse = (reason, extra = {}) => ({ _meta: META, applied: false, multiFile: true, reason, ...extra });
|
|
1344
|
+
if (typeof planDigest !== 'string' || !/^[0-9a-f]{64}$/.test(planDigest)) return refuse('plan_digest must be the 64-character hex digest returned by synthesize_fix');
|
|
1345
|
+
if (!f.stableId) return refuse('finding has no stableId, so a plan cannot be verified against it');
|
|
1346
|
+
const lc = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 52603));
|
|
1347
|
+
if (lc.languageOfFinding(f) !== 'nix') return refuse('multi-file plans are produced for NixOS findings only');
|
|
1348
|
+
const nf = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 57820));
|
|
1349
|
+
const proj = lc.loadLanguageProject(ctx.sessionRoot);
|
|
1350
|
+
const files = proj.files;
|
|
1351
|
+
// The plan is derived here, from the signed finding and the live tree. Nothing the caller sends can supply file content.
|
|
1352
|
+
const plan = nf.planNixFix(f, files);
|
|
1353
|
+
if (!plan || plan.ok === false) return refuse(`no applicable plan: ${_scrubRoot(ctx.sessionRoot, (plan && plan.reason) || 'no deterministic fix')}`);
|
|
1354
|
+
if (!Array.isArray(plan.edits) || plan.edits.length < 2) return refuse('this finding has a single-file plan; use apply_fix without plan_digest');
|
|
1355
|
+
if (_multiFilePlanDigest(f, plan.edits) !== planDigest) {
|
|
1356
|
+
return refuse('plan_digest does not match the plan computed now for this finding: the project files or the finding changed since synthesize_fix, or the digest was altered. Run synthesize_fix again and review the new plan.', { stale: true });
|
|
1357
|
+
}
|
|
1358
|
+
const targets = _checkMultiFileTargets(ctx.sessionRoot, plan.edits);
|
|
1359
|
+
if (!targets.ok) return refuse(targets.reason);
|
|
1360
|
+
const classification = (0,_posture_material_change_js__WEBPACK_IMPORTED_MODULE_6__/* .classifyFixMaterialRisk */ .kz)(Object.fromEntries(plan.edits.map((e) => [e.file, { before: e.before, after: e.after }])));
|
|
1361
|
+
if (!dryRun) {
|
|
1362
|
+
const approvalRefusal = _highImpactApprovalRefusal(ctx, classification, fixMeta);
|
|
1363
|
+
if (approvalRefusal) return { ...approvalRefusal, multiFile: true };
|
|
1364
|
+
}
|
|
1365
|
+
// The verification gates (syntax, rescan with no new finding, effective value changed as intended) run inside the lifecycle,
|
|
1366
|
+
// and the pre-write hook re-checks the digest of the VERIFIED edits and the targets immediately before the first byte is written.
|
|
1367
|
+
const preWrite = (verifiedPlan) => {
|
|
1368
|
+
const vedits = Array.isArray(verifiedPlan.edits) ? verifiedPlan.edits : [];
|
|
1369
|
+
if (_multiFilePlanDigest(f, vedits) !== planDigest) return { ok: false, detail: 'the verified plan differs from the previewed plan' };
|
|
1370
|
+
const again = _checkMultiFileTargets(ctx.sessionRoot, vedits);
|
|
1371
|
+
return again.ok ? { ok: true } : { ok: false, detail: again.reason };
|
|
1372
|
+
};
|
|
1373
|
+
let res;
|
|
1374
|
+
try { res = await nf.validateNixFix(f, { files, apply: !dryRun, root: ctx.sessionRoot, preWrite }); }
|
|
1375
|
+
catch (e) { return refuse(`plan verification failed: ${_scrubRoot(ctx.sessionRoot, e.message)}`); }
|
|
1376
|
+
const gates = _gateSummary(res.gates);
|
|
1377
|
+
const touched = plan.edits.map((e) => e.file);
|
|
1378
|
+
if (res.status === 'verified' && dryRun) {
|
|
1379
|
+
return { _meta: META, applied: false, dryRun: true, verified: true, multiFile: true, files: touched, planDigest, gates, materialClassification: classification, diff: res.preview };
|
|
1380
|
+
}
|
|
1381
|
+
if (res.status === 'applied') {
|
|
1382
|
+
let acceptance = null;
|
|
1383
|
+
try { acceptance = (0,_posture_fix_history_js__WEBPACK_IMPORTED_MODULE_4__/* .fixAcceptanceRate */ .XR)(ctx.sessionRoot); } catch { /* best-effort */ }
|
|
1384
|
+
const hist = Array.isArray(res.history) ? res.history : [];
|
|
1385
|
+
const allRecorded = hist.length === touched.length && hist.every(Boolean);
|
|
1386
|
+
return {
|
|
1387
|
+
_meta: META, applied: true, verified: true, multiFile: true, files: touched, planDigest,
|
|
1388
|
+
groupId: res.backup && res.backup.id, historyIds: hist.map((h) => (h ? h.id : null)),
|
|
1389
|
+
...(allRecorded ? {} : { warning: 'the files were written and backed up, but the fix history could not record every entry; restore with the group backup under .agentic-security/fix-backups' }),
|
|
1390
|
+
gates, integrity: status, acceptance, materialClassification: classification,
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
// Blocked or failed: nothing is left changed unless rollbackIncomplete says otherwise.
|
|
1394
|
+
const incomplete = res.rolledBack === false;
|
|
1395
|
+
return {
|
|
1396
|
+
_meta: META, applied: false, multiFile: true, files: touched,
|
|
1397
|
+
reason: _scrubRoot(ctx.sessionRoot, res.reason || `plan not applied (${res.status})`),
|
|
1398
|
+
gates,
|
|
1399
|
+
...(res.rolledBack !== undefined ? { rolledBack: res.rolledBack === true, rollbackIncomplete: incomplete, ...(incomplete ? { restoreManually: (res.rollbackFailed || []).map(String) } : {}) } : {}),
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1253
1403
|
// ─── apply_fix ───────────────────────────────────────────────────────────────
|
|
1254
1404
|
const apply_fix = {
|
|
1255
1405
|
name: 'apply_fix',
|
|
1256
|
-
description: 'Apply a fix for a finding. Two modes: (1) the stored fix.replacement, or (2) a caller-supplied `patch` (a files map) which is RE-VERIFIED inline (rescan-clean + no new ≥medium + lint) before any write — this unblocks findings that ship only a template or description. Refuses if last-scan.json fails its HMAC check, if the finding is shadow-marked, or if a path escapes the session root via lexical traversal OR a symlink. Requires confirm:true. Supports dry_run:true to preview without writing. On success, `verified:true` means verification passed but `verifiedFull:true` is the honest signal that every required leg (lint when configured, tests when a runner exists) genuinely ran — a false `verifiedFull` with `verified:true` means the pass is real but degraded (see `verify.degradedLegs`), not a full verification.',
|
|
1406
|
+
description: 'Apply a fix for a finding. Two modes: (1) the stored fix.replacement, or (2) a caller-supplied `patch` (a files map) which is RE-VERIFIED inline (rescan-clean + no new ≥medium + lint) before any write — this unblocks findings that ship only a template or description. Refuses if last-scan.json fails its HMAC check, if the finding is shadow-marked, or if a path escapes the session root via lexical traversal OR a symlink. Requires confirm:true. Supports dry_run:true to preview without writing. On success, `verified:true` means verification passed but `verifiedFull:true` is the honest signal that every required leg (lint when configured, tests when a runner exists) genuinely ran — a false `verifiedFull` with `verified:true` means the pass is real but degraded (see `verify.degradedLegs`), not a full verification. MULTI-FILE NixOS FIX: when synthesize_fix returns `languageFix.planDigest` (an option defined in several files, edited together), pass it back as `plan_digest`. apply_fix then RECOMPUTES the plan itself from the signed finding and the live files, refuses unless its digest matches (so a changed tree, a changed finding or an altered digest all refuse; you never send file content), applies the same confinement, reserved-path, shadow and high-impact-approval checks to EVERY file, runs the verification gates (syntax, rescan with no new finding, the option resolves to the intended value on the patched configuration) before writing, and writes all files or none with one backup per file and one history group that `undo` reverts together. A rollback that could not complete is reported as `rollbackIncomplete` with the files to restore. `plan_digest` cannot be combined with `patch`.',
|
|
1257
1407
|
inputSchema: {
|
|
1258
1408
|
type: 'object',
|
|
1259
1409
|
additionalProperties: false,
|
|
@@ -1261,6 +1411,7 @@ const apply_fix = {
|
|
|
1261
1411
|
finding_id: { type: 'string', minLength: 1, maxLength: 256 },
|
|
1262
1412
|
confirm: { type: 'boolean' },
|
|
1263
1413
|
dry_run: { type: 'boolean' },
|
|
1414
|
+
plan_digest: { type: 'string', minLength: 64, maxLength: 64 },
|
|
1264
1415
|
patch: {
|
|
1265
1416
|
type: 'object',
|
|
1266
1417
|
additionalProperties: { type: 'string', maxLength: 500_000 },
|
|
@@ -1320,7 +1471,7 @@ const apply_fix = {
|
|
|
1320
1471
|
},
|
|
1321
1472
|
required: ['finding_id', 'confirm'],
|
|
1322
1473
|
},
|
|
1323
|
-
async handler({ finding_id, confirm, dry_run = false, patch = null, fixMeta = null }, ctx) {
|
|
1474
|
+
async handler({ finding_id, confirm, dry_run = false, patch = null, fixMeta = null, plan_digest = null }, ctx) {
|
|
1324
1475
|
if (confirm !== true) {
|
|
1325
1476
|
return { _meta: META, applied: false, reason: 'apply_fix requires confirm: true.' };
|
|
1326
1477
|
}
|
|
@@ -1334,6 +1485,15 @@ const apply_fix = {
|
|
|
1334
1485
|
return { _meta: META, applied: false, reason: 'shadow findings cannot be auto-applied' };
|
|
1335
1486
|
}
|
|
1336
1487
|
|
|
1488
|
+
// Multi-file plan (a NixOS option defined in several files). The plan is recomputed here and bound by plan_digest; the
|
|
1489
|
+
// caller can never supply file content on this path, and it cannot be combined with `patch`.
|
|
1490
|
+
if (plan_digest !== null && plan_digest !== undefined) {
|
|
1491
|
+
if (patch && typeof patch === 'object' && Object.keys(patch).length) {
|
|
1492
|
+
return { _meta: META, applied: false, reason: 'plan_digest and patch cannot be combined: a multi-file plan is computed by the scanner, not supplied by the caller' };
|
|
1493
|
+
}
|
|
1494
|
+
return _applyMultiFilePlan({ f, planDigest: plan_digest, dryRun: dry_run === true, fixMeta, ctx, status });
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1337
1497
|
// #3 — verifier-approved patch path. When the caller supplies `patch` (a
|
|
1338
1498
|
// files map, same shape as verify_fix), apply_fix re-runs the verifier
|
|
1339
1499
|
// INLINE and writes only if it passes: the original finding's stableId is
|
|
@@ -1369,7 +1529,7 @@ const apply_fix = {
|
|
|
1369
1529
|
// default. Normalized to the scan+lint verdict shape used below.
|
|
1370
1530
|
const { verifyFixWithTests } = await __webpack_require__.e(/* import() */ 4113).then(__webpack_require__.bind(__webpack_require__, 64113));
|
|
1371
1531
|
const t = await verifyFixWithTests({ scanRoot: ctx.sessionRoot, originalFindingStableId: f.stableId, files: _files });
|
|
1372
|
-
verdict = { ok: t.ok, summary: t.summary, rescan: t.legs?.scan?.detail, lint: t.legs?.lint?.detail, tests: t.legs?.tests, testVerdict: t.verdict };
|
|
1532
|
+
verdict = { ok: t.ok, summary: t.summary, rescan: t.legs?.scan?.detail, lint: t.legs?.lint?.detail, tests: t.legs?.tests, testVerdict: t.verdict, verificationRecord: t.verificationRecord ?? null };
|
|
1373
1533
|
} else {
|
|
1374
1534
|
const verifyFixCore = await getVerifyFixCore();
|
|
1375
1535
|
verdict = await verifyFixCore({
|
|
@@ -1387,6 +1547,9 @@ const apply_fix = {
|
|
|
1387
1547
|
_meta: META, applied: false,
|
|
1388
1548
|
reason: `patch rejected by verifier: ${verdict.summary || verdict.rescan?.reason || 'did not verify'}`,
|
|
1389
1549
|
verify: { rescan: verdict.rescan, lint: { runner: verdict.lint?.runner, ok: verdict.lint?.ok }, honesty: verdict.honesty || null },
|
|
1550
|
+
// X-201: the same version-1 verification record every surface emits (additive; null when none could be formed).
|
|
1551
|
+
verificationRecord: verdict.verificationRecord ?? null,
|
|
1552
|
+
...(0,_posture_verification_projection_js__WEBPACK_IMPORTED_MODULE_13__/* .verificationFields */ .ze)(verdict.verificationRecord),
|
|
1390
1553
|
};
|
|
1391
1554
|
}
|
|
1392
1555
|
// FR-307/FR-1002/D-0024: this caller-supplied-patch branch writes via
|
|
@@ -1407,41 +1570,10 @@ const apply_fix = {
|
|
|
1407
1570
|
}
|
|
1408
1571
|
const materialClassification = (0,_posture_material_change_js__WEBPACK_IMPORTED_MODULE_6__/* .classifyFixMaterialRisk */ .kz)(filesForMaterialClassification);
|
|
1409
1572
|
if (dry_run) {
|
|
1410
|
-
return { _meta: META, applied: false, dryRun: true, verified: true, files: Object.keys(confinedAbs), summary: verdict.summary, materialClassification };
|
|
1411
|
-
}
|
|
1412
|
-
if (materialClassification.highImpactCategories.length) {
|
|
1413
|
-
const approval = fixMeta && typeof fixMeta === 'object' ? fixMeta.approval : null;
|
|
1414
|
-
const hasApprovalEvidence = !!(approval && typeof approval === 'object' &&
|
|
1415
|
-
typeof approval.approvedBy === 'string' && approval.approvedBy.trim().length > 0 &&
|
|
1416
|
-
typeof approval.reason === 'string' && approval.reason.trim().length > 0);
|
|
1417
|
-
if (!hasApprovalEvidence) {
|
|
1418
|
-
return {
|
|
1419
|
-
_meta: META, applied: false,
|
|
1420
|
-
reason: `high-impact change (${materialClassification.highImpactCategories.join(', ')}) requires approval evidence — pass fixMeta.approval: {approvedBy, reason} — before it can be applied`,
|
|
1421
|
-
materialClassification,
|
|
1422
|
-
};
|
|
1423
|
-
}
|
|
1424
|
-
const approverRegistry = (0,_fix_approver_registry_js__WEBPACK_IMPORTED_MODULE_7__.loadApproverRegistry)(ctx.sessionRoot);
|
|
1425
|
-
const requiredRoles = (0,_fix_approver_registry_js__WEBPACK_IMPORTED_MODULE_7__/* .requiredRolesFor */ .K)(approverRegistry, materialClassification.highImpactCategories);
|
|
1426
|
-
const identityCheck = (0,_fix_approver_registry_js__WEBPACK_IMPORTED_MODULE_7__.verifyApprover)(approverRegistry, approval.approvedBy, requiredRoles);
|
|
1427
|
-
if (!identityCheck.verified) {
|
|
1428
|
-
return {
|
|
1429
|
-
_meta: META, applied: false,
|
|
1430
|
-
reason: `high-impact change (${materialClassification.highImpactCategories.join(', ')}) approval rejected: ${identityCheck.reason}`,
|
|
1431
|
-
materialClassification,
|
|
1432
|
-
};
|
|
1433
|
-
}
|
|
1434
|
-
// FR-1003: separation-of-duties, same no-op-unless-configured gate
|
|
1435
|
-
// as apply-fix-service.js's own copy — see approver-registry.js.
|
|
1436
|
-
const sodCheck = (0,_fix_approver_registry_js__WEBPACK_IMPORTED_MODULE_7__.checkSeparationOfDuties)(approverRegistry, fixMeta?.author, approval.approvedBy);
|
|
1437
|
-
if (!sodCheck.ok) {
|
|
1438
|
-
return {
|
|
1439
|
-
_meta: META, applied: false,
|
|
1440
|
-
reason: `high-impact change (${materialClassification.highImpactCategories.join(', ')}) approval rejected: ${sodCheck.reason}`,
|
|
1441
|
-
materialClassification,
|
|
1442
|
-
};
|
|
1443
|
-
}
|
|
1573
|
+
return { _meta: META, applied: false, dryRun: true, verified: true, files: Object.keys(confinedAbs), summary: verdict.summary, verificationRecord: verdict.verificationRecord ?? null, ...(0,_posture_verification_projection_js__WEBPACK_IMPORTED_MODULE_13__/* .verificationFields */ .ze)(verdict.verificationRecord), materialClassification };
|
|
1444
1574
|
}
|
|
1575
|
+
const approvalRefusal = _highImpactApprovalRefusal(ctx, materialClassification, fixMeta);
|
|
1576
|
+
if (approvalRefusal) return approvalRefusal;
|
|
1445
1577
|
const written = [];
|
|
1446
1578
|
try {
|
|
1447
1579
|
for (const [rel, v] of Object.entries(confinedAbs)) {
|
|
@@ -1469,7 +1601,7 @@ const apply_fix = {
|
|
|
1469
1601
|
}
|
|
1470
1602
|
let acceptance = null;
|
|
1471
1603
|
try { acceptance = (0,_posture_fix_history_js__WEBPACK_IMPORTED_MODULE_4__/* .fixAcceptanceRate */ .XR)(ctx.sessionRoot); } catch { /* best-effort */ }
|
|
1472
|
-
return { _meta: META, applied: true, verified: true, patched: written, integrity: status, verify: { summary: verdict.summary }, acceptance, materialClassification };
|
|
1604
|
+
return { _meta: META, applied: true, verified: true, patched: written, integrity: status, verify: { summary: verdict.summary, verificationRecord: verdict.verificationRecord ?? null, ...(0,_posture_verification_projection_js__WEBPACK_IMPORTED_MODULE_13__/* .verificationFields */ .ze)(verdict.verificationRecord) }, acceptance, materialClassification };
|
|
1473
1605
|
}
|
|
1474
1606
|
|
|
1475
1607
|
if (typeof f.fix?.replacement !== 'string') {
|
|
@@ -1558,7 +1690,7 @@ const apply_fix = {
|
|
|
1558
1690
|
// proceed with apply_fix.
|
|
1559
1691
|
const verify_fix = {
|
|
1560
1692
|
name: 'verify_fix',
|
|
1561
|
-
description: 'Verify a proposed patch before applying. Re-scans the patched files in memory, runs the project linter, runs the project test suite, checks fix honesty (FULL/MITIGATION/WORKAROUND) when fixMeta is supplied, and re-runs the PoC when one exists. Returns { ok, rescan, lint, tests, honesty, poc, summary }. Does not write to the target project’s own files, but DOES append one record per attempt to .agentic-security/fix-metrics.jsonl for the measured fix-loop.',
|
|
1693
|
+
description: 'Verify a proposed patch before applying. Re-scans the patched files in memory, runs the project linter, runs the project test suite, checks fix honesty (FULL/MITIGATION/WORKAROUND) when fixMeta is supplied, and re-runs the PoC when one exists. Returns { ok, rescan, lint, tests, honesty, poc, summary, verificationRecord } (verificationRecord is the shared version-1 record: outcome is one of not-run, unsupported, inconclusive, error, refuted, confirmed, and a static re-scan alone is never reported as confirmed; verificationView is the shared projection every interface shows for it, with state, scope, evidence ids, replay prerequisites and what was and was not verified). Does not write to the target project’s own files, but DOES append one record per attempt to .agentic-security/fix-metrics.jsonl for the measured fix-loop.',
|
|
1562
1694
|
inputSchema: {
|
|
1563
1695
|
type: 'object',
|
|
1564
1696
|
additionalProperties: false,
|
|
@@ -1646,7 +1778,7 @@ const verify_fix = {
|
|
|
1646
1778
|
_meta: META,
|
|
1647
1779
|
ok: r.ok,
|
|
1648
1780
|
rescan: { ok: r.rescan.ok, reason: r.rescan.reason, introduced: r.rescan.introduced || [] },
|
|
1649
|
-
lint: { runner: r.lint.runner, ok: r.lint.ok, skipped: r.lint.skipped || false, output: (0,
|
|
1781
|
+
lint: { runner: r.lint.runner, ok: r.lint.ok, skipped: r.lint.skipped || false, output: (0,_redact_js__WEBPACK_IMPORTED_MODULE_16__/* .redactString */ .rd)(r.lint.output || '').slice(0, 1500) },
|
|
1650
1782
|
// verifyFix computes five legs, not two — tests/honesty/poc were
|
|
1651
1783
|
// being silently dropped here, leaving an agent with no structured
|
|
1652
1784
|
// way to see WHY verification failed when the failure was in one
|
|
@@ -1657,8 +1789,12 @@ const verify_fix = {
|
|
|
1657
1789
|
// it can echo proof-harness detail derived from scanned source.
|
|
1658
1790
|
tests: r.tests,
|
|
1659
1791
|
honesty: r.honesty,
|
|
1660
|
-
poc: r.poc ? { ...r.poc, reason: r.poc.reason ? (0,
|
|
1792
|
+
poc: r.poc ? { ...r.poc, reason: r.poc.reason ? (0,_redact_js__WEBPACK_IMPORTED_MODULE_16__/* .redactString */ .rd)(r.poc.reason) : r.poc.reason } : r.poc,
|
|
1661
1793
|
summary: r.summary,
|
|
1794
|
+
// X-201: the one version-1 verification record (additive). It states what was actually established, e.g.
|
|
1795
|
+
// `not-run` when no exploit oracle executed against the patch, never a bare pass/fail.
|
|
1796
|
+
verificationRecord: r.verificationRecord ?? null,
|
|
1797
|
+
...(0,_posture_verification_projection_js__WEBPACK_IMPORTED_MODULE_13__/* .verificationFields */ .ze)(r.verificationRecord),
|
|
1662
1798
|
};
|
|
1663
1799
|
} catch (e) {
|
|
1664
1800
|
return { _meta: META, ok: false, reason: `verify_fix failed: ${e.message}` };
|
|
@@ -1672,7 +1808,7 @@ const verify_fix = {
|
|
|
1672
1808
|
// sequence with the returned blob.
|
|
1673
1809
|
const synthesize_fix = {
|
|
1674
1810
|
name: 'synthesize_fix',
|
|
1675
|
-
description: 'Return the stored fix replacement for a finding (replacement text + remediation + plan if the patch is too large). Read-only; never writes to disk. Use verify_fix → apply_fix to deploy.',
|
|
1811
|
+
description: 'Return the stored fix replacement for a finding (replacement text + remediation + plan if the patch is too large). Read-only; never writes to disk. Use verify_fix → apply_fix to deploy. For a NixOS option defined in several files, `languageFix.multiFile` lists every file, `languageFix.edits` gives each file\'s before/after SHA-256 (no file content) and `languageFix.planDigest` is the value to pass to apply_fix as `plan_digest`; the preview is verified but nothing is written.',
|
|
1676
1812
|
inputSchema: {
|
|
1677
1813
|
type: 'object',
|
|
1678
1814
|
additionalProperties: false,
|
|
@@ -1721,13 +1857,20 @@ const synthesize_fix = {
|
|
|
1721
1857
|
// FULL / MITIGATION / WORKAROUND tier. It writes nothing; apply_fix re-verifies before anything lands.
|
|
1722
1858
|
if (!autofix) {
|
|
1723
1859
|
try {
|
|
1724
|
-
const lc = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__,
|
|
1860
|
+
const lc = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 52603));
|
|
1725
1861
|
if (lc.languageOfFinding(f)) {
|
|
1726
1862
|
const proj = lc.loadLanguageProject(ctx.sessionRoot);
|
|
1727
1863
|
const prev = await lc.languageFixPreview(f, proj.files);
|
|
1728
1864
|
languageFix = { status: prev.status, ok: prev.ok, label: prev.label || null, tier: prev.tier || null, reason: prev.reason || null, diff: prev.diff || null, explanation: prev.explanation || null, consequences: prev.consequences || [] };
|
|
1729
|
-
//
|
|
1730
|
-
if (prev.ok && prev.edits)
|
|
1865
|
+
// A fix that spans several files is applied with apply_fix + plan_digest (atomic, one history group); see _applyMultiFilePlan.
|
|
1866
|
+
if (prev.ok && prev.edits) {
|
|
1867
|
+
languageFix.multiFile = prev.edits.map((e) => e.file);
|
|
1868
|
+
// The digest binds finding + every file + exact pre/post image. Pass it to apply_fix as plan_digest; apply_fix
|
|
1869
|
+
// recomputes the plan itself and refuses on any difference. No file content is ever sent back to apply_fix.
|
|
1870
|
+
languageFix.planDigest = _multiFilePlanDigest(f, prev.edits);
|
|
1871
|
+
languageFix.edits = prev.edits.map((e) => ({ file: e.file, beforeSha256: _sha256(e.before), afterSha256: _sha256(e.after) }));
|
|
1872
|
+
languageFix.applyWith = { finding_id: f.id, confirm: true, plan_digest: languageFix.planDigest };
|
|
1873
|
+
}
|
|
1731
1874
|
else if (prev.ok) autofix = { deterministic: true, ruleId: f.rule || f.family || null, patch: prev.after, file: prev.file, label: prev.label || null, verified: true };
|
|
1732
1875
|
}
|
|
1733
1876
|
} catch { /* best-effort: the preview is advisory */ }
|
|
@@ -1994,7 +2137,7 @@ const append_agents_memory = {
|
|
|
1994
2137
|
required: ['agent', 'body'],
|
|
1995
2138
|
},
|
|
1996
2139
|
async handler({ agent, body }, ctx) {
|
|
1997
|
-
const r = (0,
|
|
2140
|
+
const r = (0,_posture_agents_memory_js__WEBPACK_IMPORTED_MODULE_17__/* .appendAgentsMemory */ .eu)(ctx.sessionRoot, { agent, body });
|
|
1998
2141
|
return { _meta: META, ...r };
|
|
1999
2142
|
},
|
|
2000
2143
|
};
|
|
@@ -2010,7 +2153,7 @@ const read_agents_memory = {
|
|
|
2010
2153
|
},
|
|
2011
2154
|
},
|
|
2012
2155
|
async handler({ full }, ctx) {
|
|
2013
|
-
const body = (0,
|
|
2156
|
+
const body = (0,_posture_agents_memory_js__WEBPACK_IMPORTED_MODULE_17__/* .readAgentsMemory */ .ox)(ctx.sessionRoot);
|
|
2014
2157
|
if (!body) return { _meta: META, present: false };
|
|
2015
2158
|
if (full) return { _meta: META, present: true, length: body.length, content: body };
|
|
2016
2159
|
// Tail-only — same logic as summarizeForSession but inlined to avoid a
|
|
@@ -2054,7 +2197,7 @@ const query_triage_memory = {
|
|
|
2054
2197
|
// they landed in, rather than hardcoding a field allowlist that could
|
|
2055
2198
|
// miss one.
|
|
2056
2199
|
let results;
|
|
2057
|
-
try { results = JSON.parse((0,
|
|
2200
|
+
try { results = JSON.parse((0,_redact_js__WEBPACK_IMPORTED_MODULE_16__/* .redactString */ .rd)(JSON.stringify(raw))); }
|
|
2058
2201
|
catch { results = raw; }
|
|
2059
2202
|
return {
|
|
2060
2203
|
_meta: META,
|
|
@@ -2090,7 +2233,7 @@ const query_findings_memory = {
|
|
|
2090
2233
|
// whole-structure redactString round-trip is applied rather than a
|
|
2091
2234
|
// per-field allowlist that could miss one of the four shapes.
|
|
2092
2235
|
let body;
|
|
2093
|
-
try { body = JSON.parse((0,
|
|
2236
|
+
try { body = JSON.parse((0,_redact_js__WEBPACK_IMPORTED_MODULE_16__/* .redactString */ .rd)(JSON.stringify(raw))); }
|
|
2094
2237
|
catch { body = raw; }
|
|
2095
2238
|
return { _meta: META, ...body };
|
|
2096
2239
|
},
|
|
@@ -2112,7 +2255,7 @@ const lookup_cve = {
|
|
|
2112
2255
|
required: ['cve'],
|
|
2113
2256
|
},
|
|
2114
2257
|
async handler({ cve }, _ctx) {
|
|
2115
|
-
const r = (0,
|
|
2258
|
+
const r = (0,_posture_cve_lookup_js__WEBPACK_IMPORTED_MODULE_18__/* .lookupCve */ .x)(cve);
|
|
2116
2259
|
return { _meta: META, ...r };
|
|
2117
2260
|
},
|
|
2118
2261
|
};
|
|
@@ -2217,7 +2360,7 @@ const apply_sca_upgrade = {
|
|
|
2217
2360
|
},
|
|
2218
2361
|
};
|
|
2219
2362
|
|
|
2220
|
-
const ALL_TOOLS = [scan_diff, query_taint, explain_finding, apply_fix, verify_fix, synthesize_fix, find_rule_module, append_scratchpad, read_scratchpad, append_agents_memory, read_agents_memory, lookup_cve, synthesize_sca_upgrade, apply_sca_upgrade, query_triage_memory, query_findings_memory, query_cache_telemetry,
|
|
2363
|
+
const ALL_TOOLS = [scan_diff, query_taint, explain_finding, apply_fix, verify_fix, synthesize_fix, find_rule_module, append_scratchpad, read_scratchpad, append_agents_memory, read_agents_memory, lookup_cve, synthesize_sca_upgrade, apply_sca_upgrade, query_triage_memory, query_findings_memory, query_cache_telemetry, _dataflow_tools_js__WEBPACK_IMPORTED_MODULE_15__/* .dataflow_get_graph */ .HX, _dataflow_tools_js__WEBPACK_IMPORTED_MODULE_15__/* .dataflow_get_node */ .HI, _dataflow_tools_js__WEBPACK_IMPORTED_MODULE_15__/* .dataflow_get_edge */ .gC, _dataflow_tools_js__WEBPACK_IMPORTED_MODULE_15__/* .dataflow_get_flow */ .ri];
|
|
2221
2364
|
|
|
2222
2365
|
__webpack_async_result__();
|
|
2223
2366
|
} catch(e) { __webpack_async_result__(e); } });
|