@clear-capabilities/agentic-security-scanner 0.143.0 → 0.145.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.
Files changed (135) hide show
  1. package/CHANGELOG.md +555 -0
  2. package/bin/agentic-security.js +770 -49
  3. package/dist/1.index.js +223 -0
  4. package/dist/113.index.js +117 -18
  5. package/dist/144.index.js +163 -0
  6. package/dist/178.index.js +24 -6
  7. package/dist/238.index.js +3 -2
  8. package/dist/265.index.js +191 -0
  9. package/dist/271.index.js +165 -0
  10. package/dist/384.index.js +1 -1
  11. package/dist/435.index.js +187 -52
  12. package/dist/444.index.js +11 -2
  13. package/dist/449.index.js +76 -12
  14. package/dist/526.index.js +117 -18
  15. package/dist/552.index.js +97 -0
  16. package/dist/637.index.js +27 -5
  17. package/dist/730.index.js +311 -0
  18. package/dist/736.index.js +301 -0
  19. package/dist/824.index.js +7 -0
  20. package/dist/905.index.js +88 -22
  21. package/dist/920.index.js +491 -0
  22. package/dist/970.index.js +173 -0
  23. package/dist/agentic-security.mjs +13 -13
  24. package/dist/agentic-security.mjs.sha256 +1 -1
  25. package/dist/calibration-seed.json +2 -0
  26. package/package.json +23 -9
  27. package/src/compare.js +6 -1
  28. package/src/dataflow/CLAUDE.md +1 -1
  29. package/src/dataflow/index.js +18 -0
  30. package/src/dataflow/privacy-catalog.js +290 -0
  31. package/src/dataflow/privacy-deep-walker.js +515 -0
  32. package/src/dataflow/privacy-governance.js +126 -0
  33. package/src/dataflow/privacy-inventory.js +154 -0
  34. package/src/dataflow/privacy-sink-policy.js +125 -0
  35. package/src/dataflow/privacy-taint.js +115 -54
  36. package/src/dataflow/privacy-taxonomy.js +233 -0
  37. package/src/discovery/disprove.js +7 -3
  38. package/src/discovery/hunter.js +9 -5
  39. package/src/discovery/index.js +2 -2
  40. package/src/discovery/llm-invoke.js +69 -13
  41. package/src/egress/audit.js +147 -0
  42. package/src/egress/policy.js +313 -0
  43. package/src/egress/redact.js +180 -0
  44. package/src/engine.js +1048 -302
  45. package/src/fix/apply-fix-service.js +404 -0
  46. package/src/fix/approver-registry.js +157 -0
  47. package/src/history-scan.js +22 -5
  48. package/src/ir/CLAUDE.md +1 -1
  49. package/src/llm-validator/index.js +86 -9
  50. package/src/llm-validator/model-status.js +66 -0
  51. package/src/lsp/server.js +49 -2
  52. package/src/mcp/tools.js +177 -50
  53. package/src/pipeline/analyzer-supervisor.js +93 -0
  54. package/src/pipeline/analyzer-worker.js +26 -0
  55. package/src/pipeline/annotator-runner.js +33 -0
  56. package/src/pipeline/assurance-mode.js +154 -0
  57. package/src/pipeline/cascade-worker-pool.js +172 -0
  58. package/src/pipeline/cascade-worker.js +43 -0
  59. package/src/pipeline/coverage-ledger.js +0 -0
  60. package/src/pipeline/detector-runner.js +51 -0
  61. package/src/pipeline/enrichment-completion.js +58 -0
  62. package/src/pipeline/evidence-provenance.js +91 -0
  63. package/src/pipeline/finding-schema.js +108 -0
  64. package/src/pipeline/legacy-compat.js +101 -0
  65. package/src/pipeline/producer-collector.js +48 -0
  66. package/src/pipeline/producer-registry.js +112 -0
  67. package/src/pipeline/scan-health.js +144 -0
  68. package/src/posture/CLAUDE.md +123 -0
  69. package/src/posture/accuracy-scorecard.js +156 -1
  70. package/src/posture/adversary-agent.js +15 -3
  71. package/src/posture/artifact-registry.js +241 -0
  72. package/src/posture/auditor-walkthrough.js +186 -21
  73. package/src/posture/calibration-feedback.js +201 -0
  74. package/src/posture/calibration-seed.json +2 -0
  75. package/src/posture/calibration.js +25 -0
  76. package/src/posture/compliance-evidence-signing.js +131 -0
  77. package/src/posture/compliance-policy.js +324 -17
  78. package/src/posture/cross-repo-memory.js +7 -2
  79. package/src/posture/custom-rules.js +36 -0
  80. package/src/posture/deterministic.js +8 -1
  81. package/src/posture/encryption-provider.js +205 -0
  82. package/src/posture/evidence-grade-wording.js +71 -0
  83. package/src/posture/fix-history.js +137 -20
  84. package/src/posture/fix-honesty-gate.js +47 -6
  85. package/src/posture/fix-verify.js +65 -8
  86. package/src/posture/fleet.js +0 -0
  87. package/src/posture/flow-narration.js +7 -2
  88. package/src/posture/git-history.js +13 -5
  89. package/src/posture/legal-hold.js +140 -0
  90. package/src/posture/llm-redteam.js +10 -1
  91. package/src/posture/material-change.js +111 -2
  92. package/src/posture/mttr.js +75 -12
  93. package/src/posture/policy-bundle.js +274 -0
  94. package/src/posture/pre-incident-archaeology.js +39 -7
  95. package/src/posture/privacy-framework.js +47 -6
  96. package/src/posture/production-feedback.js +179 -0
  97. package/src/posture/provenance/ai-authorship.js +68 -0
  98. package/src/posture/provenance/branch-entry.js +80 -0
  99. package/src/posture/provenance/cache.js +143 -0
  100. package/src/posture/provenance/confidence.js +36 -0
  101. package/src/posture/provenance/coordinator.js +786 -0
  102. package/src/posture/provenance/dag-walk.js +249 -0
  103. package/src/posture/provenance/evidence-attribution.js +59 -0
  104. package/src/posture/provenance/git-evidence.js +310 -0
  105. package/src/posture/provenance/lifecycle.js +208 -0
  106. package/src/posture/provenance/missing-control-resolver.js +137 -0
  107. package/src/posture/provenance/origin-resolver.js +342 -0
  108. package/src/posture/provenance/predicate-replay.js +133 -0
  109. package/src/posture/provenance/providers/config.js +39 -0
  110. package/src/posture/provenance/providers/github.js +62 -0
  111. package/src/posture/provenance/providers/gitlab.js +58 -0
  112. package/src/posture/provenance/repo-lineage.js +74 -0
  113. package/src/posture/provenance/sca-origin.js +139 -0
  114. package/src/posture/provenance/schema.js +255 -0
  115. package/src/posture/provenance/transitive-sca.js +147 -0
  116. package/src/posture/provenance/validate.js +30 -0
  117. package/src/posture/provenance-evidence-bundle.js +144 -0
  118. package/src/posture/retention-policy.js +132 -0
  119. package/src/posture/risk-dollars.js +216 -26
  120. package/src/posture/sbom-diff.js +15 -2
  121. package/src/posture/scan-checkpoint.js +176 -31
  122. package/src/posture/secret-history.js +10 -2
  123. package/src/posture/state-dir.js +64 -5
  124. package/src/posture/state-lifecycle-report.js +77 -0
  125. package/src/posture/suppressions.js +59 -3
  126. package/src/posture/vuln-archaeology.js +8 -2
  127. package/src/pr-delta.js +25 -4
  128. package/src/privacy/ir-adapter.js +380 -0
  129. package/src/report/index.js +248 -5
  130. package/src/report/oscal.js +7 -2
  131. package/src/runScan.js +34 -5
  132. package/src/sast/cpp.js +3 -14
  133. package/src/sast/rate-limit.js +33 -3
  134. package/src/sca/llm-function-extract.js +6 -0
  135. package/src/util/git-hardening.js +128 -0
