/** * Post-v5.0.0 additive scanners that the frozen v5.0.0 byte-equal baselines * predate: OST (Output-Style Validation, v5.6 C), OPT (Optimization Lens, v5.7), * and AGT (Agent-Listing Budget, v5.9 B1). The baselines froze the original * scanner set, so this strips these additive scanner entries before comparison, * the same way strip-hotspot-load-pattern.mjs handles B2's additive hotspot * fields. Stripping (rather than re-seeding the frozen snapshots) keeps the * strongest invariant: the ORIGINAL scanners still emit byte-identical output, * and the frozen snapshots stay free of post-v5.0.0 drift (the hotspot triple, * ancestor token counts) that re-seeding would bake in. * * Removing a zero-finding scanner entry only moves `aggregate.scanners_ok` * (OST is always status 'ok' and emits nothing on the deterministic fixture, so * total_findings / counts / risk_score / verdict are unchanged). The strip * decrements scanners_ok to match. * * Mutates in place and returns the payload for chaining inside a normalizer: * return stripAddedScanners(stripHotspotLoadPattern(out)); */ const ADDED_SCANNERS = new Set(['OST', 'OPT', 'AGT']); function stripFromEnvelope(env) { if (!env || typeof env !== 'object' || !Array.isArray(env.scanners)) return; let removedOk = 0; const kept = []; for (const s of env.scanners) { if (ADDED_SCANNERS.has(s.scanner)) { if (s.status === 'ok') removedOk++; continue; } kept.push(s); } env.scanners = kept; if (env.aggregate && typeof env.aggregate.scanners_ok === 'number') { env.aggregate.scanners_ok -= removedOk; } } /** * Strip added (post-v5.0.0) scanner entries from a CLI payload / envelope — * both top-level (`scanners`) and nested (`scannerEnvelope.scanners`). * @template T * @param {T} payload * @returns {T} */ export function stripAddedScanners(payload) { if (!payload || typeof payload !== 'object') return payload; stripFromEnvelope(payload); if (payload.scannerEnvelope) stripFromEnvelope(payload.scannerEnvelope); return payload; } /** * Remove `[OST] …` per-scanner progress lines from a captured stderr scorecard, * so a 14-scanner live run matches the frozen 13-scanner stderr snapshot. * @param {string} text * @returns {string} */ export function stripAddedScannerStderr(text) { if (typeof text !== 'string') return text; return text.replace(/^[ \t]*\[(OST|OPT|AGT)\][^\n]*\n/gm, ''); }