@clear-capabilities/agentic-security-scanner 0.75.0 → 0.77.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 (68) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/bin/agentic-security.js +2 -2
  3. package/dist/838.index.js +152 -0
  4. package/dist/{634.index.js → 985.index.js} +21 -144
  5. package/dist/agentic-security.mjs +8 -8
  6. package/dist/agentic-security.mjs.sha256 +1 -1
  7. package/package.json +6 -6
  8. package/src/mcp/tools.js +17 -2
  9. package/src/sca/base-images.json +1 -1
  10. package/bin/.agentic-security/findings.json +0 -1596
  11. package/bin/.agentic-security/last-scan.json +0 -1596
  12. package/bin/.agentic-security/last-scan.json.sig +0 -1
  13. package/bin/.agentic-security/scan-history.json +0 -470
  14. package/bin/.agentic-security/streak.json +0 -25
  15. package/dist/218.index.js +0 -793
  16. package/dist/601.index.js +0 -1038
  17. package/src/.agentic-security/findings.json +0 -80844
  18. package/src/.agentic-security/last-scan.json +0 -80844
  19. package/src/.agentic-security/last-scan.json.sig +0 -1
  20. package/src/.agentic-security/scan-history.json +0 -8408
  21. package/src/.agentic-security/streak.json +0 -26
  22. package/src/dataflow/.agentic-security/findings.json +0 -3487
  23. package/src/dataflow/.agentic-security/last-scan.json +0 -3487
  24. package/src/dataflow/.agentic-security/last-scan.json.sig +0 -1
  25. package/src/dataflow/.agentic-security/scan-history.json +0 -735
  26. package/src/dataflow/.agentic-security/streak.json +0 -24
  27. package/src/integrations/.agentic-security/findings.json +0 -1504
  28. package/src/integrations/.agentic-security/last-scan.json +0 -1504
  29. package/src/integrations/.agentic-security/scan-history.json +0 -40
  30. package/src/integrations/.agentic-security/streak.json +0 -21
  31. package/src/ir/.agentic-security/findings.json +0 -3036
  32. package/src/ir/.agentic-security/last-scan.json +0 -3036
  33. package/src/ir/.agentic-security/last-scan.json.sig +0 -1
  34. package/src/ir/.agentic-security/scan-history.json +0 -364
  35. package/src/ir/.agentic-security/streak.json +0 -23
  36. package/src/llm-validator/.agentic-security/findings.json +0 -1891
  37. package/src/llm-validator/.agentic-security/last-scan.json +0 -1891
  38. package/src/llm-validator/.agentic-security/last-scan.json.sig +0 -1
  39. package/src/llm-validator/.agentic-security/scan-history.json +0 -168
  40. package/src/llm-validator/.agentic-security/streak.json +0 -20
  41. package/src/lsp/.agentic-security/findings.json +0 -28
  42. package/src/lsp/.agentic-security/last-scan.json +0 -28
  43. package/src/lsp/.agentic-security/scan-history.json +0 -79
  44. package/src/lsp/.agentic-security/streak.json +0 -22
  45. package/src/mcp/.agentic-security/findings.json +0 -8358
  46. package/src/mcp/.agentic-security/last-scan.json +0 -8358
  47. package/src/mcp/.agentic-security/last-scan.json.sig +0 -1
  48. package/src/mcp/.agentic-security/scan-history.json +0 -1125
  49. package/src/mcp/.agentic-security/streak.json +0 -22
  50. package/src/posture/.agentic-security/findings.json +0 -51239
  51. package/src/posture/.agentic-security/last-scan.json +0 -51239
  52. package/src/posture/.agentic-security/last-scan.json.sig +0 -1
  53. package/src/posture/.agentic-security/scan-history.json +0 -5557
  54. package/src/posture/.agentic-security/streak.json +0 -24
  55. package/src/report/.agentic-security/findings.json +0 -79
  56. package/src/report/.agentic-security/last-scan.json +0 -79
  57. package/src/report/.agentic-security/last-scan.json.sig +0 -1
  58. package/src/report/.agentic-security/scan-history.json +0 -332
  59. package/src/report/.agentic-security/streak.json +0 -23
  60. package/src/sast/.agentic-security/findings.json +0 -5051
  61. package/src/sast/.agentic-security/last-scan.json +0 -5051
  62. package/src/sast/.agentic-security/last-scan.json.sig +0 -1
  63. package/src/sast/.agentic-security/scan-history.json +0 -788
  64. package/src/sast/.agentic-security/streak.json +0 -23
  65. package/src/sast/bench-shape/.agentic-security/findings.json +0 -28
  66. package/src/sast/bench-shape/.agentic-security/last-scan.json +0 -28
  67. package/src/sast/bench-shape/.agentic-security/scan-history.json +0 -24
  68. package/src/sast/bench-shape/.agentic-security/streak.json +0 -22