package/dist/526.index.js CHANGED
@@ -8,9 +8,10 @@ export const modules = {
8
8
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
9
  /* harmony export */ I3: () => (/* binding */ recordFixAttempt),
10
10
  /* harmony export */ fixDurationReport: () => (/* binding */ fixDurationReport),
11
- /* harmony export */ renderFixDurationSummary: () => (/* binding */ renderFixDurationSummary)
11
+ /* harmony export */ renderFixDurationSummary: () => (/* binding */ renderFixDurationSummary),
12
+ /* harmony export */ sU: () => (/* binding */ loadFixAttempts)
12
13
  /* harmony export */ });
13
- /* unused harmony exports FIX_STAGES, loadFixAttempts, bucketOf, summarizeFixDurations, _internals, summarizeFixAxes, renderFixAxes */
14
+ /* unused harmony exports FIX_STAGES, bucketOf, summarizeFixDurations, _internals, summarizeFixAxes, renderFixAxes */
14
15
  /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
15
16
  /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6760);
16
17
  /* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1174);
@@ -310,8 +311,8 @@ var external_node_child_process_ = __webpack_require__(1421);
310
311
  var external_node_fs_ = __webpack_require__(3024);
311
312
  // EXTERNAL MODULE: external "node:path"
312
313
  var external_node_path_ = __webpack_require__(6760);
313
- // EXTERNAL MODULE: ./src/engine.js + 200 modules
314
- var engine = __webpack_require__(3474);
314
+ // EXTERNAL MODULE: ./src/engine.js + 229 modules
315
+ var engine = __webpack_require__(7691);
315
316
  ;// CONCATENATED MODULE: ./src/posture/fix-honesty-gate.js
316
317
  // Deterministic honesty gates on fix / finding output (#7).
317
318
  //
@@ -456,23 +457,64 @@ function computeFixTier(signals) {
456
457
  }
457
458
 
458
459
  /**
459
- * Compose the three gates for a single fix's output.
460
+ * FR-308: cross-check a fix-completeness TIER against MECHANICAL evidence,
461
+ * when any is available. `signals` (computeFixTier's input) is agent-
462
+ * self-reported — this module cannot compute sinkSignatureChanged /
463
+ * allCallersRouted / testDiscriminates itself (see the header above: "the
464
+ * gate can only run against claims the AGENT self-reports... nothing here
465
+ * is server-computable"). `pocLeg` is different: fix-verify.js's PoC leg is
466
+ * a REAL execution result (posture/CLAUDE.md's execution-proof tiers), not
467
+ * a claim. When it is available and shows the proof-of-concept STILL
468
+ * demonstrates the vulnerability against the patch, a self-reported FULL
469
+ * tier is not merely internally inconsistent — it is REFUTED by fact. This
470
+ * is the literal "a mitigation or workaround cannot be represented as a
471
+ * full fix" acceptance criterion, now backed by mechanical evidence where
472
+ * it exists rather than by self-report consistency alone.
460
473
  *
461
- * ok = residual-honesty ok AND evidence-citation ok, further constrained by the
462
- * tier/residual consistency invariant:
474
+ * A `pocLeg` of `not-requested` or `inconclusive` carries no mechanical
475
+ * signal either way and is a no-op here — this check can only ever ADD a
476
+ * violation on real contrary evidence, never manufacture one from absence.
477
+ *
478
+ * @param {string} tier
479
+ * @param {{status: string, reason?: string}|null} pocLeg
480
+ * @returns {{ ok: boolean, violations: string[] }}
481
+ */
482
+ function checkMechanicalTierEvidence(tier, pocLeg) {
483
+ if (!pocLeg || typeof pocLeg !== 'object') return { ok: true, violations: [] };
484
+ if (tier === 'FULL' && pocLeg.status === 'still-exploitable') {
485
+ return {
486
+ ok: false,
487
+ violations: [`tier 'FULL' is refuted by mechanical evidence: the proof-of-concept still demonstrates the vulnerability against the patch${pocLeg.reason ? ` (${pocLeg.reason})` : ''}`],
488
+ };
489
+ }
490
+ return { ok: true, violations: [] };
491
+ }
492
+
493
+ /**
494
+ * Compose the four gates for a single fix's output.
495
+ *
496
+ * ok = residual-honesty ok AND evidence-citation ok AND mechanical-tier-
497
+ * evidence ok, further constrained by the tier/residual consistency
498
+ * invariant:
463
499
  * - a FULL tier must NOT carry a residual (a full fix has nothing left);
464
500
  * - a non-FULL tier MUST document a residual (say what's still open).
465
501
  *
466
502
  * @param {{ residual?: string, verdict?: string, evidence?: any, signals?: object }} input
503
+ * @param {{ pocLeg?: object|null }} [mechanical] - FR-308: optional real
504
+ * execution evidence (fix-verify.js's pocLeg) to cross-check the
505
+ * self-reported tier against. Omitted entirely by any caller that has no
506
+ * PoC leg to offer — this parameter never REQUIRES mechanical evidence,
507
+ * it only USES it when present.
467
508
  * @returns {{ ok: boolean, tier: string, violations: string[] }}
468
509
  */
