@clear-capabilities/agentic-security-scanner 0.134.0 → 0.136.9
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 +432 -0
- package/bin/agentic-security-audit.js +2 -1
- package/bin/agentic-security-consistency.js +2 -1
- package/bin/agentic-security.js +448 -74
- package/dist/113.index.js +16 -7
- package/dist/117.index.js +3 -1
- package/dist/178.index.js +1 -1
- package/dist/207.index.js +5 -4
- package/dist/220.index.js +5 -3
- package/dist/238.index.js +4 -4
- package/dist/317.index.js +300 -0
- package/dist/384.index.js +1 -1
- package/dist/435.index.js +196 -21
- package/dist/444.index.js +20 -11
- package/dist/449.index.js +8 -1
- package/dist/513.index.js +7 -3
- package/dist/526.index.js +6 -6
- package/dist/637.index.js +1 -1
- package/dist/675.index.js +7 -5
- package/dist/839.index.js +4 -3
- package/dist/905.index.js +1173 -0
- package/dist/agentic-security.mjs +14 -14
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/compliance-frameworks/ccpa.json +32 -0
- package/dist/compliance-frameworks/eu-ai-act.json +51 -0
- package/dist/compliance-frameworks/gdpr.json +45 -0
- package/dist/compliance-frameworks/hipaa-security-rule.json +56 -0
- package/dist/compliance-frameworks/nist-ai-600-1.json +51 -0
- package/dist/compliance-frameworks/nist-csf-2.json +73 -0
- package/dist/compliance-frameworks/nist-privacy-1-1.json +846 -0
- package/dist/compliance-frameworks/owasp-asvs-5.json +79 -0
- package/dist/compliance-frameworks/owasp-llm-top-10.json +69 -0
- package/package.json +24 -12
- package/src/badge.js +2 -1
- package/src/dataflow/CLAUDE.md +10 -4
- package/src/dataflow/builtin-summaries.js +1 -1
- package/src/dataflow/cross-service-taint.js +2 -1
- package/src/dataflow/engine.js +324 -60
- package/src/dataflow/ifds-precise.js +6 -4
- package/src/dataflow/implicit-flow.js +68 -36
- package/src/dataflow/incremental.js +25 -8
- package/src/dataflow/index.js +2 -1
- package/src/dataflow/proven-clean.js +41 -0
- package/src/dataflow/sanitizer-gate.js +35 -9
- package/src/dataflow/sanitizer-proof.js +21 -3
- package/src/dataflow/stub-aware-filter.js +36 -13
- package/src/dataflow/summaries.js +21 -2
- package/src/discovery/CLAUDE.md +10 -0
- package/src/discovery/index.js +175 -3
- package/src/discovery/llm-invoke.js +90 -1
- package/src/discovery/memory.js +163 -0
- package/src/engine.js +247 -50
- package/src/integrations/tickets.js +7 -6
- package/src/ir/CLAUDE.md +4 -1
- package/src/ir/balanced-call.js +55 -0
- package/src/ir/ir-stats.js +1 -1
- package/src/ir/parser-cpp.js +1 -1
- package/src/ir/parser-cs.js +62 -9
- package/src/ir/parser-go.js +29 -11
- package/src/ir/parser-java.js +96 -19
- package/src/ir/parser-js.js +151 -20
- package/src/ir/parser-php.js +44 -9
- package/src/ir/parser-rb.js +37 -7
- package/src/ir/ssa.js +6 -1
- package/src/leaderboard.js +3 -2
- package/src/llm-validator/consistency.js +6 -2
- package/src/llm-validator/index.js +1 -2
- package/src/lsp/server.js +28 -2
- package/src/mcp/CLAUDE.md +9 -2
- package/src/mcp/audit.js +2 -1
- package/src/mcp/redact.js +26 -0
- package/src/mcp/tools.js +159 -17
- package/src/posture/CLAUDE.md +45 -8
- package/src/posture/accuracy-scorecard.js +67 -1
- package/src/posture/agents-memory.js +5 -3
- package/src/posture/aibom.js +12 -8
- package/src/posture/auditor-walkthrough.js +111 -10
- package/src/posture/auth-posture-import.js +5 -4
- package/src/posture/autopilot.js +8 -1
- package/src/posture/calibration-drift.js +11 -5
- package/src/posture/calibration.js +24 -2
- package/src/posture/compliance-frameworks/nist-privacy-1-1.json +846 -0
- package/src/posture/compliance-frameworks/owasp-asvs-5.json +1 -1
- package/src/posture/compliance-policy.js +40 -10
- package/src/posture/confidence.js +44 -10
- package/src/posture/corpus-enroll.js +9 -5
- package/src/posture/corpus-match.js +19 -0
- package/src/posture/csharp-analysis.js +62 -3
- package/src/posture/custom-rules.js +7 -5
- package/src/posture/cve-alert-daemon.js +6 -5
- package/src/posture/dep-add-guard.js +2 -1
- package/src/posture/deploy-platform.js +4 -1
- package/src/posture/deterministic.js +3 -2
- package/src/posture/drift.js +7 -1
- package/src/posture/epss.js +13 -1
- package/src/posture/evidence-bundle.js +276 -0
- package/src/posture/exploitability-probability.js +15 -2
- package/src/posture/falsification.js +23 -2
- package/src/posture/feature-flags.js +3 -2
- package/src/posture/findings-memory.js +3 -3
- package/src/posture/fix-history.js +5 -2
- package/src/posture/fix-metrics.js +5 -5
- package/src/posture/fix-plan.js +2 -1
- package/src/posture/fix-verify-loop.js +10 -1
- package/src/posture/grader-calibration.js +3 -4
- package/src/posture/iac-reachability.js +14 -8
- package/src/posture/integrity.js +25 -7
- package/src/posture/intent-context.js +2 -1
- package/src/posture/learning.js +4 -3
- package/src/posture/license-attributions.js +5 -7
- package/src/posture/license-graph.js +2 -1
- package/src/posture/license-policy.js +2 -1
- package/src/posture/model-rescan.js +69 -3
- package/src/posture/mttr.js +5 -0
- package/src/posture/network-policy-import.js +3 -2
- package/src/posture/poc-inprocess.js +27 -8
- package/src/posture/pqc-migration-plan.js +7 -5
- package/src/posture/pr-augment.js +8 -5
- package/src/posture/privacy-framework.js +262 -0
- package/src/posture/regression-test-gen.js +23 -8
- package/src/posture/reverse-blast-radius.js +5 -1
- package/src/posture/risk-dollars.js +20 -3
- package/src/posture/router.js +5 -4
- package/src/posture/ruleset-version.js +2 -2
- package/src/posture/runtime-correlation.js +2 -1
- package/src/posture/sbom-diff.js +12 -3
- package/src/posture/sca-policy.js +7 -4
- package/src/posture/scan-checkpoint.js +15 -0
- package/src/posture/secret-history.js +20 -11
- package/src/posture/security-trend.js +7 -1
- package/src/posture/stack-playbook.js +22 -1
- package/src/posture/state-dir.js +34 -0
- package/src/posture/telemetry-ingest.js +4 -3
- package/src/posture/threat-model-auto.js +4 -1
- package/src/posture/threat-model-grounding.js +13 -3
- package/src/posture/time-to-fix.js +3 -2
- package/src/posture/triage-memory.js +3 -2
- package/src/posture/validator-metrics.js +10 -3
- package/src/posture/verifier.js +32 -57
- package/src/posture/waf-ingest.js +6 -5
- package/src/posture/watch-mode.js +4 -3
- package/src/report/index.js +183 -14
- package/src/runScan.js +1 -1
- package/src/sast/_comment-strip.js +15 -4
- package/src/sast/_secret-entropy.js +1 -1
- package/src/sast/authz.js +6 -4
- package/src/sast/bench-shape/index.js +2 -7
- package/src/sast/claude-md-prompt-injection.js +14 -3
- package/src/sast/cloud-iam.js +60 -7
- package/src/sast/code-injection-multilang.js +29 -0
- package/src/sast/cpp-bench-extras.js +1 -1
- package/src/sast/csrf.js +7 -5
- package/src/sast/env-hygiene.js +5 -2
- package/src/sast/iac-terraform.js +25 -0
- package/src/sast/java-bench-extras.js +1 -1
- package/src/sast/java-constant-fold.js +5 -5
- package/src/sast/llm-owasp.js +4 -2
- package/src/sast/mcp-audit.js +7 -0
- package/src/sast/pipeline.js +8 -0
- package/src/sast/prompt-template.js +8 -6
- package/src/sast/prototype-pollution.js +6 -2
- package/src/sast/redos-nfa.js +6 -6
- package/src/sast/secret-concat.js +13 -2
- package/src/sast/ssrf-cloud-metadata.js +6 -3
- package/src/sast/xss-reflected-multilang.js +1 -1
- package/src/sast/xxe.js +1 -1
- package/src/sca/CLAUDE.md +3 -4
- package/src/sca/container.js +35 -3
- package/src/sca/dep-confusion.js +9 -1
- package/src/sca/sarif-ingest.js +0 -187
package/dist/435.index.js
CHANGED
|
@@ -124,6 +124,32 @@ const PATTERNS = [
|
|
|
124
124
|
[/rk_(?:live|test)_[A-Za-z0-9]{20,}/g, 'stripe-restricted-key'],
|
|
125
125
|
[/SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}/g, 'sendgrid-key'],
|
|
126
126
|
[/AIza[0-9A-Za-z_-]{35}/g, 'google-api-key'],
|
|
127
|
+
// Stage 4 correctness audit (coverage breadth, AI security): this list
|
|
128
|
+
// only covered a small subset of what the scanner's OWN credential
|
|
129
|
+
// detector (engine.js's CREDENTIAL_PATTERNS, 40+ provider shapes) finds
|
|
130
|
+
// — a Shopify/Telegram/Twilio/Discord-webhook/Square/Google-OAuth/JDBC
|
|
131
|
+
// secret detected and reported by a scan reached explain_finding's
|
|
132
|
+
// output completely unredacted, because none of those shapes were in
|
|
133
|
+
// THIS separate, narrower list. Reusing the same regex bodies as
|
|
134
|
+
// engine.js's CREDENTIAL_PATTERNS for the shapes verified to leak
|
|
135
|
+
// (rather than importing engine.js itself, which would pull its entire
|
|
136
|
+
// multi-thousand-line module graph into the MCP server's dependency
|
|
137
|
+
// surface for a handful of consts).
|
|
138
|
+
[/ya29\.[0-9A-Za-z_-]{20,}/g, 'google-oauth-token'],
|
|
139
|
+
[/shp(?:at|ss|ca|pa)_[a-fA-F0-9]{32}/g, 'shopify-token'],
|
|
140
|
+
[/(?<![0-9])[0-9]{8,10}:AA[0-9A-Za-z_-]{33}(?![A-Za-z0-9_])/g, 'telegram-bot-token'],
|
|
141
|
+
[/twilio.{0,20}SK[0-9a-fA-F]{32}/gi, 'twilio-api-key'],
|
|
142
|
+
[/sq0atp-[0-9A-Za-z_-]{22}/g, 'square-access-token'],
|
|
143
|
+
[/sq0csp-[0-9A-Za-z_-]{43}/g, 'square-oauth-secret'],
|
|
144
|
+
[/access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}/g, 'paypal-braintree-token'],
|
|
145
|
+
[/https:\/\/(?:discordapp|discord)\.com\/api\/webhooks\/[0-9]+\/[A-Za-z0-9_-]+/g, 'discord-webhook'],
|
|
146
|
+
[/https:\/\/hooks\.slack\.com\/services\/T[a-zA-Z0-9_]{8}\/B[a-zA-Z0-9_]{8,12}\/[a-zA-Z0-9_]{24}/g, 'slack-webhook'],
|
|
147
|
+
[/https:\/\/outlook\.office\.com\/webhook\/[A-Za-z0-9\-@]+\/IncomingWebhook\/[A-Za-z0-9-]+\/[A-Za-z0-9-]+/g, 'teams-webhook'],
|
|
148
|
+
[/https:\/\/(?:www\.)?hooks\.zapier\.com\/hooks\/catch\/[A-Za-z0-9]+\/[A-Za-z0-9]+\//g, 'zapier-webhook'],
|
|
149
|
+
// JDBC connection string carrying a password: only redact when password
|
|
150
|
+
// evidence is actually on the line (matches engine.js's own ctx gate),
|
|
151
|
+
// so a credential-free JDBC URL in docs isn't needlessly mangled.
|
|
152
|
+
[/jdbc:[a-z:]+:\/\/[A-Za-z0-9.\-_:;=/@?,&]*(?:@|password=|passwd=|pwd=)[A-Za-z0-9.\-_:;=/@?,&]*/gi, 'jdbc-connection-string'],
|
|
127
153
|
// JWT — three dot-separated b64url segments starting with eyJ
|
|
128
154
|
[/eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g, 'jwt'],
|
|
129
155
|
// PEM-encoded private keys
|
|
@@ -171,6 +197,10 @@ function redactArgsBlob(s) {
|
|
|
171
197
|
return redactString(s);
|
|
172
198
|
}
|
|
173
199
|
|
|
200
|
+
// EXTERNAL MODULE: ./src/report/index.js + 2 modules
|
|
201
|
+
var report = __webpack_require__(3550);
|
|
202
|
+
// EXTERNAL MODULE: ./src/posture/state-dir.js
|
|
203
|
+
var state_dir = __webpack_require__(1174);
|
|
174
204
|
;// CONCATENATED MODULE: ./src/posture/agents-memory.js
|
|
175
205
|
// AGENTS.md — writable continual-learning memory (harness-anatomy #2).
|
|
176
206
|
//
|
|
@@ -203,6 +233,7 @@ function redactArgsBlob(s) {
|
|
|
203
233
|
|
|
204
234
|
|
|
205
235
|
|
|
236
|
+
|
|
206
237
|
const MEMORY_FILE = '.agentic-security/AGENTS.md';
|
|
207
238
|
const ARCHIVE_FILE = '.agentic-security/AGENTS.md.archive';
|
|
208
239
|
const MAX_BYTES = 20 * 1024;
|
|
@@ -210,8 +241,8 @@ const MAX_ENTRY_BYTES = 2 * 1024;
|
|
|
210
241
|
const ARCHIVE_MAX_BYTES = 200 * 1024;
|
|
211
242
|
const HEADER = '# AGENTS.md\n\nAgent-authored continual-learning notes. Each entry: timestamp + agent name + one short paragraph. New entries appended at the bottom; oldest entries rotate to AGENTS.md.archive when this file exceeds 20 KB.\n\n';
|
|
212
243
|
|
|
213
|
-
function _resolve(scanRoot) { return
|
|
214
|
-
function _archivePath(scanRoot) { return
|
|
244
|
+
function _resolve(scanRoot) { return (0,state_dir/* statePath */.BQ)(scanRoot, 'AGENTS.md'); }
|
|
245
|
+
function _archivePath(scanRoot) { return (0,state_dir/* statePath */.BQ)(scanRoot, 'AGENTS.md.archive'); }
|
|
215
246
|
|
|
216
247
|
function readAgentsMemory(scanRoot) {
|
|
217
248
|
const fp = _resolve(scanRoot);
|
|
@@ -239,7 +270,8 @@ function appendAgentsMemory(scanRoot, { agent, body }) {
|
|
|
239
270
|
const entry = `\n## ${ts} agent: ${agent}\n\n${snippet}\n`;
|
|
240
271
|
try {
|
|
241
272
|
const fp = _resolve(scanRoot);
|
|
242
|
-
|
|
273
|
+
if (!(0,state_dir.stateWritesEnabled)()) return;
|
|
274
|
+
external_node_fs_.mkdirSync(external_node_path_.dirname(fp), { recursive: true });
|
|
243
275
|
if (!external_node_fs_.existsSync(fp)) external_node_fs_.writeFileSync(fp, HEADER);
|
|
244
276
|
external_node_fs_.appendFileSync(fp, entry);
|
|
245
277
|
_maybeRotate(scanRoot);
|
|
@@ -466,6 +498,7 @@ const cve_lookup_internals = { CACHE_DIR, CVE_RE, _stalenessTier };
|
|
|
466
498
|
|
|
467
499
|
|
|
468
500
|
|
|
501
|
+
|
|
469
502
|
// Lazy-loaded: these transitively pull in npm packages (@babel/core and
|
|
470
503
|
// friends) that aren't available in the plugin-cache install path
|
|
471
504
|
// (no node_modules). Deferring keeps the MCP server bootable everywhere;
|
|
@@ -596,12 +629,17 @@ function _validateScratchpadPath(relPath) {
|
|
|
596
629
|
return { ok: true, agent, session, fileParts };
|
|
597
630
|
}
|
|
598
631
|
|
|
632
|
+
// Routes through the same lstat+realpath confinement every other write/
|
|
633
|
+
// path-taking tool uses (OWASP MCP05) — a lexical prefix/charset check
|
|
634
|
+
// alone doesn't stop a pre-planted symlink at any path component from
|
|
635
|
+
// relocating the write/read outside the session root. Throws on escape;
|
|
636
|
+
// callers must catch (see append_scratchpad / read_scratchpad).
|
|
599
637
|
function _scratchpadAbs(sessionRoot, relPath) {
|
|
600
|
-
return
|
|
638
|
+
return _confine(sessionRoot, relPath.replace(/\\/g, '/'), 'scratchpad path');
|
|
601
639
|
}
|
|
602
640
|
|
|
603
641
|
function _scratchpadTotalBytes(sessionRoot) {
|
|
604
|
-
const base =
|
|
642
|
+
const base = (0,state_dir/* statePath */.BQ)(sessionRoot, 'agent-scratchpad');
|
|
605
643
|
if (!external_node_fs_.existsSync(base)) return 0;
|
|
606
644
|
let total = 0;
|
|
607
645
|
const walk = (dir) => {
|
|
@@ -666,8 +704,8 @@ function _confine(sessionRoot, candidate, label) {
|
|
|
666
704
|
}
|
|
667
705
|
|
|
668
706
|
function _readLastScanVerified(sessionRoot, { allowUnsigned = false } = {}) {
|
|
669
|
-
const
|
|
670
|
-
const scanFile = external_node_path_.join(
|
|
707
|
+
const stateDirPath = (0,state_dir/* stateDir */.Pn)(sessionRoot);
|
|
708
|
+
const scanFile = external_node_path_.join(stateDirPath, 'last-scan.json');
|
|
671
709
|
const sigFile = scanFile + '.sig';
|
|
672
710
|
if (!external_node_fs_.existsSync(scanFile)) return { scan: null, status: 'missing' };
|
|
673
711
|
const body = external_node_fs_.readFileSync(scanFile, 'utf8');
|
|
@@ -781,12 +819,22 @@ const scan_diff = {
|
|
|
781
819
|
const wantSet = new Set(Object.keys(fileContents));
|
|
782
820
|
const sevRank = { info: 0, low: 1, medium: 2, high: 3, critical: 4 };
|
|
783
821
|
const min = sevRank[severity] ?? 0;
|
|
784
|
-
|
|
822
|
+
// Stage 6 correctness audit: this only ever read result.scan.findings
|
|
823
|
+
// (the SAST channel) — scan.secrets and scan.logicVulns are separate
|
|
824
|
+
// arrays on the raw runScan() result (report/index.js's normalizeFindings
|
|
825
|
+
// is what merges all four channels, and that merge hasn't run yet here).
|
|
826
|
+
// A file containing a bare hardcoded credential reported findingCount: 0
|
|
827
|
+
// through a tool whose own description promises "Use BEFORE writing a
|
|
828
|
+
// Write/Edit to disk so the agent can self-correct". Also reused
|
|
829
|
+
// _remediationOf so a fix-string detector (the majority of engine.js's
|
|
830
|
+
// own, ~127 call sites) doesn't silently report an empty `description`
|
|
831
|
+
// the way reading only `.remediation` did.
|
|
832
|
+
const findings = [...(result.scan.findings || []), ...(result.scan.secrets || []), ...(result.scan.logicVulns || [])]
|
|
785
833
|
.filter(f => wantSet.has(String(f.file || '').replace(/\\/g, '/')) && (sevRank[f.severity] ?? 0) >= min)
|
|
786
834
|
.map(f => redactFinding({
|
|
787
835
|
id: f.id, severity: f.severity, file: f.file, line: f.line,
|
|
788
836
|
title: f.title || f.vuln, cwe: f.cwe,
|
|
789
|
-
description: f.description, remediation: f
|
|
837
|
+
description: f.description, remediation: (0,report/* _remediationOf */.uV)(f),
|
|
790
838
|
}));
|
|
791
839
|
// Harness-anatomy #1: offload when the result exceeds OFFLOAD_THRESHOLD.
|
|
792
840
|
// The agent gets a head+tail preview plus a path it can page through;
|
|
@@ -949,10 +997,40 @@ const apply_fix = {
|
|
|
949
997
|
additionalProperties: { type: 'string', maxLength: 500_000 },
|
|
950
998
|
minProperties: 1, maxProperties: 8,
|
|
951
999
|
},
|
|
1000
|
+
// Stage 6 correctness audit: same gap and same fix as verify_fix — the
|
|
1001
|
+
// honesty gate is reachable but was never wired to any real caller.
|
|
1002
|
+
// Here it's stronger than advisory: the inline re-verify below already
|
|
1003
|
+
// gates the WRITE on `verdict.ok`, and verifyFixCore's own `ok`
|
|
1004
|
+
// formula already folds in `honesty.ok` when fixMeta is supplied — so
|
|
1005
|
+
// passing it through here makes a dishonest fixMeta (hand-wave
|
|
1006
|
+
// residual, uncited false-positive verdict) block the write itself,
|
|
1007
|
+
// not just report a verdict.
|
|
1008
|
+
fixMeta: {
|
|
1009
|
+
type: 'object',
|
|
1010
|
+
additionalProperties: false,
|
|
1011
|
+
properties: {
|
|
1012
|
+
residual: { type: 'string', maxLength: 2000 },
|
|
1013
|
+
verdict: { type: 'string', maxLength: 64 },
|
|
1014
|
+
evidence: { type: 'array', items: { type: 'string', maxLength: 500 }, maxItems: 20 },
|
|
1015
|
+
signals: {
|
|
1016
|
+
type: 'object',
|
|
1017
|
+
additionalProperties: false,
|
|
1018
|
+
properties: {
|
|
1019
|
+
sinkSignatureChanged: { type: 'boolean' },
|
|
1020
|
+
allCallersRouted: { type: 'boolean' },
|
|
1021
|
+
testDiscriminates: { type: 'boolean' },
|
|
1022
|
+
rateLimitOnly: { type: 'boolean' },
|
|
1023
|
+
docsOnly: { type: 'boolean' },
|
|
1024
|
+
logOnlyNoReject: { type: 'boolean' },
|
|
1025
|
+
partialSanitization: { type: 'boolean' },
|
|
1026
|
+
},
|
|
1027
|
+
},
|
|
1028
|
+
},
|
|
1029
|
+
},
|
|
952
1030
|
},
|
|
953
1031
|
required: ['finding_id', 'confirm'],
|
|
954
1032
|
},
|
|
955
|
-
async handler({ finding_id, confirm, dry_run = false, patch = null }, ctx) {
|
|
1033
|
+
async handler({ finding_id, confirm, dry_run = false, patch = null, fixMeta = null }, ctx) {
|
|
956
1034
|
if (confirm !== true) {
|
|
957
1035
|
return { _meta: META, applied: false, reason: 'apply_fix requires confirm: true.' };
|
|
958
1036
|
}
|
|
@@ -1008,6 +1086,7 @@ const apply_fix = {
|
|
|
1008
1086
|
scanRoot: ctx.sessionRoot,
|
|
1009
1087
|
originalFindingStableId: f.stableId,
|
|
1010
1088
|
files: _files,
|
|
1089
|
+
fixMeta,
|
|
1011
1090
|
});
|
|
1012
1091
|
}
|
|
1013
1092
|
} catch (e) {
|
|
@@ -1017,7 +1096,7 @@ const apply_fix = {
|
|
|
1017
1096
|
return {
|
|
1018
1097
|
_meta: META, applied: false,
|
|
1019
1098
|
reason: `patch rejected by verifier: ${verdict.summary || verdict.rescan?.reason || 'did not verify'}`,
|
|
1020
|
-
verify: { rescan: verdict.rescan, lint: { runner: verdict.lint?.runner, ok: verdict.lint?.ok } },
|
|
1099
|
+
verify: { rescan: verdict.rescan, lint: { runner: verdict.lint?.runner, ok: verdict.lint?.ok }, honesty: verdict.honesty || null },
|
|
1021
1100
|
};
|
|
1022
1101
|
}
|
|
1023
1102
|
if (dry_run) {
|
|
@@ -1029,7 +1108,7 @@ const apply_fix = {
|
|
|
1029
1108
|
const originalContent = external_node_fs_.existsSync(v.abs) ? await promises_.readFile(v.abs, 'utf8') : '';
|
|
1030
1109
|
const entry = await (0,fix_history/* applyFix */.oM)({
|
|
1031
1110
|
scanRoot: ctx.sessionRoot, file: rel, originalContent, newContent: v.content,
|
|
1032
|
-
findingId: f.id, stableId: f.stableId, ruleId: f.
|
|
1111
|
+
findingId: f.id, stableId: f.stableId, ruleId: f.ruleId || f.cwe || f.family || null, vuln: f.vuln || f.title || null,
|
|
1033
1112
|
});
|
|
1034
1113
|
written.push({ file: rel, historyId: entry.id, backupPath: entry.backupPath });
|
|
1035
1114
|
}
|
|
@@ -1087,7 +1166,7 @@ const apply_fix = {
|
|
|
1087
1166
|
newContent: f.fix.replacement,
|
|
1088
1167
|
findingId: f.id,
|
|
1089
1168
|
stableId: f.stableId || null, // premortem 4R-8
|
|
1090
|
-
ruleId: f.
|
|
1169
|
+
ruleId: f.ruleId || f.cwe || f.family || null,
|
|
1091
1170
|
vuln: f.vuln || f.title || null,
|
|
1092
1171
|
});
|
|
1093
1172
|
} catch (e) {
|
|
@@ -1124,7 +1203,7 @@ const apply_fix = {
|
|
|
1124
1203
|
// proceed with apply_fix.
|
|
1125
1204
|
const verify_fix = {
|
|
1126
1205
|
name: 'verify_fix',
|
|
1127
|
-
description: 'Verify a proposed patch before applying. Re-scans the patched files in memory
|
|
1206
|
+
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.',
|
|
1128
1207
|
inputSchema: {
|
|
1129
1208
|
type: 'object',
|
|
1130
1209
|
additionalProperties: false,
|
|
@@ -1136,10 +1215,41 @@ const verify_fix = {
|
|
|
1136
1215
|
minProperties: 1,
|
|
1137
1216
|
maxProperties: 8,
|
|
1138
1217
|
},
|
|
1218
|
+
// Stage 6 correctness audit: posture/fix-honesty-gate.js's deterministic
|
|
1219
|
+
// honesty checks (vague-assurance residual prose, unbacked false-
|
|
1220
|
+
// positive verdicts, tier/residual consistency) were fully built and
|
|
1221
|
+
// fix-verify.js already consulted them when given a `fixMeta` — but
|
|
1222
|
+
// this schema never had a `fixMeta` property, so no call through the
|
|
1223
|
+
// MCP surface could ever supply one. The gate can only run against
|
|
1224
|
+
// claims the AGENT self-reports (residual risk, verdict, evidence,
|
|
1225
|
+
// completeness signals) — nothing here is server-computable — so
|
|
1226
|
+
// fixing this meant exposing the property, not inventing a lookup.
|
|
1227
|
+
fixMeta: {
|
|
1228
|
+
type: 'object',
|
|
1229
|
+
additionalProperties: false,
|
|
1230
|
+
properties: {
|
|
1231
|
+
residual: { type: 'string', maxLength: 2000 },
|
|
1232
|
+
verdict: { type: 'string', maxLength: 64 },
|
|
1233
|
+
evidence: { type: 'array', items: { type: 'string', maxLength: 500 }, maxItems: 20 },
|
|
1234
|
+
signals: {
|
|
1235
|
+
type: 'object',
|
|
1236
|
+
additionalProperties: false,
|
|
1237
|
+
properties: {
|
|
1238
|
+
sinkSignatureChanged: { type: 'boolean' },
|
|
1239
|
+
allCallersRouted: { type: 'boolean' },
|
|
1240
|
+
testDiscriminates: { type: 'boolean' },
|
|
1241
|
+
rateLimitOnly: { type: 'boolean' },
|
|
1242
|
+
docsOnly: { type: 'boolean' },
|
|
1243
|
+
logOnlyNoReject: { type: 'boolean' },
|
|
1244
|
+
partialSanitization: { type: 'boolean' },
|
|
1245
|
+
},
|
|
1246
|
+
},
|
|
1247
|
+
},
|
|
1248
|
+
},
|
|
1139
1249
|
},
|
|
1140
1250
|
required: ['stable_id', 'files'],
|
|
1141
1251
|
},
|
|
1142
|
-
async handler({ stable_id, files }, ctx) {
|
|
1252
|
+
async handler({ stable_id, files, fixMeta }, ctx) {
|
|
1143
1253
|
// Confine every file path before passing to the verifier.
|
|
1144
1254
|
const confined = {};
|
|
1145
1255
|
for (const [relPath, content] of Object.entries(files || {})) {
|
|
@@ -1151,17 +1261,48 @@ const verify_fix = {
|
|
|
1151
1261
|
confined[relPath] = String(content);
|
|
1152
1262
|
}
|
|
1153
1263
|
try {
|
|
1264
|
+
// The PoC-re-check leg (verifyFixCore's `pocLeg`) needs a `poc` param
|
|
1265
|
+
// to do anything — until now nothing supplied one, so it always
|
|
1266
|
+
// reported {status:'not-requested'} through this surface (see
|
|
1267
|
+
// posture/CLAUDE.md's disclosure). Rather than widening inputSchema
|
|
1268
|
+
// to make the CALLER pass PoC data back, look it up server-side: the
|
|
1269
|
+
// scan pipeline already attaches an HTTP-shaped f.poc to matching
|
|
1270
|
+
// findings by default (engine.js's annotatePocs), and last-scan.json
|
|
1271
|
+
// already carries it under the same stableId this handler receives.
|
|
1272
|
+
// Best-effort: a missing/unsigned/tampered scan just means no PoC is
|
|
1273
|
+
// available to re-check, not a verify_fix failure — the rescan/lint/
|
|
1274
|
+
// tests legs below are independent of this and still apply.
|
|
1275
|
+
let poc = null;
|
|
1276
|
+
try {
|
|
1277
|
+
const { scan: lastScan } = _readLastScanVerified(ctx.sessionRoot, { allowUnsigned: true });
|
|
1278
|
+
const orig = lastScan && (lastScan.findings || []).find(f => f.stableId === stable_id);
|
|
1279
|
+
if (orig && orig.poc && orig.poc.code) poc = { ...orig.poc, finding: orig };
|
|
1280
|
+
} catch { /* best-effort lookup; poc stays null */ }
|
|
1281
|
+
|
|
1154
1282
|
const verifyFixCore = await getVerifyFixCore();
|
|
1155
1283
|
const r = await verifyFixCore({
|
|
1156
1284
|
scanRoot: ctx.sessionRoot,
|
|
1157
1285
|
originalFindingStableId: stable_id,
|
|
1158
1286
|
files: confined,
|
|
1287
|
+
poc,
|
|
1288
|
+
fixMeta,
|
|
1159
1289
|
});
|
|
1160
1290
|
return {
|
|
1161
1291
|
_meta: META,
|
|
1162
1292
|
ok: r.ok,
|
|
1163
1293
|
rescan: { ok: r.rescan.ok, reason: r.rescan.reason, introduced: r.rescan.introduced || [] },
|
|
1164
1294
|
lint: { runner: r.lint.runner, ok: r.lint.ok, skipped: r.lint.skipped || false, output: redactString(r.lint.output || '').slice(0, 1500) },
|
|
1295
|
+
// verifyFix computes five legs, not two — tests/honesty/poc were
|
|
1296
|
+
// being silently dropped here, leaving an agent with no structured
|
|
1297
|
+
// way to see WHY verification failed when the failure was in one
|
|
1298
|
+
// of those three (only the free-text summary carried it).
|
|
1299
|
+
// test-runner.js's runProjectTests never returns raw stdout/stderr,
|
|
1300
|
+
// so no redaction is needed there; honesty.violations are static,
|
|
1301
|
+
// code-generated strings; poc.reason is redacted defensively since
|
|
1302
|
+
// it can echo proof-harness detail derived from scanned source.
|
|
1303
|
+
tests: r.tests,
|
|
1304
|
+
honesty: r.honesty,
|
|
1305
|
+
poc: r.poc ? { ...r.poc, reason: r.poc.reason ? redactString(r.poc.reason) : r.poc.reason } : r.poc,
|
|
1165
1306
|
summary: r.summary,
|
|
1166
1307
|
};
|
|
1167
1308
|
} catch (e) {
|
|
@@ -1245,7 +1386,13 @@ const synthesize_fix = {
|
|
|
1245
1386
|
regression_test: f.regression_test || null,
|
|
1246
1387
|
remediation: typeof fix.description === 'string' ? fix.description : (typeof fix === 'string' ? fix : null),
|
|
1247
1388
|
patchBounds: { touchedFiles, locDelta, oversized },
|
|
1248
|
-
|
|
1389
|
+
// oversized can only be true when hasReplacement is true (locDelta is
|
|
1390
|
+
// only computed in that branch, and touchedFiles never varies) — a
|
|
1391
|
+
// `!hasReplacement` conjunct here was a structural contradiction that
|
|
1392
|
+
// made this permanently false. The correct signal: the stored
|
|
1393
|
+
// replacement itself is too big to trust auto-applying, and there's
|
|
1394
|
+
// no safer deterministic alternative.
|
|
1395
|
+
recommendsFixPlan: oversized && !autofix,
|
|
1249
1396
|
};
|
|
1250
1397
|
},
|
|
1251
1398
|
};
|
|
@@ -1377,7 +1524,9 @@ const append_scratchpad = {
|
|
|
1377
1524
|
async handler({ path: relPath, content }, ctx) {
|
|
1378
1525
|
const v = _validateScratchpadPath(relPath);
|
|
1379
1526
|
if (!v.ok) return { _meta: META, ok: false, reason: v.reason };
|
|
1380
|
-
|
|
1527
|
+
let abs;
|
|
1528
|
+
try { abs = _scratchpadAbs(ctx.sessionRoot, relPath); }
|
|
1529
|
+
catch (e) { return { _meta: META, ok: false, reason: `path-escape refused: ${e.message}` }; }
|
|
1381
1530
|
const total = _scratchpadTotalBytes(ctx.sessionRoot);
|
|
1382
1531
|
if (total + content.length > SCRATCHPAD_MAX_TOTAL_BYTES) {
|
|
1383
1532
|
return {
|
|
@@ -1423,7 +1572,9 @@ const read_scratchpad = {
|
|
|
1423
1572
|
async handler({ path: relPath, offset, limit }, ctx) {
|
|
1424
1573
|
const v = _validateScratchpadPath(relPath);
|
|
1425
1574
|
if (!v.ok) return { _meta: META, ok: false, reason: v.reason };
|
|
1426
|
-
|
|
1575
|
+
let abs;
|
|
1576
|
+
try { abs = _scratchpadAbs(ctx.sessionRoot, relPath); }
|
|
1577
|
+
catch (e) { return { _meta: META, ok: false, reason: `path-escape refused: ${e.message}` }; }
|
|
1427
1578
|
if (!external_node_fs_.existsSync(abs)) return { _meta: META, ok: false, reason: 'not-found' };
|
|
1428
1579
|
let stat;
|
|
1429
1580
|
try { stat = external_node_fs_.statSync(abs); } catch (e) { return { _meta: META, ok: false, reason: `stat-failed: ${e.message}` }; }
|
|
@@ -1457,6 +1608,7 @@ const read_scratchpad = {
|
|
|
1457
1608
|
|
|
1458
1609
|
|
|
1459
1610
|
|
|
1611
|
+
|
|
1460
1612
|
const append_agents_memory = {
|
|
1461
1613
|
name: 'append_agents_memory',
|
|
1462
1614
|
description: 'Append a short narrative entry to AGENTS.md — agent-authored continual-learning notes. Use at session end to record "what worked / what didn\'t / what I\'d try differently next time" so the next agent can pick up the lesson. Bounded: 2 KB per entry, 20 KB total before rotation to AGENTS.md.archive. Use sparingly — narrative, not structured data.',
|
|
@@ -1518,7 +1670,20 @@ const query_triage_memory = {
|
|
|
1518
1670
|
},
|
|
1519
1671
|
async handler({ query }, ctx) {
|
|
1520
1672
|
const { queryMemory } = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 1905));
|
|
1521
|
-
const
|
|
1673
|
+
const raw = queryMemory(ctx.sessionRoot, query || '');
|
|
1674
|
+
// Stage 6 correctness audit: this returned queryMemory's output
|
|
1675
|
+
// verbatim, with no redaction pass — every other tool that echoes
|
|
1676
|
+
// scanned-source-derived text redacts it (mcp/CLAUDE.md's "Adding a new
|
|
1677
|
+
// tool" step 3). Round-trip through redactString the same way
|
|
1678
|
+
// redactFinding already does for its own opaque `.trace` field: results
|
|
1679
|
+
// here mix shapes (a triage decision's free-text `reason`, a finding's
|
|
1680
|
+
// `vuln`/`family`/file path), so scrubbing the whole serialized
|
|
1681
|
+
// structure catches secret-shaped substrings regardless of which field
|
|
1682
|
+
// they landed in, rather than hardcoding a field allowlist that could
|
|
1683
|
+
// miss one.
|
|
1684
|
+
let results;
|
|
1685
|
+
try { results = JSON.parse(redactString(JSON.stringify(raw))); }
|
|
1686
|
+
catch { results = raw; }
|
|
1522
1687
|
return {
|
|
1523
1688
|
_meta: META,
|
|
1524
1689
|
count: results.length,
|
|
@@ -1546,7 +1711,16 @@ const query_findings_memory = {
|
|
|
1546
1711
|
},
|
|
1547
1712
|
async handler({ query }, ctx) {
|
|
1548
1713
|
const { queryFindingsMemory } = await __webpack_require__.e(/* import() */ 839).then(__webpack_require__.bind(__webpack_require__, 3839));
|
|
1549
|
-
|
|
1714
|
+
const raw = queryFindingsMemory(ctx.sessionRoot, query || '');
|
|
1715
|
+
// Stage 6 correctness audit — same redaction gap and same fix as
|
|
1716
|
+
// query_triage_memory just above: this mixes four differently-shaped
|
|
1717
|
+
// result kinds (finding / triage / history / AGENTS.md text), so a
|
|
1718
|
+
// whole-structure redactString round-trip is applied rather than a
|
|
1719
|
+
// per-field allowlist that could miss one of the four shapes.
|
|
1720
|
+
let body;
|
|
1721
|
+
try { body = JSON.parse(redactString(JSON.stringify(raw))); }
|
|
1722
|
+
catch { body = raw; }
|
|
1723
|
+
return { _meta: META, ...body };
|
|
1550
1724
|
},
|
|
1551
1725
|
};
|
|
1552
1726
|
|
|
@@ -1754,6 +1928,7 @@ function validate(schema, value, path = 'arguments') {
|
|
|
1754
1928
|
|
|
1755
1929
|
|
|
1756
1930
|
|
|
1931
|
+
|
|
1757
1932
|
const MAX_ARG_BYTES = 1024;
|
|
1758
1933
|
const GENESIS = 'GENESIS';
|
|
1759
1934
|
const REMOTE_TIMEOUT_MS = 1500;
|
|
@@ -1814,7 +1989,7 @@ function auditCall({ sessionRoot, tool, args, outcome, reason }) {
|
|
|
1814
1989
|
let hasMarker = false;
|
|
1815
1990
|
for (const m of MARKERS) { try { if (external_node_fs_.existsSync(external_node_path_.join(sessionRoot, m))) { hasMarker = true; break; } } catch {} }
|
|
1816
1991
|
if (!hasMarker) return;
|
|
1817
|
-
const dir =
|
|
1992
|
+
const dir = (0,state_dir/* stateDir */.Pn)(sessionRoot);
|
|
1818
1993
|
external_node_fs_.mkdirSync(dir, { recursive: true });
|
|
1819
1994
|
const logFile = external_node_path_.join(dir, 'mcp-audit.log');
|
|
1820
1995
|
const entry = {
|
package/dist/444.index.js
CHANGED
|
@@ -43,17 +43,26 @@ function scanHistoryDiff(diffText, commit, detectFn) {
|
|
|
43
43
|
if (!added.trim()) return [];
|
|
44
44
|
let findings = [];
|
|
45
45
|
try { findings = detectFn(`git-history@${commit}`, added) || []; } catch { return []; }
|
|
46
|
-
return findings.map((f) =>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
return findings.map((f) => {
|
|
47
|
+
const remediation = 'Rotate the credential now, then purge it from history (git filter-repo / BFG) and move it to a secrets manager. Removing it from HEAD alone is insufficient.';
|
|
48
|
+
return {
|
|
49
|
+
...f,
|
|
50
|
+
id: `secret-history:${commit}:${f.id || f.vuln || 'secret'}`,
|
|
51
|
+
file: `git-history@${commit}`,
|
|
52
|
+
line: 0,
|
|
53
|
+
commit,
|
|
54
|
+
_historical: true,
|
|
55
|
+
vuln: `${f.vuln || 'Hardcoded Secret'} (in git history)`,
|
|
56
|
+
description: `${f.description || 'A credential was committed.'} Found in commit ${commit}; even if removed from HEAD it remains recoverable from git and must be rotated.`,
|
|
57
|
+
remediation,
|
|
58
|
+
// report/index.js's _remediationOf checks `.fix` before `.remediation`
|
|
59
|
+
// — the underlying detector already set `.fix` to a generic "remove
|
|
60
|
+
// the line" string, which would otherwise silently shadow this
|
|
61
|
+
// history-specific instruction ("removing it from HEAD alone is
|
|
62
|
+
// insufficient") in every report format.
|
|
63
|
+
fix: remediation,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
/**
|
package/dist/449.index.js
CHANGED
|
@@ -7,10 +7,12 @@ export const modules = {
|
|
|
7
7
|
|
|
8
8
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
9
9
|
/* harmony export */ buildBaselineMap: () => (/* binding */ buildBaselineMap),
|
|
10
|
+
/* harmony export */ computeMTTR: () => (/* binding */ computeMTTR),
|
|
11
|
+
/* harmony export */ fingerprintFinding: () => (/* binding */ fingerprintFinding),
|
|
10
12
|
/* harmony export */ renderSlaSummary: () => (/* binding */ renderSlaSummary),
|
|
11
13
|
/* harmony export */ stampFindingTimestamps: () => (/* binding */ stampFindingTimestamps)
|
|
12
14
|
/* harmony export */ });
|
|
13
|
-
/* unused harmony
|
|
15
|
+
/* unused harmony export findingsExceedingSLA */
|
|
14
16
|
/* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7598);
|
|
15
17
|
// 0.8.0 Feat-11: MTTR / finding-age tracking — per-finding firstSeenAt/lastSeenAt with SLA breach detection.
|
|
16
18
|
//
|
|
@@ -24,6 +26,11 @@ export const modules = {
|
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
// Stable fingerprint for cross-scan finding identity. Mirrors the dedupe key.
|
|
29
|
+
// Exported so a caller can compute the "removed since baseline" (i.e. fixed)
|
|
30
|
+
// set that computeMTTR needs, using the exact same identity function
|
|
31
|
+
// buildBaselineMap uses internally — a caller-side reimplementation would
|
|
32
|
+
// risk drifting from this one and silently under/over-counting fixes.
|
|
33
|
+
function fingerprintFinding(f) { return _fingerprint(f); }
|
|
27
34
|
function _fingerprint(f) {
|
|
28
35
|
const file = (f.file || '').split(' -> ').pop();
|
|
29
36
|
const line = f.line || f.source?.line || f.sink?.line || 0;
|
package/dist/513.index.js
CHANGED
|
@@ -12,6 +12,7 @@ export const modules = {
|
|
|
12
12
|
/* unused harmony exports badgeFromScanRoot, renderSvg, _internal */
|
|
13
13
|
/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
|
|
14
14
|
/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6760);
|
|
15
|
+
/* harmony import */ var _posture_state_dir_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1174);
|
|
15
16
|
// Live SVG badge generator (v0.72).
|
|
16
17
|
//
|
|
17
18
|
// Every repo can drop a badge in its README pulling from the latest scan:
|
|
@@ -42,6 +43,7 @@ export const modules = {
|
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
|
|
46
|
+
|
|
45
47
|
const COLORS = {
|
|
46
48
|
critical: '#e05d44', // red
|
|
47
49
|
high: '#fe7d37', // orange
|
|
@@ -56,7 +58,7 @@ const SEVERITIES = ['critical', 'high', 'medium', 'low', 'info'];
|
|
|
56
58
|
|
|
57
59
|
function _readLastScan(scanRoot) {
|
|
58
60
|
if (!scanRoot) return null;
|
|
59
|
-
const fp =
|
|
61
|
+
const fp = (0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, 'last-scan.json');
|
|
60
62
|
if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(fp)) return null;
|
|
61
63
|
try { return JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8')); }
|
|
62
64
|
catch { return null; }
|
|
@@ -214,6 +216,7 @@ const _internal = { COLORS, _ageString, _readLastScan };
|
|
|
214
216
|
/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
|
|
215
217
|
/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6760);
|
|
216
218
|
/* harmony import */ var _badge_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3117);
|
|
219
|
+
/* harmony import */ var _posture_state_dir_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1174);
|
|
217
220
|
// Leaderboard backend (v0.72).
|
|
218
221
|
//
|
|
219
222
|
// Generates the data shape that powers the future public leaderboard at
|
|
@@ -243,6 +246,7 @@ const _internal = { COLORS, _ageString, _readLastScan };
|
|
|
243
246
|
|
|
244
247
|
|
|
245
248
|
|
|
249
|
+
|
|
246
250
|
// Grade thresholds. Critical findings dominate; high/medium contribute
|
|
247
251
|
// secondarily. These numbers are heuristic — calibrate against the
|
|
248
252
|
// public leaderboard corpus once data lands.
|
|
@@ -306,7 +310,7 @@ function _deltaTrend(history) {
|
|
|
306
310
|
*/
|
|
307
311
|
function leaderboardRowFor({ scanRoot, repo, badgeBase = 'https://agentic-security.dev/badge' } = {}) {
|
|
308
312
|
if (!repo) throw new Error('leaderboardRowFor: repo slug is required');
|
|
309
|
-
const lastScanPath =
|
|
313
|
+
const lastScanPath = (0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_3__/* .statePath */ .BQ)(scanRoot || '.', 'last-scan.json');
|
|
310
314
|
let scan = null;
|
|
311
315
|
try { scan = JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(lastScanPath, 'utf8')); } catch {}
|
|
312
316
|
const summary = (0,_badge_js__WEBPACK_IMPORTED_MODULE_2__/* .summarizeForBadge */ .iI)(scan);
|
|
@@ -314,7 +318,7 @@ function leaderboardRowFor({ scanRoot, repo, badgeBase = 'https://agentic-securi
|
|
|
314
318
|
const topCwe = _topCwe(scan);
|
|
315
319
|
|
|
316
320
|
// Optional scan history for the trend signal.
|
|
317
|
-
const historyPath =
|
|
321
|
+
const historyPath = (0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_3__/* .statePath */ .BQ)(scanRoot || '.', 'scan-history.jsonl');
|
|
318
322
|
let history = [];
|
|
319
323
|
if (node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(historyPath)) {
|
|
320
324
|
try {
|
package/dist/526.index.js
CHANGED
|
@@ -52,7 +52,6 @@ export const modules = {
|
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
const STATE_DIR = '.agentic-security';
|
|
56
55
|
const LOG_FILE = 'fix-metrics.jsonl';
|
|
57
56
|
|
|
58
57
|
// Below this many samples a percentile is an artifact of the sample, not a
|
|
@@ -65,7 +64,7 @@ const RELIABLE_N = 10;
|
|
|
65
64
|
const FIX_STAGES = Object.freeze(['rescan', 'lint', 'tests', 'honesty', 'poc']);
|
|
66
65
|
|
|
67
66
|
function _logPath(scanRoot) {
|
|
68
|
-
return
|
|
67
|
+
return (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, LOG_FILE);
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
/**
|
|
@@ -77,9 +76,10 @@ function _logPath(scanRoot) {
|
|
|
77
76
|
function recordFixAttempt(scanRoot, record) {
|
|
78
77
|
if (!scanRoot || !record || typeof record !== 'object') return false;
|
|
79
78
|
try {
|
|
80
|
-
const dir =
|
|
79
|
+
const dir = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .stateDir */ .Pn)(scanRoot);
|
|
81
80
|
if (!(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.isSafeStateDir)(dir)) return false;
|
|
82
|
-
|
|
81
|
+
if (!(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.stateWritesEnabled)()) return false;
|
|
82
|
+
node_fs__WEBPACK_IMPORTED_MODULE_0__.mkdirSync(dir, { recursive: true });
|
|
83
83
|
// One writeSync of one newline-terminated line: a concurrent reader sees
|
|
84
84
|
// whole records or nothing, and a torn tail is dropped on read.
|
|
85
85
|
node_fs__WEBPACK_IMPORTED_MODULE_0__.appendFileSync(_logPath(scanRoot), JSON.stringify(record) + '\n', 'utf8');
|
|
@@ -234,8 +234,8 @@ var external_node_child_process_ = __webpack_require__(1421);
|
|
|
234
234
|
var external_node_fs_ = __webpack_require__(3024);
|
|
235
235
|
// EXTERNAL MODULE: external "node:path"
|
|
236
236
|
var external_node_path_ = __webpack_require__(6760);
|
|
237
|
-
// EXTERNAL MODULE: ./src/engine.js +
|
|
238
|
-
var engine = __webpack_require__(
|
|
237
|
+
// EXTERNAL MODULE: ./src/engine.js + 196 modules
|
|
238
|
+
var engine = __webpack_require__(2739);
|
|
239
239
|
;// CONCATENATED MODULE: ./src/posture/fix-honesty-gate.js
|
|
240
240
|
// Deterministic honesty gates on fix / finding output (#7).
|
|
241
241
|
//
|
package/dist/637.index.js
CHANGED
|
@@ -11,7 +11,7 @@ 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__(
|
|
14
|
+
/* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2739);
|
|
15
15
|
// Shadowscan / security-DELTA on PR (v0.72).
|
|
16
16
|
//
|
|
17
17
|
// Most SAST PR-comment integrations show absolute counts — "12 findings
|
package/dist/675.index.js
CHANGED
|
@@ -11,6 +11,7 @@ export const modules = {
|
|
|
11
11
|
/* unused harmony exports readDeps, _queryOsvForDep, loadState, persistState, loadConfig, formatPayload, _internals */
|
|
12
12
|
/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
|
|
13
13
|
/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6760);
|
|
14
|
+
/* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1174);
|
|
14
15
|
// Continuous CVE-watch daemon.
|
|
15
16
|
//
|
|
16
17
|
// Polls OSV for the project's dependency tree on a schedule (or one-shot),
|
|
@@ -40,9 +41,9 @@ export const modules = {
|
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
|
|
44
|
+
|
|
43
45
|
const OSV_API = 'https://api.osv.dev/v1/query';
|
|
44
|
-
|
|
45
|
-
const STATE_PATH = '.agentic-security/cve-alerts-state.json';
|
|
46
|
+
|
|
46
47
|
|
|
47
48
|
// Multi-ecosystem dep extraction. Each entry: { manifest filename → ecosystem }
|
|
48
49
|
const ECOSYSTEM_BY_MANIFEST = {
|
|
@@ -280,7 +281,7 @@ async function _queryOsvForDep({ name, ecosystem }, { fetchImpl = globalThis.fet
|
|
|
280
281
|
// ─── State persistence ───────────────────────────────────────────────────
|
|
281
282
|
|
|
282
283
|
function loadState(scanRoot) {
|
|
283
|
-
const fp =
|
|
284
|
+
const fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, 'cve-alerts-state.json');
|
|
284
285
|
if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(fp)) return { known: new Set(), lastRun: null };
|
|
285
286
|
try {
|
|
286
287
|
const j = JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8'));
|
|
@@ -289,7 +290,8 @@ function loadState(scanRoot) {
|
|
|
289
290
|
}
|
|
290
291
|
|
|
291
292
|
function persistState(scanRoot, state) {
|
|
292
|
-
const fp =
|
|
293
|
+
const fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, 'cve-alerts-state.json');
|
|
294
|
+
if (!(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.stateWritesEnabled)()) return;
|
|
293
295
|
node_fs__WEBPACK_IMPORTED_MODULE_0__.mkdirSync(node_path__WEBPACK_IMPORTED_MODULE_1__.dirname(fp), { recursive: true });
|
|
294
296
|
node_fs__WEBPACK_IMPORTED_MODULE_0__.writeFileSync(fp, JSON.stringify({
|
|
295
297
|
known: [...state.known].sort(),
|
|
@@ -298,7 +300,7 @@ function persistState(scanRoot, state) {
|
|
|
298
300
|
}
|
|
299
301
|
|
|
300
302
|
function loadConfig(scanRoot) {
|
|
301
|
-
const fp =
|
|
303
|
+
const fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, 'cve-alerts.json');
|
|
302
304
|
if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(fp)) return null;
|
|
303
305
|
try { return JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8')); } catch { return null; }
|
|
304
306
|
}
|