package/CHANGELOG.md CHANGED
@@ -1,5 +1,62 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.76.0 — Command consolidation: 80 → 38 slash commands
4
+
5
+ Simplified the command surface from 80 individual slash commands down to
6
+ 38 by merging related commands into consolidated routers with flags.
7
+ No functionality removed — all logic preserved behind flags on fewer,
8
+ more discoverable parent commands.
9
+
10
+ ### New consolidated commands
11
+
12
+ | New command | Absorbed | Routing |
13
+ |---|---|---|
14
+ | `/audit` | db-audit, auth-audit, rate-limit-check, webhook-audit, env-check, csp-cors, deploy-check, launch-check, llm-cost-ceiling, prompt-firewall | `--target <area>` or `--all` |
15
+ | `/threat` | threat-model, personas, playbook, bounty, adversary, attack-surface, trust-boundary, spof | `--view <name>` |
16
+ | `/llm` | llm-redteam, jailbreak-detector, llm-eval | `--mode redteam\|jailbreak\|eval` |
17
+ | `/ci` | ci-gate, predeploy-gate, install-hooks | default / `--predeploy` / `--hooks` |
18
+ | `/generate` | privacy-docs, disaster-playbook, social-media, security-tests | `--type privacy\|disaster\|social\|tests` |
19
+ | `/scanner` | self-test, diff-scan, scan-baseline, concurrency-bugs, spec-drift | `--self-test` / `--diff` / `--baseline` / `--concurrency` / `--spec-drift` |
20
+
21
+ ### Commands absorbed into existing commands
22
+
23
+ - `/why-fired` → `/explain --provenance --finding <id>`
24
+ - `/why-not` → `/explain --gap <CWE>`
25
+ - `/install-script-audit` → `/supply-chain-check --show install-scripts`
26
+ - `/vendor-audit` → `/supply-chain-check --show vendored`
27
+
28
+ ### Deleted deprecated aliases (11)
29
+
30
+ ci-gate-multi, rotate-key-auto, trim-dead-code, trim-dependencies,
31
+ story-explain, security-badge, security-onepager, trust-page,
32
+ dep-pinning, dep-freshness, dep-alternatives.
33
+
34
+ ## 0.75.1 — /agent-harness-assessment + interactive compliance routing + README badge relocation
35
+
36
+ Three follow-ups to the 0.75.0 surface:
37
+
38
+ **Renamed `/executive-summary` → `/agent-harness-assessment`.** The
39
+ previous name framed this as a finance-style report. The actual artifact
40
+ is an assessment of the AI-agent harness: a CISO/buyer reading it wants
41
+ to know whether to trust an AI agent working in this project, not just
42
+ see a posture grade. The new name reflects the audience.
43
+
44
+ **Interactive compliance step.** After printing the six-control
45
+ assessment, the command now asks (via AskUserQuestion) which compliance
46
+ frameworks the reader wants generated NOW — NIST AI 600-1, OWASP ASVS,
47
+ OWASP LLM Top 10 (2025), or none. For each selection, the model invokes
48
+ `/compliance-report <fw>` with the matching positional argument
49
+ (`nist`, `asvs`, `llm`) so an auditor-ready file lands on disk. The
50
+ Compliance section in the assessment now says what evidence COULD be
51
+ produced; the interactive step closes the loop to evidence that EXISTS.
52
+
53
+ **README "Status badge" section relocated** from the top-of-README hero
54
+ region into the Security Pros section, between the 5-minute pro setup
55
+ and the full pro catalog. Adopting the badge is a pro-shaped step
56
+ (it requires CI wiring + a baseline scan). Three example badges now
57
+ render on three distinct lines via trailing `<br>` so the severity
58
+ ladder is legible at a glance.
59
+
3
60
  ## 0.75.0 — /executive-summary: CISO-facing six-control posture report