469
- function gateFixOutput({ residual, verdict, evidence, signals } = {}) {
510
+ function gateFixOutput({ residual, verdict, evidence, signals } = {}, { pocLeg = null } = {}) {
470
511
  const tier = computeFixTier(signals);
471
512
  const residualCheck = checkResidualHonesty(residual);
472
513
  const evidenceCheck = requireCitedEvidence(verdict, evidence);
514
+ const mechanicalCheck = checkMechanicalTierEvidence(tier, pocLeg);
473
515
 
474
- const violations = [...residualCheck.violations, ...evidenceCheck.violations];
475
- let ok = residualCheck.ok && evidenceCheck.ok;
516
+ const violations = [...residualCheck.violations, ...evidenceCheck.violations, ...mechanicalCheck.violations];
517
+ let ok = residualCheck.ok && evidenceCheck.ok && mechanicalCheck.ok;
476
518
 
477
519
  const residualEmpty = typeof residual !== 'string' || residual.trim() === '';
478
520
  if (tier === 'FULL' && !residualEmpty) {
@@ -681,7 +723,15 @@ async function verifyPatch({
681
723
  const fileContents = { ...files };
682
724
  let scan;
683
725
  try {
684
- scan = await (0,engine/* runFullScan */.wW)({ fileContents, depFileContents, scanRoot }, () => {});
726
+ // `provenance:false` is REQUIRED here, not an optimisation. This scan is
727
+ // deliberately scoped to just the patched file(s), so its finding set is a
728
+ // tiny subset of the project's. updateLifecycle marks every open stableId
729
+ // NOT in the set it is handed as `remediated` — so a single fix
730
+ // verification (every /fix, apply_fix, and autopilot iteration runs one)
731
+ // would mass-mark the rest of the project as remediated, then
732
+ // `reintroduced` on the next real scan. The patched content is also not
733
+ // committed, so there is no history to resolve provenance against anyway.
734
+ scan = await (0,engine/* runFullScan */.wW)({ fileContents, depFileContents, scanRoot, provenance: false }, () => {});
685
735
  } catch (e) {
686
736
  return { ok: false, reason: 'rescan-failed', error: e.message };
687
737
  }
@@ -830,12 +880,6 @@ async function verifyFix({
830
880
  // and on the result so a caller cannot mistake it for a verified patch.
831
881
  const _testedPrePatch = !tests.skipped && _candidateDiffersFromDisk(scanRoot, files);
832
882
  const testsOk = tests.skipped ? true : tests.passed === true;
833
- let honesty = null;
834
- if (fixMeta && typeof fixMeta === 'object') {
835
- try { honesty = gateFixOutput(fixMeta); } catch { honesty = null; }
836
- }
837
- _lap('honesty');
838
-
839
883
  // R5 — the PoC leg. Re-run the finding's proof-of-concept against the
840
884
  // CANDIDATE patch inside R1's sandbox. A patch that still lets the PoC
841
885
  // demonstrate the predicted effect has not fixed anything, however green the
@@ -847,6 +891,11 @@ async function verifyFix({
847
891
  // to run, or a sandbox that could not start, is recorded as `inconclusive`
848
892
  // and left out of the verdict entirely. Treating "could not prove it" as
849
893
  // "fixed" is exactly the false confidence this leg exists to prevent.
894
+ //
895
+ // Computed BEFORE the honesty gate (FR-308): a still-exploitable PoC is
896
+ // MECHANICAL evidence, not a self-report, and gateFixOutput cross-checks a
897
+ // self-reported FULL tier against it below — the ordering matters, not
898
+ // just the value.
850
899
  let pocLeg = { status: 'not-requested', reason: null, tier: null };
851
900
  if (poc?.code) {
852
901
  try {
@@ -865,7 +914,54 @@ async function verifyFix({
865
914
  _lap('poc');
866
915
  const pocOk = pocLeg.status !== 'still-exploitable';
867
916
 
917
+ // FR-308: "a mitigation or workaround cannot be represented as a full
918
+ // fix" — gateFixOutput's tier/residual check is a pure self-consistency
919
+ // check (fixMeta.signals is agent-self-reported; see fix-honesty-gate.js's
920
+ // header for why nothing there is server-computable). pocLeg IS
921
+ // server-computable — a real execution result, not a claim — so it is
922
+ // passed through as the one MECHANICAL cross-check available: a
923
+ // self-reported FULL tier is refuted, not just internally inconsistent,
924
+ // when the PoC still demonstrates the vulnerability against the patch.
925
+ // D-0024: `fixMeta` is a shared envelope — FR-307/FR-1002's `approval`
926
+ // key lives alongside FR-308's completeness self-report (`residual`/
927
+ // `verdict`/`evidence`/`signals`). Gating on mere object-truthiness meant
928
+ // a caller supplying ONLY `approval` (a real, common shape once the CLI's
929
+ // --approved-by flag and the MCP schema fix made that reachable) got
930
+ // silently gated on an UNRELATED FR-308 self-consistency check it never
931
+ // engaged with — computeFixTier(undefined) defaults to MITIGATION, which
932
+ // then demands a `residual` nobody was ever asked to supply, blocking an
933
+ // otherwise-genuine, approved fix for a reason that has nothing to do
934
+ // with completeness honesty. Scope the gate to fixMeta shapes that
935
+ // actually make a completeness-adjacent claim.
936
+ const hasHonestyClaim = fixMeta && typeof fixMeta === 'object' &&
937
+ (fixMeta.residual !== undefined || fixMeta.verdict !== undefined ||
938
+ fixMeta.evidence !== undefined || fixMeta.signals !== undefined);
939
+ let honesty = null;
940
+ if (hasHonestyClaim) {
941
+ try { honesty = gateFixOutput(fixMeta, { pocLeg }); } catch { honesty = null; }
942
+ }
943
+ _lap('honesty');
944
+
868
945
  const ok = rescan.ok && (lint.ok || lint.skipped) && testsOk && pocOk && (honesty ? honesty.ok : true);
946
+
947
+ // FR-305 (assurance-hardening PRD): `ok` alone conflates "every leg
948
+ // genuinely ran and passed" with "passed, but a required leg was skipped
949
+ // or unavailable" — `lint.ok`/`testsOk` are both true in the skipped case
950
+ // by design (this codebase does not fail-closed just because a repo has
951
+ // no linter or no detected test runner), so a caller checking only `ok`
952
+ // cannot tell the difference. `lint.skipped` is only ever true when a
953
+ // linter WAS configured but its binary could not be run (missing config
954
+ // entirely returns `runner: 'none'` with no `skipped` field at all — that
955
+ // is a genuine N/A, nothing was required, not a degradation). `verifiedFull`
956
+ // is the honest label: true only when nothing that WAS required was
957
+ // skipped. A caller must never present `ok: true, verifiedFull: false` as
958
+ // "fully verified" — `degradedLegs` names exactly what was skipped so a
959
+ // report can say so plainly instead of a bare pass.
960
+ const degradedLegs = [];
961
+ if (lint.skipped) degradedLegs.push(`lint: ${lint.runner} not installed`);
962
+ if (tests.skipped) degradedLegs.push(`tests: skipped (${tests.reason})`);
963
+ const verifiedFull = ok && degradedLegs.length === 0;
964
+
869
965
  const durations = { ...stages, totalMs: Date.now() - t0 };
870
966
  const summary = [
871
967
  `re-scan: ${rescan.ok ? 'PASS' : 'FAIL — ' + rescan.reason}`,
@@ -887,6 +983,8 @@ async function verifyFix({
887
983
  : pocLeg.status === 'still-exploitable' ? `poc: FAIL — the proof-of-concept still demonstrates the vulnerability against the patch`
888
984
  : pocLeg.status === 'no-longer-proven' ? 'poc: PASS (ran against the patch and no longer demonstrates the vulnerability)'
889
985
  : `poc: inconclusive — not counted either way (${pocLeg.reason || 'no detail reported'})`,
986
+ // FR-305: never let a degraded pass read the same as a full one.
987
+ ok && !verifiedFull ? `NOTE: PASSED, but NOT fully verified — ${degradedLegs.join('; ')}` : null,
890
988
  ].filter(Boolean).join('\n');
891
989
  // Persist the attempt so the distribution can be reported from real runs.
892
990
  // `testsRan` is the load-bearing field: it is what keeps "verified with no
@@ -899,6 +997,7 @@ async function verifyFix({
899
997
  at: new Date().toISOString(),
900
998
  stableId: originalFindingStableId || null,
901
999
  ok,
1000
+ verifiedFull,
902
1001
  testsRan: !tests.skipped,
903
1002
  testsPassed: tests.skipped ? null : tests.passed === true,
904
1003
  testedPrePatch: _testedPrePatch,
@@ -911,7 +1010,7 @@ async function verifyFix({
911
1010
  });
912
1011
  }
913
1012
 
914
- return { ok, rescan, lint, tests, testedPrePatch: _testedPrePatch, honesty, poc: pocLeg, durations, summary };
1013
+ return { ok, verifiedFull, degradedLegs, rescan, lint, tests, testedPrePatch: _testedPrePatch, honesty, poc: pocLeg, durations, summary };
915
1014
  }
916
1015
 
917
1016
 
@@ -0,0 +1,97 @@
1
+ export const id = 552;
2
+ export const ids = [552];
3
+ export const modules = {
4
+
5
+ /***/ 7552:
6
+ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
+
8
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
+ /* harmony export */ buildDeletionReport: () => (/* binding */ buildDeletionReport),
10
+ /* harmony export */ buildExportReport: () => (/* binding */ buildExportReport),
11
+ /* harmony export */ writeDeletionReport: () => (/* binding */ writeDeletionReport),
12
+ /* harmony export */ writeExportReport: () => (/* binding */ writeExportReport)
13
+ /* harmony export */ });
14
+ /* unused harmony exports DELETION_REPORT_FILE, EXPORT_REPORT_FILE */
15
+ /* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1174);
16
+ // FR-706 (assurance-hardening PRD): manifest-based export and deletion
17
+ // reports — "Operators can prove what was exported, deleted, retained, or
18
+ // failed."
19
+ //
20
+ // Two report kinds, one shared per-item shape ({name, status, ...}), each
21
+ // written by its one real caller in bin/agentic-security.js:
22
+ // - `cmdReset` builds a deletion report from the SAME target/preserved
23
+ // data it already computes for its console output (see FR-702/FR-703),
24
+ // reshaped into a durable, structured artifact instead of only ever
25
+ // printed and lost. Written on BOTH a dry run (items carry status
26
+ // 'planned') and a real run (status 'deleted'/'failed'), so an operator
27
+ // can prove either "what would happen" or "what did happen" depending
28
+ // on which they asked for — `dryRun` on the report says which.
29
+ // - `cmdExport` (new) builds an export report after copying every
30
+ // currently-present registered artifact to an operator-chosen
31
+ // destination, naming what was copied (with a hash) and what failed.
32
+ //
33
+ // Each report is a SINGLE OVERWRITTEN file per kind — the same "last
34
+ // action" precedent as last-scan.json, not an ever-growing log. An
35
+ // operator who needs a persistent audit trail across many runs should
36
+ // retain these externally (commit them, forward to their own SIEM); this
37
+ // module's job is only to make the LAST action's outcome durable and
38
+ // provable, not to be the audit log itself.
39
+
40
+
41
+
42
+ const DELETION_REPORT_FILE = 'deletion-report.json';
43
+ const EXPORT_REPORT_FILE = 'export-report.json';
44
+
45
+ function buildDeletionReport({ mode, dryRun, root, items, preserved }) {
46
+ const list = Array.isArray(items) ? items : [];
47
+ const preservedList = Array.isArray(preserved) ? preserved : [];
48
+ return {
49
+ schema: 'agentic-security/deletion-report@1',
50
+ generatedAt: new Date().toISOString(),
51
+ mode: mode || 'reset',
52
+ dryRun: !!dryRun,
53
+ root: root || null,
54
+ items: list,
55
+ preserved: preservedList,
56
+ summary: {
57
+ planned: list.filter(i => i.status === 'planned').length,
58
+ deleted: list.filter(i => i.status === 'deleted').length,
59
+ failed: list.filter(i => i.status === 'failed').length,
60
+ preserved: preservedList.length,
61
+ },
62
+ };
63
+ }
64
+
65
+ function writeDeletionReport(scanRoot, report) {
66
+ try {
67
+ const fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_0__/* .statePath */ .BQ)(scanRoot, DELETION_REPORT_FILE);
68
+ return (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_0__/* .safeWriteState */ .Ep)(fp, JSON.stringify(report, null, 2) + '\n') ? fp : null;
69
+ } catch { return null; }
70
+ }
71
+
72
+ function buildExportReport({ root, outDir, items }) {
73
+ const list = Array.isArray(items) ? items : [];
74
+ return {
75
+ schema: 'agentic-security/export-report@1',
76
+ generatedAt: new Date().toISOString(),
77
+ root: root || null,
78
+ outDir: outDir || null,
79
+ items: list,
80
+ summary: {
81
+ exported: list.filter(i => i.status === 'exported').length,
82
+ failed: list.filter(i => i.status === 'failed').length,
83
+ },
84
+ };
85
+ }
86
+
87
+ function writeExportReport(scanRoot, report) {
88
+ try {
89
+ const fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_0__/* .statePath */ .BQ)(scanRoot, EXPORT_REPORT_FILE);
90
+ return (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_0__/* .safeWriteState */ .Ep)(fp, JSON.stringify(report, null, 2) + '\n') ? fp : null;
91
+ } catch { return null; }
92
+ }
93
+
94
+
95
+ /***/ })
96
+
97
+ };
package/dist/637.index.js CHANGED
@@ -11,7 +11,8 @@ export const modules = {
11
11
  /* harmony export */ renderPrDeltaText: () => (/* binding */ renderPrDeltaText)
12
12
  /* harmony export */ });
13
13
  /* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1421);
14
- /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3474);
14
+ /* harmony import */ var _util_git_hardening_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8844);
15
+ /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7691);
15
16
  // Shadowscan / security-DELTA on PR (v0.72).
16
17
  //
17
18
  // Most SAST PR-comment integrations show absolute counts — "12 findings
@@ -40,16 +41,25 @@ export const modules = {
40
41
 
41
42
 
42
43
 
44
+
43
45
  const FILE_EXT_RE = /\.(?:js|jsx|ts|tsx|mjs|cjs|py|java|cs|kt|go|rb|php|sol|swift|rs|tf|yml|yaml|json|toml|md)$/i;
44
46
  const SEVERITIES = ['critical', 'high', 'medium', 'low', 'info'];
45
47
 
48
+ // `root` is the PR's repository — a scan target, not this project's own
49
+ // trusted checkout — hardened per FR-PROV-024 / the second Finding
50
+ // Provenance PRD audit (same exposure class as
51
+ // provenance/git-evidence.js's `_run`).
46
52
  function _git(root, args) {
47
- const r = (0,node_child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync)('git', args, { cwd: root, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
53
+ const r = (0,node_child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync)('git', (0,_util_git_hardening_js__WEBPACK_IMPORTED_MODULE_1__/* .hardenGitArgs */ .Ax)(args), { cwd: root, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024, env: (0,_util_git_hardening_js__WEBPACK_IMPORTED_MODULE_1__/* .hardenGitEnv */ .Si)() });
48
54
  return { ok: r.status === 0, stdout: r.stdout || '', stderr: r.stderr || '' };
49
55
  }
50
56
 
51
57
  function _readFileAtRef(root, ref, file) {
52
- const r = _git(root, ['show', `${ref}:${file}`]);
58
+ // `--no-textconv`: this blob-cat form of `show` (`<ref>:<file>`, not a
59
+ // diff) was verified NOT reachable via a hostile textconv driver in
60
+ // current git, same as git-evidence.js's getBlobAtCommit — kept for
61
+ // defense-in-depth/uniformity.
62
+ const r = _git(root, ['show', '--no-textconv', `${ref}:${file}`]);
53
63
  return r.ok ? r.stdout : null;
54
64
  }
55
65
 
@@ -70,7 +80,12 @@ async function _scanAtRef(root, ref) {
70
80
  const c = _readFileAtRef(root, ref, f);
71
81
  if (c != null) fileContents[f] = c;
72
82
  }
73
- return (0,_engine_js__WEBPACK_IMPORTED_MODULE_1__/* .runFullScan */ .wW)({ fileContents, scanRoot: root }, () => {});
83
+ // `provenance:false` a base-ref snapshot, not the current working state.
84
+ // Beyond the wasted git walks, updateLifecycle marks every open stableId
85
+ // absent from the finding set it is handed as `remediated`; running the PR
86
+ // delta gate would silently rewrite the project's lifecycle store from the
87
+ // base ref's findings.
88
+ return (0,_engine_js__WEBPACK_IMPORTED_MODULE_2__/* .runFullScan */ .wW)({ fileContents, scanRoot: root, provenance: false }, () => {});
74
89
  }
75
90
 
76
91
  function _summary(findings) {
@@ -85,7 +100,14 @@ function _summary(findings) {
85
100
  }
86
101
 
87
102
  function _changedFiles(root, baseRef, headRef) {
88
- const r = _git(root, ['diff', '--name-only', `${baseRef}...${headRef}`]);
103
+ // `--no-ext-diff`: found during the second-audit-remediation sweep — this
104
+ // is a `git diff` call site (`--name-only`, no content rendered, so not
105
+ // itself a verified exploit path today, same as runScan.js's
106
+ // `changedSince`), but every `diff` invocation in this codebase gets it
107
+ // uniformly per the live exploit VERIFIED in material-change.js's
108
+ // classifyGitDiff (external diff drivers fire on `git diff` even with
109
+ // `--no-textconv`, which is a surface `--no-textconv` alone does not close).
110
+ const r = _git(root, ['diff', '--name-only', '--no-ext-diff', `${baseRef}...${headRef}`]);
89
111
  if (!r.ok) return new Set();
90
112
  return new Set(r.stdout.trim().split('\n').filter(Boolean));
91
113
  }