@clear-capabilities/agentic-security-scanner 0.143.0 → 0.144.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 +304 -0
- package/bin/agentic-security.js +477 -47
- package/dist/1.index.js +223 -0
- package/dist/113.index.js +108 -17
- package/dist/144.index.js +163 -0
- package/dist/178.index.js +1 -1
- package/dist/238.index.js +3 -2
- package/dist/265.index.js +191 -0
- package/dist/384.index.js +1 -1
- package/dist/435.index.js +165 -52
- package/dist/526.index.js +108 -17
- package/dist/552.index.js +97 -0
- package/dist/637.index.js +1 -1
- package/dist/730.index.js +311 -0
- package/dist/736.index.js +301 -0
- package/dist/824.index.js +7 -0
- package/dist/905.index.js +88 -22
- package/dist/920.index.js +491 -0
- package/dist/970.index.js +109 -0
- package/dist/agentic-security.mjs +13 -13
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/calibration-seed.json +2 -0
- package/package.json +19 -11
- package/src/dataflow/index.js +18 -0
- package/src/dataflow/privacy-catalog.js +290 -0
- package/src/dataflow/privacy-deep-walker.js +515 -0
- package/src/dataflow/privacy-governance.js +126 -0
- package/src/dataflow/privacy-inventory.js +154 -0
- package/src/dataflow/privacy-sink-policy.js +125 -0
- package/src/dataflow/privacy-taint.js +115 -54
- package/src/dataflow/privacy-taxonomy.js +233 -0
- package/src/discovery/disprove.js +7 -3
- package/src/discovery/hunter.js +9 -5
- package/src/discovery/index.js +2 -2
- package/src/discovery/llm-invoke.js +69 -13
- package/src/egress/audit.js +147 -0
- package/src/egress/policy.js +313 -0
- package/src/egress/redact.js +180 -0
- package/src/engine.js +575 -288
- package/src/fix/apply-fix-service.js +403 -0
- package/src/fix/approver-registry.js +157 -0
- package/src/llm-validator/index.js +86 -9
- package/src/llm-validator/model-status.js +66 -0
- package/src/mcp/tools.js +157 -50
- package/src/pipeline/analyzer-supervisor.js +93 -0
- package/src/pipeline/analyzer-worker.js +26 -0
- package/src/pipeline/annotator-runner.js +33 -0
- package/src/pipeline/assurance-mode.js +91 -0
- package/src/pipeline/cascade-worker-pool.js +172 -0
- package/src/pipeline/cascade-worker.js +43 -0
- package/src/pipeline/coverage-ledger.js +0 -0
- package/src/pipeline/detector-runner.js +51 -0
- package/src/pipeline/enrichment-completion.js +58 -0
- package/src/pipeline/evidence-provenance.js +91 -0
- package/src/pipeline/finding-schema.js +101 -0
- package/src/pipeline/legacy-compat.js +101 -0
- package/src/pipeline/producer-collector.js +48 -0
- package/src/pipeline/producer-registry.js +112 -0
- package/src/pipeline/scan-health.js +144 -0
- package/src/posture/CLAUDE.md +2 -0
- package/src/posture/accuracy-scorecard.js +96 -1
- package/src/posture/adversary-agent.js +15 -3
- package/src/posture/artifact-registry.js +217 -0
- package/src/posture/auditor-walkthrough.js +70 -8
- package/src/posture/calibration-feedback.js +201 -0
- package/src/posture/calibration-seed.json +2 -0
- package/src/posture/calibration.js +25 -0
- package/src/posture/compliance-evidence-signing.js +131 -0
- package/src/posture/compliance-policy.js +314 -17
- package/src/posture/custom-rules.js +36 -0
- package/src/posture/deterministic.js +8 -1
- package/src/posture/encryption-provider.js +205 -0
- package/src/posture/evidence-grade-wording.js +71 -0
- package/src/posture/fix-history.js +113 -19
- package/src/posture/fix-honesty-gate.js +47 -6
- package/src/posture/fix-verify.js +56 -7
- package/src/posture/fleet.js +0 -0
- package/src/posture/flow-narration.js +7 -2
- package/src/posture/legal-hold.js +140 -0
- package/src/posture/llm-redteam.js +10 -1
- package/src/posture/material-change.js +90 -0
- package/src/posture/policy-bundle.js +274 -0
- package/src/posture/privacy-framework.js +33 -6
- package/src/posture/production-feedback.js +179 -0
- package/src/posture/retention-policy.js +132 -0
- package/src/posture/risk-dollars.js +216 -26
- package/src/posture/scan-checkpoint.js +176 -31
- package/src/posture/state-dir.js +36 -1
- package/src/posture/state-lifecycle-report.js +77 -0
- package/src/posture/suppressions.js +59 -3
- package/src/privacy/ir-adapter.js +380 -0
- package/src/report/index.js +51 -2
- package/src/report/oscal.js +7 -2
- package/src/sast/cpp.js +3 -14
- package/src/sca/llm-function-extract.js +6 -0
package/dist/1.index.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
export const id = 1;
|
|
2
|
+
export const ids = [1];
|
|
3
|
+
export const modules = {
|
|
4
|
+
|
|
5
|
+
/***/ 3001:
|
|
6
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
7
|
+
|
|
8
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
9
|
+
/* harmony export */ OUTCOMES: () => (/* binding */ OUTCOMES),
|
|
10
|
+
/* harmony export */ buildCalibrationReport: () => (/* binding */ buildCalibrationReport),
|
|
11
|
+
/* harmony export */ recordCalibrationFeedback: () => (/* binding */ recordCalibrationFeedback),
|
|
12
|
+
/* harmony export */ renderCalibrationReportSummary: () => (/* binding */ renderCalibrationReportSummary)
|
|
13
|
+
/* harmony export */ });
|
|
14
|
+
/* unused harmony exports CALIBRATION_FEEDBACK_FILE, loadCalibrationFeedback, _internals */
|
|
15
|
+
/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
|
|
16
|
+
/* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7598);
|
|
17
|
+
/* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1174);
|
|
18
|
+
// FR-806 (assurance-hardening PRD): "Validate model calibration against
|
|
19
|
+
// accepted and realized incidents where customers opt in | Calibration
|
|
20
|
+
// reports are aggregated and privacy-preserving."
|
|
21
|
+
//
|
|
22
|
+
// SCOPE, stated explicitly because the PRD's own wording is terse: this
|
|
23
|
+
// codebase has no SaaS control plane and takes no runtime cloud calls
|
|
24
|
+
// (root CLAUDE.md's own "No runtime cloud calls" rule, and E10's own goal
|
|
25
|
+
// of "consistent policy... without requiring a SaaS control plane") — so
|
|
26
|
+
// "aggregated" here means aggregated WITHIN one installation, across every
|
|
27
|
+
// feedback event an operator has recorded over time, never aggregated
|
|
28
|
+
// ACROSS installations on some central server. An operator who wants a
|
|
29
|
+
// cross-organization rollup can feed this module's own report output into
|
|
30
|
+
// their own aggregation, the same way `fleet.js` composes many single-repo
|
|
31
|
+
// scans without a hosted backend.
|
|
32
|
+
//
|
|
33
|
+
// TWO OUTCOMES, matching the PRD's own two named cases:
|
|
34
|
+
// 'accepted-risk' — an operator/customer reviewed a finding, accepted
|
|
35
|
+
// the risk, and (later, of their own accord) reports
|
|
36
|
+
// that no incident occurred. A well-calibrated model
|
|
37
|
+
// should have predicted LOW confidence/risk for
|
|
38
|
+
// these.
|
|
39
|
+
// 'realized-incident' — an operator reports that a finding's
|
|
40
|
+
// vulnerability WAS actually exploited or otherwise
|
|
41
|
+
// caused a real incident. A well-calibrated model
|
|
42
|
+
// should have predicted HIGH confidence/risk for
|
|
43
|
+
// these — a realized incident on a LOW-predicted
|
|
44
|
+
// finding is exactly the miscalibration this
|
|
45
|
+
// requirement exists to surface.
|
|
46
|
+
//
|
|
47
|
+
// PRIVACY-PRESERVING AT THE SOURCE, not just at the report layer: a
|
|
48
|
+
// feedback record snapshots only the model's OWN prediction signals
|
|
49
|
+
// (confidence, severity, riskDollars.ev) plus the operator's outcome and
|
|
50
|
+
// optional free-text note — never file path, line, vuln title, or code
|
|
51
|
+
// snippet. This mirrors this codebase's existing privacy modules' own
|
|
52
|
+
// discipline (dataflow/privacy-*.js) of never persisting more than a
|
|
53
|
+
// report needs to answer its one question.
|
|
54
|
+
//
|
|
55
|
+
// OPT-IN, genuinely: nothing here is ever auto-populated by a scan. A
|
|
56
|
+
// record exists only when an operator explicitly calls
|
|
57
|
+
// `recordCalibrationFeedback` (via the CLI's `calibration-feedback record`
|
|
58
|
+
// command) — the file simply does not exist for every project that never
|
|
59
|
+
// opts in, and every read degrades to "no data" rather than throwing.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
const CALIBRATION_FEEDBACK_FILE = 'calibration-feedback.jsonl';
|
|
66
|
+
const OUTCOMES = Object.freeze(['accepted-risk', 'realized-incident']);
|
|
67
|
+
|
|
68
|
+
// Below this many samples, a rate is an artifact of the sample, not a
|
|
69
|
+
// property of the model — same precedent as fix-metrics.js's RELIABLE_N.
|
|
70
|
+
const RELIABLE_N = 10;
|
|
71
|
+
|
|
72
|
+
function _findFinding(scanRoot, findingId) {
|
|
73
|
+
try {
|
|
74
|
+
const raw = JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync((0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, 'last-scan.json'), 'utf8'));
|
|
75
|
+
const findings = Array.isArray(raw.findings) ? raw.findings : [];
|
|
76
|
+
return findings.find(f => f && (f.id === findingId || f.stableId === findingId)) || null;
|
|
77
|
+
} catch { return null; }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// FR-806 privacy fix: the caller-supplied findingId is routinely a finding's
|
|
81
|
+
// plain `.id` (e.g. "client-side:DANGEROUS_INNERHTML:src/billing/secret.js:142"),
|
|
82
|
+
// which embeds the exact file path and line this module's own docstring
|
|
83
|
+
// promises never to persist. Never write the caller's raw string to disk:
|
|
84
|
+
// prefer the matched finding's own privacy-safe `.stableId` (a hash, by
|
|
85
|
+
// construction elsewhere in this codebase), and otherwise hash the input
|
|
86
|
+
// ourselves so a stale/removed finding's id can never leak path/line either.
|
|
87
|
+
function _privacySafeFindingId(findingId, finding) {
|
|
88
|
+
if (finding && typeof finding.stableId === 'string' && finding.stableId) return finding.stableId;
|
|
89
|
+
return node_crypto__WEBPACK_IMPORTED_MODULE_1__.createHash('sha256').update(findingId).digest('hex').slice(0, 16);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Record one opt-in calibration-feedback event. Snapshots ONLY the
|
|
94
|
+
* model's own prediction signals for the named finding (if it is still
|
|
95
|
+
* present in the last scan — a finding fixed/removed since is still
|
|
96
|
+
* recordable, just without a fresh snapshot) plus the outcome and an
|
|
97
|
+
* optional note. Never throws; returns {ok, record} or {ok:false, reason}.
|
|
98
|
+
*/
|
|
99
|
+
function recordCalibrationFeedback(scanRoot, { findingId, outcome, note } = {}) {
|
|
100
|
+
if (!findingId || typeof findingId !== 'string') return { ok: false, reason: '--finding-id is required' };
|
|
101
|
+
if (!OUTCOMES.includes(outcome)) return { ok: false, reason: `--outcome must be one of: ${OUTCOMES.join(', ')}` };
|
|
102
|
+
const finding = _findFinding(scanRoot, findingId);
|
|
103
|
+
const record = {
|
|
104
|
+
at: new Date().toISOString(),
|
|
105
|
+
findingId: _privacySafeFindingId(findingId, finding),
|
|
106
|
+
outcome,
|
|
107
|
+
predictedConfidence: finding && typeof finding.confidence === 'number' ? finding.confidence : null,
|
|
108
|
+
predictedConfidenceTier: finding?.confidenceTier || null,
|
|
109
|
+
predictedSeverity: finding?.severity || null,
|
|
110
|
+
predictedRiskEv: finding?.riskDollars && typeof finding.riskDollars.ev === 'number' ? finding.riskDollars.ev : null,
|
|
111
|
+
note: note ? String(note).slice(0, 280) : null,
|
|
112
|
+
};
|
|
113
|
+
// Append-only, same primitives as fix-metrics.js/triage-memory.js's own
|
|
114
|
+
// JSONL writers — a single fs.appendFileSync, never a read-modify-write
|
|
115
|
+
// of the whole file (which would also reintroduce a TOCTOU between an
|
|
116
|
+
// existence check and the write, the exact anti-pattern this codebase's
|
|
117
|
+
// own conventions forbid).
|
|
118
|
+
const dir = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .stateDir */ .Pn)(scanRoot);
|
|
119
|
+
if (!(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.isSafeStateDir)(dir)) return { ok: false, reason: 'no safe state directory' };
|
|
120
|
+
if (!(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.stateWritesEnabled)()) return { ok: false, reason: 'state writes are disabled (--no-state)' };
|
|
121
|
+
try {
|
|
122
|
+
node_fs__WEBPACK_IMPORTED_MODULE_0__.mkdirSync(dir, { recursive: true });
|
|
123
|
+
node_fs__WEBPACK_IMPORTED_MODULE_0__.appendFileSync((0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, CALIBRATION_FEEDBACK_FILE), JSON.stringify(record) + '\n', 'utf8');
|
|
124
|
+
} catch (e) { return { ok: false, reason: e.message }; }
|
|
125
|
+
return { ok: true, record };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Every well-formed feedback event ever recorded. A line that fails to
|
|
130
|
+
* parse or lacks a valid outcome is skipped, not fatal.
|
|
131
|
+
*/
|
|
132
|
+
function loadCalibrationFeedback(scanRoot) {
|
|
133
|
+
let fp;
|
|
134
|
+
try { fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, CALIBRATION_FEEDBACK_FILE); } catch { return []; }
|
|
135
|
+
let raw;
|
|
136
|
+
try { raw = node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8'); } catch { return []; }
|
|
137
|
+
const out = [];
|
|
138
|
+
for (const line of raw.split('\n')) {
|
|
139
|
+
if (!line.trim()) continue;
|
|
140
|
+
try {
|
|
141
|
+
const rec = JSON.parse(line);
|
|
142
|
+
if (rec && OUTCOMES.includes(rec.outcome)) out.push(rec);
|
|
143
|
+
} catch { /* torn or hand-edited line — drop it, keep the rest */ }
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function _avg(nums) {
|
|
149
|
+
const v = nums.filter(n => typeof n === 'number' && Number.isFinite(n));
|
|
150
|
+
return v.length ? v.reduce((a, b) => a + b, 0) / v.length : null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function _summarizeOutcome(records) {
|
|
154
|
+
const withConfidence = records.map(r => r.predictedConfidence).filter(c => typeof c === 'number');
|
|
155
|
+
return {
|
|
156
|
+
n: records.length,
|
|
157
|
+
reliable: records.length >= RELIABLE_N,
|
|
158
|
+
avgPredictedConfidence: _avg(withConfidence),
|
|
159
|
+
withoutPrediction: records.length - withConfidence.length,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Aggregate ALL recorded feedback (this installation only — see the
|
|
165
|
+
* module header for why cross-installation aggregation is out of scope)
|
|
166
|
+
* into a privacy-preserving report: rates and averages only, never a
|
|
167
|
+
* per-finding breakdown, never file/line/vuln text (none of that was ever
|
|
168
|
+
* stored in the first place).
|
|
169
|
+
*/
|
|
170
|
+
function buildCalibrationReport(scanRoot) {
|
|
171
|
+
const records = loadCalibrationFeedback(scanRoot);
|
|
172
|
+
const acceptedRisk = records.filter(r => r.outcome === 'accepted-risk');
|
|
173
|
+
const realizedIncident = records.filter(r => r.outcome === 'realized-incident');
|
|
174
|
+
return {
|
|
175
|
+
schema: 'agentic-security/calibration-report@1',
|
|
176
|
+
generatedAt: new Date().toISOString(),
|
|
177
|
+
totalEvents: records.length,
|
|
178
|
+
acceptedRisk: _summarizeOutcome(acceptedRisk),
|
|
179
|
+
realizedIncident: _summarizeOutcome(realizedIncident),
|
|
180
|
+
// The calibration question itself: accepted-risk events SHOULD skew
|
|
181
|
+
// toward low predicted confidence; realized-incident events SHOULD
|
|
182
|
+
// skew toward high. This flag is a coarse, disclosed-uncertainty
|
|
183
|
+
// signal, not a verdict — it only ever fires when BOTH buckets have
|
|
184
|
+
// enough samples to say anything at all (RELIABLE_N each).
|
|
185
|
+
possibleMiscalibration: (() => {
|
|
186
|
+
const a = _summarizeOutcome(acceptedRisk);
|
|
187
|
+
const r = _summarizeOutcome(realizedIncident);
|
|
188
|
+
if (!a.reliable || !r.reliable || a.avgPredictedConfidence == null || r.avgPredictedConfidence == null) return null;
|
|
189
|
+
return r.avgPredictedConfidence <= a.avgPredictedConfidence;
|
|
190
|
+
})(),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* One block of human-readable summary, or null when nothing has ever been
|
|
196
|
+
* recorded — genuinely opt-in, so "nothing recorded" is the expected
|
|
197
|
+
* default state for almost every project, not an error.
|
|
198
|
+
*/
|
|
199
|
+
function renderCalibrationReportSummary(report) {
|
|
200
|
+
if (!report || report.totalEvents === 0) return null;
|
|
201
|
+
const lines = [
|
|
202
|
+
'Calibration feedback (this installation, opt-in):',
|
|
203
|
+
` accepted-risk: n=${report.acceptedRisk.n}${report.acceptedRisk.reliable ? '' : ' (below reliable sample size)'}` +
|
|
204
|
+
(report.acceptedRisk.avgPredictedConfidence != null ? ` avg predicted confidence=${report.acceptedRisk.avgPredictedConfidence.toFixed(2)}` : ''),
|
|
205
|
+
` realized-incident: n=${report.realizedIncident.n}${report.realizedIncident.reliable ? '' : ' (below reliable sample size)'}` +
|
|
206
|
+
(report.realizedIncident.avgPredictedConfidence != null ? ` avg predicted confidence=${report.realizedIncident.avgPredictedConfidence.toFixed(2)}` : ''),
|
|
207
|
+
];
|
|
208
|
+
if (report.possibleMiscalibration === true) {
|
|
209
|
+
lines.push(' ⚠ realized incidents were NOT predicted with higher confidence than accepted risks — possible miscalibration.');
|
|
210
|
+
} else if (report.possibleMiscalibration === false) {
|
|
211
|
+
lines.push(' ✓ realized incidents were predicted with higher confidence than accepted risks, as expected.');
|
|
212
|
+
} else {
|
|
213
|
+
lines.push(' (not enough samples in both buckets yet to assess calibration — this is not a pass or fail.)');
|
|
214
|
+
}
|
|
215
|
+
return lines.join('\n');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const _internals = { RELIABLE_N, _findFinding };
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
/***/ })
|
|
222
|
+
|
|
223
|
+
};
|
package/dist/113.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,
|
|
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);
|
|
@@ -491,8 +492,8 @@ var external_node_child_process_ = __webpack_require__(1421);
|
|
|
491
492
|
var external_node_fs_ = __webpack_require__(3024);
|
|
492
493
|
// EXTERNAL MODULE: external "node:path"
|
|
493
494
|
var external_node_path_ = __webpack_require__(6760);
|
|
494
|
-
// EXTERNAL MODULE: ./src/engine.js +
|
|
495
|
-
var engine = __webpack_require__(
|
|
495
|
+
// EXTERNAL MODULE: ./src/engine.js + 211 modules
|
|
496
|
+
var engine = __webpack_require__(7087);
|
|
496
497
|
;// CONCATENATED MODULE: ./src/posture/fix-honesty-gate.js
|
|
497
498
|
// Deterministic honesty gates on fix / finding output (#7).
|
|
498
499
|
//
|
|
@@ -637,23 +638,64 @@ function computeFixTier(signals) {
|
|
|
637
638
|
}
|
|
638
639
|
|
|
639
640
|
/**
|
|
640
|
-
*
|
|
641
|
+
* FR-308: cross-check a fix-completeness TIER against MECHANICAL evidence,
|
|
642
|
+
* when any is available. `signals` (computeFixTier's input) is agent-
|
|
643
|
+
* self-reported — this module cannot compute sinkSignatureChanged /
|
|
644
|
+
* allCallersRouted / testDiscriminates itself (see the header above: "the
|
|
645
|
+
* gate can only run against claims the AGENT self-reports... nothing here
|
|
646
|
+
* is server-computable"). `pocLeg` is different: fix-verify.js's PoC leg is
|
|
647
|
+
* a REAL execution result (posture/CLAUDE.md's execution-proof tiers), not
|
|
648
|
+
* a claim. When it is available and shows the proof-of-concept STILL
|
|
649
|
+
* demonstrates the vulnerability against the patch, a self-reported FULL
|
|
650
|
+
* tier is not merely internally inconsistent — it is REFUTED by fact. This
|
|
651
|
+
* is the literal "a mitigation or workaround cannot be represented as a
|
|
652
|
+
* full fix" acceptance criterion, now backed by mechanical evidence where
|
|
653
|
+
* it exists rather than by self-report consistency alone.
|
|
641
654
|
*
|
|
642
|
-
*
|
|
643
|
-
*
|
|
655
|
+
* A `pocLeg` of `not-requested` or `inconclusive` carries no mechanical
|
|
656
|
+
* signal either way and is a no-op here — this check can only ever ADD a
|
|
657
|
+
* violation on real contrary evidence, never manufacture one from absence.
|
|
658
|
+
*
|
|
659
|
+
* @param {string} tier
|
|
660
|
+
* @param {{status: string, reason?: string}|null} pocLeg
|
|
661
|
+
* @returns {{ ok: boolean, violations: string[] }}
|
|
662
|
+
*/
|
|
663
|
+
function checkMechanicalTierEvidence(tier, pocLeg) {
|
|
664
|
+
if (!pocLeg || typeof pocLeg !== 'object') return { ok: true, violations: [] };
|
|
665
|
+
if (tier === 'FULL' && pocLeg.status === 'still-exploitable') {
|
|
666
|
+
return {
|
|
667
|
+
ok: false,
|
|
668
|
+
violations: [`tier 'FULL' is refuted by mechanical evidence: the proof-of-concept still demonstrates the vulnerability against the patch${pocLeg.reason ? ` (${pocLeg.reason})` : ''}`],
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
return { ok: true, violations: [] };
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Compose the four gates for a single fix's output.
|
|
676
|
+
*
|
|
677
|
+
* ok = residual-honesty ok AND evidence-citation ok AND mechanical-tier-
|
|
678
|
+
* evidence ok, further constrained by the tier/residual consistency
|
|
679
|
+
* invariant:
|
|
644
680
|
* - a FULL tier must NOT carry a residual (a full fix has nothing left);
|
|
645
681
|
* - a non-FULL tier MUST document a residual (say what's still open).
|
|
646
682
|
*
|
|
647
683
|
* @param {{ residual?: string, verdict?: string, evidence?: any, signals?: object }} input
|
|
684
|
+
* @param {{ pocLeg?: object|null }} [mechanical] - FR-308: optional real
|
|
685
|
+
* execution evidence (fix-verify.js's pocLeg) to cross-check the
|
|
686
|
+
* self-reported tier against. Omitted entirely by any caller that has no
|
|
687
|
+
* PoC leg to offer — this parameter never REQUIRES mechanical evidence,
|
|
688
|
+
* it only USES it when present.
|
|
648
689
|
* @returns {{ ok: boolean, tier: string, violations: string[] }}
|
|
649
690
|
*/
|
|
650
|
-
function gateFixOutput({ residual, verdict, evidence, signals } = {}) {
|
|
691
|
+
function gateFixOutput({ residual, verdict, evidence, signals } = {}, { pocLeg = null } = {}) {
|
|
651
692
|
const tier = computeFixTier(signals);
|
|
652
693
|
const residualCheck = checkResidualHonesty(residual);
|
|
653
694
|
const evidenceCheck = requireCitedEvidence(verdict, evidence);
|
|
695
|
+
const mechanicalCheck = checkMechanicalTierEvidence(tier, pocLeg);
|
|
654
696
|
|
|
655
|
-
const violations = [...residualCheck.violations, ...evidenceCheck.violations];
|
|
656
|
-
let ok = residualCheck.ok && evidenceCheck.ok;
|
|
697
|
+
const violations = [...residualCheck.violations, ...evidenceCheck.violations, ...mechanicalCheck.violations];
|
|
698
|
+
let ok = residualCheck.ok && evidenceCheck.ok && mechanicalCheck.ok;
|
|
657
699
|
|
|
658
700
|
const residualEmpty = typeof residual !== 'string' || residual.trim() === '';
|
|
659
701
|
if (tier === 'FULL' && !residualEmpty) {
|
|
@@ -1011,12 +1053,6 @@ async function verifyFix({
|
|
|
1011
1053
|
// and on the result so a caller cannot mistake it for a verified patch.
|
|
1012
1054
|
const _testedPrePatch = !tests.skipped && _candidateDiffersFromDisk(scanRoot, files);
|
|
1013
1055
|
const testsOk = tests.skipped ? true : tests.passed === true;
|
|
1014
|
-
let honesty = null;
|
|
1015
|
-
if (fixMeta && typeof fixMeta === 'object') {
|
|
1016
|
-
try { honesty = gateFixOutput(fixMeta); } catch { honesty = null; }
|
|
1017
|
-
}
|
|
1018
|
-
_lap('honesty');
|
|
1019
|
-
|
|
1020
1056
|
// R5 — the PoC leg. Re-run the finding's proof-of-concept against the
|
|
1021
1057
|
// CANDIDATE patch inside R1's sandbox. A patch that still lets the PoC
|
|
1022
1058
|
// demonstrate the predicted effect has not fixed anything, however green the
|
|
@@ -1028,6 +1064,11 @@ async function verifyFix({
|
|
|
1028
1064
|
// to run, or a sandbox that could not start, is recorded as `inconclusive`
|
|
1029
1065
|
// and left out of the verdict entirely. Treating "could not prove it" as
|
|
1030
1066
|
// "fixed" is exactly the false confidence this leg exists to prevent.
|
|
1067
|
+
//
|
|
1068
|
+
// Computed BEFORE the honesty gate (FR-308): a still-exploitable PoC is
|
|
1069
|
+
// MECHANICAL evidence, not a self-report, and gateFixOutput cross-checks a
|
|
1070
|
+
// self-reported FULL tier against it below — the ordering matters, not
|
|
1071
|
+
// just the value.
|
|
1031
1072
|
let pocLeg = { status: 'not-requested', reason: null, tier: null };
|
|
1032
1073
|
if (poc?.code) {
|
|
1033
1074
|
try {
|
|
@@ -1046,7 +1087,54 @@ async function verifyFix({
|
|
|
1046
1087
|
_lap('poc');
|
|
1047
1088
|
const pocOk = pocLeg.status !== 'still-exploitable';
|
|
1048
1089
|
|
|
1090
|
+
// FR-308: "a mitigation or workaround cannot be represented as a full
|
|
1091
|
+
// fix" — gateFixOutput's tier/residual check is a pure self-consistency
|
|
1092
|
+
// check (fixMeta.signals is agent-self-reported; see fix-honesty-gate.js's
|
|
1093
|
+
// header for why nothing there is server-computable). pocLeg IS
|
|
1094
|
+
// server-computable — a real execution result, not a claim — so it is
|
|
1095
|
+
// passed through as the one MECHANICAL cross-check available: a
|
|
1096
|
+
// self-reported FULL tier is refuted, not just internally inconsistent,
|
|
1097
|
+
// when the PoC still demonstrates the vulnerability against the patch.
|
|
1098
|
+
// D-0024: `fixMeta` is a shared envelope — FR-307/FR-1002's `approval`
|
|
1099
|
+
// key lives alongside FR-308's completeness self-report (`residual`/
|
|
1100
|
+
// `verdict`/`evidence`/`signals`). Gating on mere object-truthiness meant
|
|
1101
|
+
// a caller supplying ONLY `approval` (a real, common shape once the CLI's
|
|
1102
|
+
// --approved-by flag and the MCP schema fix made that reachable) got
|
|
1103
|
+
// silently gated on an UNRELATED FR-308 self-consistency check it never
|
|
1104
|
+
// engaged with — computeFixTier(undefined) defaults to MITIGATION, which
|
|
1105
|
+
// then demands a `residual` nobody was ever asked to supply, blocking an
|
|
1106
|
+
// otherwise-genuine, approved fix for a reason that has nothing to do
|
|
1107
|
+
// with completeness honesty. Scope the gate to fixMeta shapes that
|
|
1108
|
+
// actually make a completeness-adjacent claim.
|
|
1109
|
+
const hasHonestyClaim = fixMeta && typeof fixMeta === 'object' &&
|
|
1110
|
+
(fixMeta.residual !== undefined || fixMeta.verdict !== undefined ||
|
|
1111
|
+
fixMeta.evidence !== undefined || fixMeta.signals !== undefined);
|
|
1112
|
+
let honesty = null;
|
|
1113
|
+
if (hasHonestyClaim) {
|
|
1114
|
+
try { honesty = gateFixOutput(fixMeta, { pocLeg }); } catch { honesty = null; }
|
|
1115
|
+
}
|
|
1116
|
+
_lap('honesty');
|
|
1117
|
+
|
|
1049
1118
|
const ok = rescan.ok && (lint.ok || lint.skipped) && testsOk && pocOk && (honesty ? honesty.ok : true);
|
|
1119
|
+
|
|
1120
|
+
// FR-305 (assurance-hardening PRD): `ok` alone conflates "every leg
|
|
1121
|
+
// genuinely ran and passed" with "passed, but a required leg was skipped
|
|
1122
|
+
// or unavailable" — `lint.ok`/`testsOk` are both true in the skipped case
|
|
1123
|
+
// by design (this codebase does not fail-closed just because a repo has
|
|
1124
|
+
// no linter or no detected test runner), so a caller checking only `ok`
|
|
1125
|
+
// cannot tell the difference. `lint.skipped` is only ever true when a
|
|
1126
|
+
// linter WAS configured but its binary could not be run (missing config
|
|
1127
|
+
// entirely returns `runner: 'none'` with no `skipped` field at all — that
|
|
1128
|
+
// is a genuine N/A, nothing was required, not a degradation). `verifiedFull`
|
|
1129
|
+
// is the honest label: true only when nothing that WAS required was
|
|
1130
|
+
// skipped. A caller must never present `ok: true, verifiedFull: false` as
|
|
1131
|
+
// "fully verified" — `degradedLegs` names exactly what was skipped so a
|
|
1132
|
+
// report can say so plainly instead of a bare pass.
|
|
1133
|
+
const degradedLegs = [];
|
|
1134
|
+
if (lint.skipped) degradedLegs.push(`lint: ${lint.runner} not installed`);
|
|
1135
|
+
if (tests.skipped) degradedLegs.push(`tests: skipped (${tests.reason})`);
|
|
1136
|
+
const verifiedFull = ok && degradedLegs.length === 0;
|
|
1137
|
+
|
|
1050
1138
|
const durations = { ...stages, totalMs: Date.now() - t0 };
|
|
1051
1139
|
const summary = [
|
|
1052
1140
|
`re-scan: ${rescan.ok ? 'PASS' : 'FAIL — ' + rescan.reason}`,
|
|
@@ -1068,6 +1156,8 @@ async function verifyFix({
|
|
|
1068
1156
|
: pocLeg.status === 'still-exploitable' ? `poc: FAIL — the proof-of-concept still demonstrates the vulnerability against the patch`
|
|
1069
1157
|
: pocLeg.status === 'no-longer-proven' ? 'poc: PASS (ran against the patch and no longer demonstrates the vulnerability)'
|
|
1070
1158
|
: `poc: inconclusive — not counted either way (${pocLeg.reason || 'no detail reported'})`,
|
|
1159
|
+
// FR-305: never let a degraded pass read the same as a full one.
|
|
1160
|
+
ok && !verifiedFull ? `NOTE: PASSED, but NOT fully verified — ${degradedLegs.join('; ')}` : null,
|
|
1071
1161
|
].filter(Boolean).join('\n');
|
|
1072
1162
|
// Persist the attempt so the distribution can be reported from real runs.
|
|
1073
1163
|
// `testsRan` is the load-bearing field: it is what keeps "verified with no
|
|
@@ -1080,6 +1170,7 @@ async function verifyFix({
|
|
|
1080
1170
|
at: new Date().toISOString(),
|
|
1081
1171
|
stableId: originalFindingStableId || null,
|
|
1082
1172
|
ok,
|
|
1173
|
+
verifiedFull,
|
|
1083
1174
|
testsRan: !tests.skipped,
|
|
1084
1175
|
testsPassed: tests.skipped ? null : tests.passed === true,
|
|
1085
1176
|
testedPrePatch: _testedPrePatch,
|
|
@@ -1092,7 +1183,7 @@ async function verifyFix({
|
|
|
1092
1183
|
});
|
|
1093
1184
|
}
|
|
1094
1185
|
|
|
1095
|
-
return { ok, rescan, lint, tests, testedPrePatch: _testedPrePatch, honesty, poc: pocLeg, durations, summary };
|
|
1186
|
+
return { ok, verifiedFull, degradedLegs, rescan, lint, tests, testedPrePatch: _testedPrePatch, honesty, poc: pocLeg, durations, summary };
|
|
1096
1187
|
}
|
|
1097
1188
|
|
|
1098
1189
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
export const id = 144;
|
|
2
|
+
export const ids = [144];
|
|
3
|
+
export const modules = {
|
|
4
|
+
|
|
5
|
+
/***/ 5144:
|
|
6
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
7
|
+
|
|
8
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
9
|
+
/* harmony export */ addLegalHold: () => (/* binding */ addLegalHold),
|
|
10
|
+
/* harmony export */ isUnderHold: () => (/* binding */ isUnderHold),
|
|
11
|
+
/* harmony export */ listLegalHolds: () => (/* binding */ listLegalHolds),
|
|
12
|
+
/* harmony export */ loadLegalHolds: () => (/* binding */ loadLegalHolds),
|
|
13
|
+
/* harmony export */ removeLegalHold: () => (/* binding */ removeLegalHold)
|
|
14
|
+
/* harmony export */ });
|
|
15
|
+
/* unused harmony export LEGAL_HOLD_FILE */
|
|
16
|
+
/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
|
|
17
|
+
/* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1174);
|
|
18
|
+
/* harmony import */ var _artifact_registry_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(471);
|
|
19
|
+
// FR-707 (assurance-hardening PRD): "Support legal hold and policy-
|
|
20
|
+
// authorized retention exceptions | Legal hold is identity-bound, reasoned,
|
|
21
|
+
// time-bounded where applicable, and auditable."
|
|
22
|
+
//
|
|
23
|
+
// A third instance of the recurring {owner, reason, expires_at} exception
|
|
24
|
+
// shape this codebase already uses twice — `posture/suppressions.js`'s
|
|
25
|
+
// pro-tier exception (scoped to a FINDING) and `posture/compliance-policy.js`'s
|
|
26
|
+
// structured `not-applicable` (scoped to a COMPLIANCE CONTROL). Per D-0025,
|
|
27
|
+
// these are deliberately distinct mechanisms serving different subjects, not
|
|
28
|
+
// one shared module — this file is the third subject: a STATE ARTIFACT.
|
|
29
|
+
//
|
|
30
|
+
// Field naming matches the existing two schemas' snake_case convention
|
|
31
|
+
// (`owner`, `reason`, `expires_at`) rather than inventing a fourth style.
|
|
32
|
+
//
|
|
33
|
+
// identity-bound -> `owner` (required, who placed the hold and is
|
|
34
|
+
// accountable for lifting it)
|
|
35
|
+
// reasoned -> `reason` (required — "we might need this later" is
|
|
36
|
+
// not a reason; same discipline suppressions.js
|
|
37
|
+
// already enforces for its own exceptions)
|
|
38
|
+
// time-bounded
|
|
39
|
+
// where applicable -> `expires_at` is OPTIONAL: a null/absent value is
|
|
40
|
+
// an INDEFINITE hold, which the acceptance
|
|
41
|
+
// criterion's own "where applicable" phrase
|
|
42
|
+
// explicitly allows (a genuine legal matter may have
|
|
43
|
+
// no known end date) — an ISO date value behaves
|
|
44
|
+
// exactly like FR-506/FR-1004's own expiring
|
|
45
|
+
// exceptions: once past, the hold is no longer
|
|
46
|
+
// active and the artifact is exposed to its normal
|
|
47
|
+
// retention TTL again.
|
|
48
|
+
// auditable -> persisted as a single JSON array under
|
|
49
|
+
// `.agentic-security/legal-holds.json` (itself
|
|
50
|
+
// registered as operator-config — an operator/legal
|
|
51
|
+
// team's own input, never scanner-written from scan
|
|
52
|
+
// results), readable via `listLegalHolds`.
|
|
53
|
+
//
|
|
54
|
+
// Consulted from TWO places, not one: `retention-policy.js#findExpiredArtifacts`
|
|
55
|
+
// (defense in depth for any caller reaching it directly) AND `cmdReset`
|
|
56
|
+
// itself for its PLAIN (non-`--expired`) path, which deletes every
|
|
57
|
+
// registered 'generated' artifact unconditionally and would otherwise blow
|
|
58
|
+
// through a hold that only gated TTL expiry.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
const LEGAL_HOLD_FILE = 'legal-holds.json';
|
|
65
|
+
|
|
66
|
+
function _loadRaw(scanRoot) {
|
|
67
|
+
let fp;
|
|
68
|
+
try { fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_1__/* .statePath */ .BQ)(scanRoot, LEGAL_HOLD_FILE); } catch { return []; }
|
|
69
|
+
let raw;
|
|
70
|
+
try { raw = node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8'); } catch { return []; }
|
|
71
|
+
try {
|
|
72
|
+
const doc = JSON.parse(raw);
|
|
73
|
+
return Array.isArray(doc) ? doc : [];
|
|
74
|
+
} catch { return []; }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* All legal holds ever recorded for this project, malformed entries
|
|
79
|
+
* dropped rather than throwing. Never filters by expiry — see
|
|
80
|
+
* `isUnderHold`/`listLegalHolds` for that.
|
|
81
|
+
*/
|
|
82
|
+
function loadLegalHolds(scanRoot) {
|
|
83
|
+
return _loadRaw(scanRoot).filter(h => h && typeof h === 'object' && typeof h.artifact === 'string' && typeof h.owner === 'string' && typeof h.reason === 'string');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Is `artifactName` currently protected by an active (non-expired) hold?
|
|
88
|
+
* Returns the matching hold record, or null. Multiple holds on the same
|
|
89
|
+
* artifact are permitted (e.g. two independent legal matters); the first
|
|
90
|
+
* still-active one found is returned.
|
|
91
|
+
*/
|
|
92
|
+
function isUnderHold(artifactName, holds, now = Date.now()) {
|
|
93
|
+
for (const h of holds) {
|
|
94
|
+
if (h.artifact !== artifactName) continue;
|
|
95
|
+
if (!h.expires_at) return h; // indefinite hold — always active
|
|
96
|
+
const t = Date.parse(h.expires_at);
|
|
97
|
+
if (!Number.isFinite(t) || t >= now) return h;
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Active (non-expired) holds only, unless `includeExpired`. For
|
|
104
|
+
* `legal-hold list` / auditing.
|
|
105
|
+
*/
|
|
106
|
+
function listLegalHolds(scanRoot, { includeExpired = false, now = Date.now() } = {}) {
|
|
107
|
+
const holds = loadLegalHolds(scanRoot);
|
|
108
|
+
if (includeExpired) return holds;
|
|
109
|
+
return holds.filter(h => {
|
|
110
|
+
if (!h.expires_at) return true;
|
|
111
|
+
const t = Date.parse(h.expires_at);
|
|
112
|
+
return !Number.isFinite(t) || t >= now;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Add a legal hold. Validates identity-bound + reasoned up front (both
|
|
118
|
+
* required, non-empty) and that `expires_at`, if given, is a parseable
|
|
119
|
+
* date in the future — an already-expired hold would be a hold that
|
|
120
|
+
* protects nothing, which is never a legitimate request. `artifact` must
|
|
121
|
+
* name a real registered artifact (artifact-registry.js) — a hold on an
|
|
122
|
+
* unrecognised name can never protect anything and almost always means a
|
|
123
|
+
* typo. Returns `{ok:true, hold}` or `{ok:false, reason}`; never throws.
|
|
124
|
+
*/
|
|
125
|
+
function addLegalHold(scanRoot, { artifact, owner, reason, expires_at } = {}) {
|
|
126
|
+
if (!artifact || typeof artifact !== 'string') return { ok: false, reason: '--artifact is required' };
|
|
127
|
+
if (!(0,_artifact_registry_js__WEBPACK_IMPORTED_MODULE_2__/* .isRegisteredArtifact */ .Jl)(artifact)) return { ok: false, reason: `"${artifact}" is not a registered state artifact` };
|
|
128
|
+
if (!owner || typeof owner !== 'string') return { ok: false, reason: '--owner is required (identity-bound)' };
|
|
129
|
+
if (!reason || typeof reason !== 'string') return { ok: false, reason: '--reason is required (reasoned)' };
|
|
130
|
+
if (expires_at) {
|
|
131
|
+
const t = Date.parse(expires_at);
|
|
132
|
+
if (!Number.isFinite(t)) return { ok: false, reason: 'expires_at must be a parseable date' };
|
|
133
|
+
if (t < Date.now()) return { ok: false, reason: 'expires_at is in the past — a hold that already expired protects nothing' };
|
|
134
|
+
}
|
|
135
|
+
const hold = { artifact, owner, reason, expires_at: expires_at || null, created_at: new Date().toISOString() };
|
|
136
|
+
const holds = _loadRaw(scanRoot);
|
|
137
|
+
holds.push(hold);
|
|
138
|
+
const fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_1__/* .statePath */ .BQ)(scanRoot, LEGAL_HOLD_FILE);
|
|
139
|
+
if (!(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_1__/* .safeWriteState */ .Ep)(fp, JSON.stringify(holds, null, 2) + '\n')) {
|
|
140
|
+
return { ok: false, reason: 'state writes are disabled (--no-state) or this is not a safe state directory' };
|
|
141
|
+
}
|
|
142
|
+
return { ok: true, hold };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Remove every hold on `artifact` (lifting a hold, not letting it expire).
|
|
147
|
+
* Returns the number removed. A no-op (0) if none existed — never an error.
|
|
148
|
+
*/
|
|
149
|
+
function removeLegalHold(scanRoot, artifact) {
|
|
150
|
+
const holds = _loadRaw(scanRoot);
|
|
151
|
+
const remaining = holds.filter(h => !(h && h.artifact === artifact));
|
|
152
|
+
const removedCount = holds.length - remaining.length;
|
|
153
|
+
if (removedCount > 0) {
|
|
154
|
+
const fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_1__/* .statePath */ .BQ)(scanRoot, LEGAL_HOLD_FILE);
|
|
155
|
+
(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_1__/* .safeWriteState */ .Ep)(fp, JSON.stringify(remaining, null, 2) + '\n');
|
|
156
|
+
}
|
|
157
|
+
return removedCount;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
/***/ })
|
|
162
|
+
|
|
163
|
+
};
|
package/dist/178.index.js
CHANGED
|
@@ -13,7 +13,7 @@ export const modules = {
|
|
|
13
13
|
/* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1421);
|
|
14
14
|
/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3024);
|
|
15
15
|
/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6760);
|
|
16
|
-
/* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(
|
|
16
|
+
/* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(7087);
|
|
17
17
|
// Time-travel + counterfactual scanning (v0.68).
|
|
18
18
|
//
|
|
19
19
|
// Two new modes that exploit the pure-input shape of runFullScan:
|
package/dist/238.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,
|
|
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);
|