4
61
 
5
62
  New top-level command for buyer-questionnaire / diligence / CISO use.
@@ -137,7 +137,7 @@ function printBanner(args) {
137
137
  BOLD: '\x1b[1m',
138
138
  RESET: '\x1b[0m',
139
139
  } : { FROG:'', DEEP:'', CREAM:'', DIM:'', BOLD:'', RESET:'' };
140
- const v = '0.75.0';
140
+ const v = '0.75.1';
141
141
  const compact = !args.flags.full;
142
142
  if (compact) {
143
143
  const lines = [
@@ -1665,7 +1665,7 @@ async function main() {
1665
1665
  }
1666
1666
  process.exit(0);
1667
1667
  }
1668
- case 'version': console.log('agentic-security 0.75.0 · created by ClearCapabilities.Com'); process.exit(0);
1668
+ case 'version': console.log('agentic-security 0.75.1 · created by ClearCapabilities.Com'); process.exit(0);
1669
1669
  case 'banner': { printBanner(args); process.exit(0); }
1670
1670
  case 'harness': process.exit(await cmdHarness(args));
1671
1671
  case 'scan-baseline': process.exit(await cmdScanBaseline(args));
@@ -0,0 +1,152 @@
1
+ export const id = 838;
2
+ export const ids = [838];
3
+ export const modules = {
4
+
5
+ /***/ 7838:
6
+ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
+
8
+ __webpack_require__.r(__webpack_exports__);
9
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
10
+ /* harmony export */ runProjectLinter: () => (/* binding */ runProjectLinter),
11
+ /* harmony export */ verifyFix: () => (/* binding */ verifyFix),
12
+ /* harmony export */ verifyPatch: () => (/* binding */ verifyPatch)
13
+ /* harmony export */ });
14
+ /* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1421);
15
+ /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3024);
16
+ /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6760);
17
+ /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3291);
18
+ // Closed-loop /fix verification (Sentinel-parity FR-L4-4, FR-L4-5).
19
+ //
20
+ // Given a candidate patch (the new file content + the finding stableId being
21
+ // fixed), verify it:
22
+ //
23
+ // 1. The original finding's stableId no longer fires on the patched file.
24
+ // 2. No new findings at severity ≥ medium were introduced by the patch.
25
+ // 3. The project's existing linter (when present) passes on the patched file.
26
+ //
27
+ // If any of those fail, the caller is expected to NOT apply the patch and
28
+ // instead surface a "fix plan" — a numbered list of steps the engineer can
29
+ // follow — rather than dump a broken patch on the user.
30
+
31
+
32
+
33
+
34
+
35
+
36
+ const SEVERITY_RANK = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
37
+
38
+ // Run a focused re-scan over just the patched file(s) using the in-memory
39
+ // engine. No filesystem write needed — we hand the new content in via the
40
+ // fileContents map.
41
+ async function verifyPatch({
42
+ scanRoot,
43
+ originalFindingStableId,
44
+ files, // { [relPath]: newContent }
45
+ depFileContents = {},
46
+ } = {}) {
47
+ if (!files || typeof files !== 'object') return { ok: false, reason: 'no-files-provided' };
48
+ const fileContents = { ...files };
49
+ let scan;
50
+ try {
51
+ scan = await (0,_engine_js__WEBPACK_IMPORTED_MODULE_3__/* .runFullScan */ .wW)({ fileContents, depFileContents, scanRoot }, () => {});
52
+ } catch (e) {
53
+ return { ok: false, reason: 'rescan-failed', error: e.message };
54
+ }
55
+ const findings = (scan && scan.findings) || [];
56
+ const stillHasOriginal = !!originalFindingStableId &&
57
+ findings.some(f => f.stableId === originalFindingStableId);
58
+ if (stillHasOriginal) {
59
+ return { ok: false, reason: 'original-finding-still-present', stableId: originalFindingStableId };
60
+ }
61
+ const introducedHighOrAbove = findings.filter(f =>
62
+ (SEVERITY_RANK[f.severity] ?? 9) <= SEVERITY_RANK.medium);
63
+ // Don't count findings on lines outside the patched files — but our
64
+ // fileContents map IS the patched files, so every finding is in-scope.
65
+ return {
66
+ ok: introducedHighOrAbove.length === 0,
67
+ reason: introducedHighOrAbove.length === 0 ? 'verified' : 'introduced-new-findings',
68
+ introduced: introducedHighOrAbove.map(f => ({
69
+ vuln: f.vuln, file: f.file, line: f.line, severity: f.severity,
70
+ stableId: f.stableId,
71
+ })),
72
+ };
73
+ }
74
+
75
+ // Detect which linter the project uses and run it on the patched files.
76
+ // Returns { ok, runner, output } or { ok: true, runner: 'none' } when no
77
+ // linter is configured (silent pass).
78
+ function runProjectLinter(scanRoot, filePaths) {
79
+ if (!scanRoot || !Array.isArray(filePaths) || filePaths.length === 0) {
80
+ return { ok: true, runner: 'none' };
81
+ }
82
+ const has = (p) => { try { return node_fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(node_path__WEBPACK_IMPORTED_MODULE_2__.join(scanRoot, p)); } catch { return false; } };
83
+ // Pick the linter by config file present in the repo root.
84
+ const jsFiles = filePaths.filter(f => /\.(?:js|jsx|ts|tsx|mjs|cjs)$/i.test(f));
85
+ const pyFiles = filePaths.filter(f => /\.py$/i.test(f));
86
+ const goFiles = filePaths.filter(f => /\.go$/i.test(f));
87
+ const javaFiles = filePaths.filter(f => /\.java$/i.test(f));
88
+
89
+ if (jsFiles.length && (has('.eslintrc') || has('.eslintrc.json') || has('.eslintrc.js') || has('eslint.config.js') || has('eslint.config.mjs'))) {
90
+ return runLinter(scanRoot, 'eslint', ['--no-error-on-unmatched-pattern', ...jsFiles]);
91
+ }
92
+ if (pyFiles.length && (has('pyproject.toml') || has('ruff.toml') || has('.ruff.toml'))) {
93
+ return runLinter(scanRoot, 'ruff', ['check', ...pyFiles]);
94
+ }
95
+ if (pyFiles.length && has('.flake8')) {
96
+ return runLinter(scanRoot, 'flake8', pyFiles);
97
+ }
98
+ if (goFiles.length && (has('.golangci.yml') || has('.golangci.yaml'))) {
99
+ return runLinter(scanRoot, 'golangci-lint', ['run', ...goFiles]);
100
+ }
101
+ if (javaFiles.length && has('checkstyle.xml')) {
102
+ return runLinter(scanRoot, 'checkstyle', ['-c', 'checkstyle.xml', ...javaFiles]);
103
+ }
104
+ return { ok: true, runner: 'none' };
105
+ }
106
+
107
+ function runLinter(cwd, cmd, args) {
108
+ let r;
109
+ try {
110
+ r = (0,node_child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync)(cmd, args, { cwd, encoding: 'utf8', timeout: 60_000 });
111
+ } catch (e) {
112
+ return { ok: true, runner: cmd, skipped: true, reason: 'binary-missing', error: e.message };
113
+ }
114
+ if (r.error && r.error.code === 'ENOENT') {
115
+ return { ok: true, runner: cmd, skipped: true, reason: 'binary-missing' };
116
+ }
117
+ if (r.status === null) {
118
+ return { ok: false, runner: cmd, reason: 'timed-out', output: (r.stderr || r.stdout || '').slice(-2000) };
119
+ }
120
+ return {
121
+ ok: r.status === 0,
122
+ runner: cmd,
123
+ exitCode: r.status,
124
+ output: ((r.stderr || '') + (r.stdout || '')).slice(-2000),
125
+ };
126
+ }
127
+
128
+ // Top-level verify: re-scan + lint. Returns the combined verdict + a
129
+ // human-readable summary string suitable for surfacing to the user.
130
+ async function verifyFix({
131
+ scanRoot,
132
+ originalFindingStableId,
133
+ files,
134
+ depFileContents,
135
+ } = {}) {
136
+ const rescan = await verifyPatch({ scanRoot, originalFindingStableId, files, depFileContents });
137
+ const lint = runProjectLinter(scanRoot, Object.keys(files || {}));
138
+ const ok = rescan.ok && (lint.ok || lint.skipped);
139
+ const summary = [
140
+ `re-scan: ${rescan.ok ? 'PASS' : 'FAIL — ' + rescan.reason}`,
141
+ `linter: ${lint.runner === 'none' ? 'skipped (no linter config)'
142
+ : lint.skipped ? `${lint.runner} not installed`
143
+ : lint.ok ? `${lint.runner} PASS`
144
+ : `${lint.runner} FAIL (exit ${lint.exitCode})`}`,
145
+ ].join('\n');
146
+ return { ok, rescan, lint, summary };
147
+ }
148
+
149
+
150
+ /***/ })
151
+
152
+ };
@@ -1,8 +1,8 @@
1
- export const id = 634;
2
- export const ids = [634];
1
+ export const id = 985;
2
+ export const ids = [985];
3
3
  export const modules = {
4
4
 
5
- /***/ 7634:
5
+ /***/ 3985:
6
6
  /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
7
 
8
8
 
@@ -21,8 +21,6 @@ var external_node_path_ = __webpack_require__(6760);
21
21
  var external_node_url_ = __webpack_require__(3136);
22
22
  // EXTERNAL MODULE: external "node:fs/promises"
23
23
  var promises_ = __webpack_require__(1455);
24
- // EXTERNAL MODULE: ./src/runScan.js + 2 modules
25
- var runScan = __webpack_require__(9099);
26
24
  // EXTERNAL MODULE: ./src/posture/fix-history.js
27
25
  var fix_history = __webpack_require__(4407);
28
26
  // EXTERNAL MODULE: ./src/posture/integrity.js
@@ -104,142 +102,6 @@ function redactArgsBlob(s) {
104
102
  return redactString(s);
105
103
  }
106
104
 
107
- // EXTERNAL MODULE: external "node:child_process"
108
- var external_node_child_process_ = __webpack_require__(1421);
109
- // EXTERNAL MODULE: ./src/engine.js + 442 modules
110
- var engine = __webpack_require__(3291);
111
- ;// CONCATENATED MODULE: ./src/posture/fix-verify.js
112
- // Closed-loop /fix verification (Sentinel-parity FR-L4-4, FR-L4-5).
113
- //
114
- // Given a candidate patch (the new file content + the finding stableId being
115
- // fixed), verify it:
116
- //
117
- // 1. The original finding's stableId no longer fires on the patched file.
118
- // 2. No new findings at severity ≥ medium were introduced by the patch.
119
- // 3. The project's existing linter (when present) passes on the patched file.
120
- //
121
- // If any of those fail, the caller is expected to NOT apply the patch and
122
- // instead surface a "fix plan" — a numbered list of steps the engineer can
123
- // follow — rather than dump a broken patch on the user.
124
-
125
-
126
-
127
-
128
-
129
-
130
- const SEVERITY_RANK = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
131
-
132
- // Run a focused re-scan over just the patched file(s) using the in-memory
133
- // engine. No filesystem write needed — we hand the new content in via the
134
- // fileContents map.
135
- async function verifyPatch({
136
- scanRoot,
137
- originalFindingStableId,
138
- files, // { [relPath]: newContent }
139
- depFileContents = {},
140
- } = {}) {
141
- if (!files || typeof files !== 'object') return { ok: false, reason: 'no-files-provided' };
142
- const fileContents = { ...files };
143
- let scan;
144
- try {
145
- scan = await (0,engine/* runFullScan */.wW)({ fileContents, depFileContents, scanRoot }, () => {});
146
- } catch (e) {
147
- return { ok: false, reason: 'rescan-failed', error: e.message };
148
- }
149
- const findings = (scan && scan.findings) || [];
150
- const stillHasOriginal = !!originalFindingStableId &&
151
- findings.some(f => f.stableId === originalFindingStableId);
152
- if (stillHasOriginal) {
153
- return { ok: false, reason: 'original-finding-still-present', stableId: originalFindingStableId };
154
- }
155
- const introducedHighOrAbove = findings.filter(f =>
156
- (SEVERITY_RANK[f.severity] ?? 9) <= SEVERITY_RANK.medium);
157
- // Don't count findings on lines outside the patched files — but our
158
- // fileContents map IS the patched files, so every finding is in-scope.
159
- return {
160
- ok: introducedHighOrAbove.length === 0,
161
- reason: introducedHighOrAbove.length === 0 ? 'verified' : 'introduced-new-findings',
162
- introduced: introducedHighOrAbove.map(f => ({
163
- vuln: f.vuln, file: f.file, line: f.line, severity: f.severity,
164
- stableId: f.stableId,
165
- })),
166
- };
167
- }
168
-
169
- // Detect which linter the project uses and run it on the patched files.
170
- // Returns { ok, runner, output } or { ok: true, runner: 'none' } when no
171
- // linter is configured (silent pass).
172
- function runProjectLinter(scanRoot, filePaths) {
173
- if (!scanRoot || !Array.isArray(filePaths) || filePaths.length === 0) {
174
- return { ok: true, runner: 'none' };
175
- }
176
- const has = (p) => { try { return external_node_fs_.existsSync(external_node_path_.join(scanRoot, p)); } catch { return false; } };
177
- // Pick the linter by config file present in the repo root.
178
- const jsFiles = filePaths.filter(f => /\.(?:js|jsx|ts|tsx|mjs|cjs)$/i.test(f));
179
- const pyFiles = filePaths.filter(f => /\.py$/i.test(f));
180
- const goFiles = filePaths.filter(f => /\.go$/i.test(f));
181
- const javaFiles = filePaths.filter(f => /\.java$/i.test(f));
182
-
183
- if (jsFiles.length && (has('.eslintrc') || has('.eslintrc.json') || has('.eslintrc.js') || has('eslint.config.js') || has('eslint.config.mjs'))) {
184
- return runLinter(scanRoot, 'eslint', ['--no-error-on-unmatched-pattern', ...jsFiles]);
185
- }
186
- if (pyFiles.length && (has('pyproject.toml') || has('ruff.toml') || has('.ruff.toml'))) {
187
- return runLinter(scanRoot, 'ruff', ['check', ...pyFiles]);
188
- }
189
- if (pyFiles.length && has('.flake8')) {
190
- return runLinter(scanRoot, 'flake8', pyFiles);
191
- }
192
- if (goFiles.length && (has('.golangci.yml') || has('.golangci.yaml'))) {
193
- return runLinter(scanRoot, 'golangci-lint', ['run', ...goFiles]);
194
- }
195
- if (javaFiles.length && has('checkstyle.xml')) {
196
- return runLinter(scanRoot, 'checkstyle', ['-c', 'checkstyle.xml', ...javaFiles]);
197
- }
198
- return { ok: true, runner: 'none' };
199
- }
200
-
201
- function runLinter(cwd, cmd, args) {
202
- let r;
203
- try {
204
- r = (0,external_node_child_process_.spawnSync)(cmd, args, { cwd, encoding: 'utf8', timeout: 60_000 });
205
- } catch (e) {
206
- return { ok: true, runner: cmd, skipped: true, reason: 'binary-missing', error: e.message };
207
- }
208
- if (r.error && r.error.code === 'ENOENT') {
209
- return { ok: true, runner: cmd, skipped: true, reason: 'binary-missing' };
210
- }
211
- if (r.status === null) {
212
- return { ok: false, runner: cmd, reason: 'timed-out', output: (r.stderr || r.stdout || '').slice(-2000) };
213
- }
214
- return {
215
- ok: r.status === 0,
216
- runner: cmd,
217
- exitCode: r.status,
218
- output: ((r.stderr || '') + (r.stdout || '')).slice(-2000),
219
- };
220
- }
221
-
222
- // Top-level verify: re-scan + lint. Returns the combined verdict + a
223
- // human-readable summary string suitable for surfacing to the user.
224
- async function verifyFix({
225
- scanRoot,
226
- originalFindingStableId,
227
- files,
228
- depFileContents,
229
- } = {}) {
230
- const rescan = await verifyPatch({ scanRoot, originalFindingStableId, files, depFileContents });
231
- const lint = runProjectLinter(scanRoot, Object.keys(files || {}));
232
- const ok = rescan.ok && (lint.ok || lint.skipped);
233
- const summary = [
234
- `re-scan: ${rescan.ok ? 'PASS' : 'FAIL — ' + rescan.reason}`,
235
- `linter: ${lint.runner === 'none' ? 'skipped (no linter config)'
236
- : lint.skipped ? `${lint.runner} not installed`
237
- : lint.ok ? `${lint.runner} PASS`
238
- : `${lint.runner} FAIL (exit ${lint.exitCode})`}`,
239
- ].join('\n');
240
- return { ok, rescan, lint, summary };
241
- }
242
-
243
105
  ;// CONCATENATED MODULE: ./src/posture/agents-memory.js
244
106
  // AGENTS.md — writable continual-learning memory (harness-anatomy #2).
245
107
  //
@@ -533,7 +395,20 @@ const cve_lookup_internals = { CACHE_DIR, CVE_RE, _stalenessTier };
533
395
 
534
396
 
535
397
 
536
-
398
+ // Lazy-loaded: these transitively pull in npm packages (fast-glob,
399
+ // @babel/core) that aren't available in the plugin-cache install path
400
+ // (no node_modules). Deferring keeps the MCP server bootable everywhere;
401
+ // the import only runs when a tool that needs them is actually called.
402
+ let _runScan;
403
+ async function getRunScan() {
404
+ if (!_runScan) _runScan = (await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 9099))).runScan;
405
+ return _runScan;
406
+ }
407
+ let _verifyFixCore;
408
+ async function getVerifyFixCore() {
409
+ if (!_verifyFixCore) _verifyFixCore = (await __webpack_require__.e(/* import() */ 838).then(__webpack_require__.bind(__webpack_require__, 7838))).verifyFix;
410
+ return _verifyFixCore;
411
+ }
537
412
 
538
413
  const MAX_FILES_PER_SCAN = 1024;
539
414
  const MAX_FILE_BYTES = 500_000;
@@ -828,7 +703,8 @@ const scan_diff = {
828
703
  fileContents[rel] = content;
829
704
  }
830
705
 
831
- const result = await (0,runScan.runScan)(sessionRoot, { network: false, fileContents });
706
+ const runScan = await getRunScan();
707
+ const result = await runScan(sessionRoot, { network: false, fileContents });
832
708
  const wantSet = new Set(Object.keys(fileContents));
833
709
  const sevRank = { info: 0, low: 1, medium: 2, high: 3, critical: 4 };
834
710
  const min = sevRank[severity] ?? 0;
@@ -1101,7 +977,8 @@ const verify_fix = {
1101
977
  confined[relPath] = String(content);
1102
978
  }
1103
979
  try {
1104
- const r = await verifyFix({
980
+ const verifyFixCore = await getVerifyFixCore();
981
+ const r = await verifyFixCore({
1105
982
  scanRoot: ctx.sessionRoot,
1106
983
  originalFindingStableId: stable_id,
1107
984
  files: confined,