@clear-capabilities/agentic-security-scanner 0.186.1 → 0.187.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 +24 -1
- package/bin/agentic-security.js +42 -17
- package/dist/{2066.index.js → 1602.index.js} +477 -9
- package/dist/9560.index.js +62 -5
- package/dist/agentic-security.mjs +2 -2
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/{ca62ee93079be4c24eee.json → deabc47d0e27e21bf99b.json} +5 -5
- package/package.json +5 -5
- package/src/capabilities/decide.js +7 -1
- package/src/dataflow/CLAUDE.md +2 -0
- package/src/dataflow/catalog-haskell.js +7 -1
- package/src/dataflow/engine.js +16 -0
- package/src/dataflow/privacy-catalog-haskell.js +2 -1
- package/src/language/CLAUDE.md +19 -0
- package/src/language/compliance-map.js +6 -0
- package/src/language/context.js +20 -0
- package/src/language/haskell-disclosure.js +16 -4
- package/src/language/haskell-guards.js +130 -3
- package/src/language/haskell-ir.js +182 -10
- package/src/language/haskell-models.js +93 -4
- package/src/language/haskell-security-rules.js +112 -34
- package/src/language/haskell-syntax.js +10 -1
- package/src/language/haskell-web.js +44 -8
- package/src/language/support-registry.js +3 -2
- package/src/mcp/tools.js +54 -3
- package/src/posture/attack-taxonomy.js +10 -0
- package/src/posture/entrypoint-inventory.js +6 -2
- package/src/posture/proof-fabric/cli.js +1 -1
- package/src/posture/proof-fabric/container.js +2 -2
- package/src/posture/proof-fabric/generate-haskell.js +454 -0
- package/src/posture/proof-fabric/generate.js +15 -5
- package/src/posture/proof-fabric/oracles.js +21 -0
- package/src/posture/proof-fabric/reproducer.js +4 -2
- package/src/posture/proof-fabric/runner.js +4 -4
- package/src/posture/stable-id.js +2 -1
- package/src/report/index.js +3 -0
package/dist/9560.index.js
CHANGED
|
@@ -721,8 +721,14 @@ const INTERPRETER_NAMES = new Set([
|
|
|
721
721
|
'time', 'timeout', 'sudo', 'doas', 'su', 'ssh', 'osascript', 'launchctl', 'script', 'expect', 'tclsh', 'wish',
|
|
722
722
|
'awk', 'gawk', 'mawk', 'nawk', 'java', 'jshell', 'deno', 'bun', 'nodejs', 'swift', 'make', 'gmake',
|
|
723
723
|
'npm', 'npx', 'yarn', 'pnpm', 'pip', 'pip3', 'pipx', 'gem', 'bundle', 'cargo', 'go', 'gradle', 'mvn', 'watch',
|
|
724
|
+
// Haskell and Nix toolchains run code supplied by the project: GHC and cabal execute Template Haskell splices and a
|
|
725
|
+
// custom Setup.hs at build time, runghc/ghci interpret a source file, and the Nix evaluators run the expressions they
|
|
726
|
+
// are given (builders run the derivation's build script).
|
|
727
|
+
'ghc', 'ghci', 'runghc', 'runhaskell', 'cabal', 'stack',
|
|
728
|
+
'nix', 'nix-build', 'nix-shell', 'nix-instantiate', 'nix-env', 'nix-store',
|
|
724
729
|
]);
|
|
725
|
-
|
|
730
|
+
// Versioned toolchain binaries (`ghc-9.14.1`, `runghc-9.14.1`, `cabal-3.14`) are the same executables under another name.
|
|
731
|
+
const INTERPRETER_FAMILY = /^(?:python|pypy|ruby|perl|php|lua|luajit|node|tclsh)[0-9.]*$|^(?:ghc|ghci|runghc|runhaskell|cabal|stack)-[0-9][0-9.]*$/;
|
|
726
732
|
|
|
727
733
|
/**
|
|
728
734
|
* What kind of executable this is. `interpreter` is true for a known shell or
|
|
@@ -2620,7 +2626,7 @@ __webpack_require__.a(__webpack_module__, async (__webpack_handle_async_dependen
|
|
|
2620
2626
|
/* harmony export */ });
|
|
2621
2627
|
/* harmony import */ var _posture_proof_fabric_orchestrator_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3740);
|
|
2622
2628
|
/* harmony import */ var _posture_proof_fabric_fix_proof_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(818);
|
|
2623
|
-
/* harmony import */ var _posture_proof_fabric_cli_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(
|
|
2629
|
+
/* harmony import */ var _posture_proof_fabric_cli_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(31602);
|
|
2624
2630
|
/* harmony import */ var _posture_proof_fabric_store_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(62199);
|
|
2625
2631
|
/* harmony import */ var _posture_proof_fabric_reproducer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(37055);
|
|
2626
2632
|
/* harmony import */ var _posture_proof_fabric_siblings_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25099);
|
|
@@ -3578,6 +3584,22 @@ const scan_diff = {
|
|
|
3578
3584
|
},
|
|
3579
3585
|
};
|
|
3580
3586
|
|
|
3587
|
+
// The ordered source-to-sink steps of a finding: its `trace` when the detector wrote one, else the `chain` the IR taint
|
|
3588
|
+
// engine records (file, line, label, kind, provenance). Null when the finding carries neither.
|
|
3589
|
+
function _flowSteps(f) {
|
|
3590
|
+
if (Array.isArray(f.trace) && f.trace.length) return f.trace;
|
|
3591
|
+
if (f.trace && !Array.isArray(f.trace)) return f.trace;
|
|
3592
|
+
if (Array.isArray(f.chain) && f.chain.length) {
|
|
3593
|
+
return f.chain.map((s) => ({ file: s.file ?? null, line: s.line ?? null, label: s.label ?? null, kind: s.kind || (s.provenance ? 'source' : 'step'), provenance: s.provenance ?? null }));
|
|
3594
|
+
}
|
|
3595
|
+
return null;
|
|
3596
|
+
}
|
|
3597
|
+
// Labels of the structured source, sink and rule, for matching only.
|
|
3598
|
+
function _flowEnds(f) {
|
|
3599
|
+
const lab = (x) => (x && typeof x === 'object' ? [x.label, x.family, x.kind].filter((v) => typeof v === 'string').join(' ') : '');
|
|
3600
|
+
return [lab(f.source), lab(f.sink), typeof f.rule === 'string' ? f.rule : ''].join(' ');
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3581
3603
|
// ─── query_taint ─────────────────────────────────────────────────────────────
|
|
3582
3604
|
const query_taint = {
|
|
3583
3605
|
name: 'query_taint',
|
|
@@ -3608,13 +3630,16 @@ const query_taint = {
|
|
|
3608
3630
|
// dilutes its reasoning; the agent receives a bounded slice plus the
|
|
3609
3631
|
// cursor to fetch the rest if it wants.
|
|
3610
3632
|
const all = (scan.findings || []).filter(f => {
|
|
3611
|
-
|
|
3633
|
+
// The structured flow counts too: an IR taint finding (Haskell included) records its path as `source`, `sink`, `rule`
|
|
3634
|
+
// and `chain`, and carries no free-text description or `trace`, so matching only the text fields would answer
|
|
3635
|
+
// "no path" for a path the scan did find.
|
|
3636
|
+
const hay = [f.description, f.title, f.vuln, f.snippet, JSON.stringify(_flowSteps(f) || ''), _flowEnds(f)].join(' ').toLowerCase();
|
|
3612
3637
|
return hay.includes(srcL) && hay.includes(sinkL);
|
|
3613
3638
|
});
|
|
3614
3639
|
const page = all.slice(off, off + lim).map(f => (0,_redact_js__WEBPACK_IMPORTED_MODULE_21__/* .redactFinding */ .lE)({
|
|
3615
3640
|
id: f.id, severity: f.severity, file: f.file, line: f.line,
|
|
3616
3641
|
title: f.title || f.vuln, description: f.description,
|
|
3617
|
-
trace: f
|
|
3642
|
+
trace: _flowSteps(f),
|
|
3618
3643
|
}));
|
|
3619
3644
|
return {
|
|
3620
3645
|
_meta: META,
|
|
@@ -3654,7 +3679,7 @@ const explain_finding = {
|
|
|
3654
3679
|
title: f.title || f.vuln, cwe: f.cwe,
|
|
3655
3680
|
description: f.description, remediation: f.remediation,
|
|
3656
3681
|
snippet: f.snippet || null,
|
|
3657
|
-
trace: f
|
|
3682
|
+
trace: _flowSteps(f),
|
|
3658
3683
|
});
|
|
3659
3684
|
// Harness-anatomy #1: explain_finding's trace is the most-likely-large
|
|
3660
3685
|
// field on a single finding. Offload when it crosses the threshold so
|
|
@@ -4314,6 +4339,9 @@ const synthesize_fix = {
|
|
|
4314
4339
|
languageFix.applyWith = { finding_id: f.id, confirm: true, plan_digest: languageFix.planDigest };
|
|
4315
4340
|
}
|
|
4316
4341
|
else if (prev.ok) autofix = { deterministic: true, ruleId: f.rule || f.family || null, patch: prev.after, file: prev.file, label: prev.label || null, verified: true };
|
|
4342
|
+
} else if (lc.isLanguageDependencyFinding(f)) {
|
|
4343
|
+
// Named, not silent: a Hackage or flake dependency is fixed by raising a constraint, which has its own tool.
|
|
4344
|
+
languageFix = { status: 'use-sca-upgrade', ok: false, label: null, tier: null, reason: 'this is a dependency finding: use synthesize_sca_upgrade for the constraint edit (apply_sca_upgrade writes it)', diff: null, explanation: null, consequences: [] };
|
|
4317
4345
|
}
|
|
4318
4346
|
} catch { /* best-effort: the preview is advisory */ }
|
|
4319
4347
|
}
|
|
@@ -4728,6 +4756,22 @@ const query_cache_telemetry = {
|
|
|
4728
4756
|
},
|
|
4729
4757
|
};
|
|
4730
4758
|
|
|
4759
|
+
async function _languageUpgrade(lc, f, ctx, apply) {
|
|
4760
|
+
const proj = lc.loadLanguageProject(ctx.sessionRoot);
|
|
4761
|
+
return lc.languageUpgradePreview(f, { ...proj.files, ...proj.manifests }, { apply, root: ctx.sessionRoot });
|
|
4762
|
+
}
|
|
4763
|
+
// The result shape both SCA upgrade tools return for a Hackage or flake dependency. `verified` is never true: the edit
|
|
4764
|
+
// changes a declared constraint or a ref, not the resolved version.
|
|
4765
|
+
function _languageUpgradeResult(f, r) {
|
|
4766
|
+
return {
|
|
4767
|
+
ok: true, mode: r.ok ? 'source-edit' : 'blocked', ecosystem: f.ecosystem, package: f.package || null,
|
|
4768
|
+
currentVersion: f.version || null, targetVersion: null, command: null,
|
|
4769
|
+
reason: r.ok ? null : (r.reason || 'no upgrade could be planned'),
|
|
4770
|
+
languageUpgrade: { status: r.status || null, tier: r.tier || null, label: r.label || null, file: r.file || null, diff: r.diff || null, explanation: r.explanation || null, consequences: r.consequences || [], backup: r.backup ? r.backup.id || null : null },
|
|
4771
|
+
verified: false,
|
|
4772
|
+
};
|
|
4773
|
+
}
|
|
4774
|
+
|
|
4731
4775
|
// ─── synthesize_sca_upgrade ───────────────────────────────────────────────
|
|
4732
4776
|
// Phase 3 / Item 5 of the SCA improvement plan. Read-only counterpart to
|
|
4733
4777
|
// apply_sca_upgrade — produces a structured upgrade plan via the
|
|
@@ -4756,6 +4800,10 @@ const synthesize_sca_upgrade = {
|
|
|
4756
4800
|
if (f.type !== 'vulnerable_dep') {
|
|
4757
4801
|
return { _meta: META, ok: false, reason: 'finding is not an SCA vulnerable_dep — use synthesize_fix for SAST findings' };
|
|
4758
4802
|
}
|
|
4803
|
+
// Hackage constraints and flake inputs are source edits the language planner owns; without this the generic planner
|
|
4804
|
+
// answers "no automated upgrade" for a dependency the CLI can fix. Read-only: nothing is written or run.
|
|
4805
|
+
const lc = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 52603));
|
|
4806
|
+
if (lc.isLanguageDependencyFinding(f)) return { _meta: META, ..._languageUpgradeResult(f, await _languageUpgrade(lc, f, ctx, false)) };
|
|
4759
4807
|
const { planScaUpgrade } = await _getScaUpgrade();
|
|
4760
4808
|
const plan = await planScaUpgrade({ scanRoot: ctx.sessionRoot, finding: f });
|
|
4761
4809
|
return { _meta: META, ...plan };
|
|
@@ -4796,6 +4844,15 @@ const apply_sca_upgrade = {
|
|
|
4796
4844
|
if (f.type !== 'vulnerable_dep') {
|
|
4797
4845
|
return { _meta: META, applied: false, reason: 'finding is not an SCA vulnerable_dep — use apply_fix for SAST findings' };
|
|
4798
4846
|
}
|
|
4847
|
+
const lc = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 52603));
|
|
4848
|
+
if (lc.isLanguageDependencyFinding(f)) {
|
|
4849
|
+
// Plan first, confine the one file it names, then write it with a backup and a history entry (`undo` reverts it).
|
|
4850
|
+
const plan = await _languageUpgrade(lc, f, ctx, false);
|
|
4851
|
+
if (!plan.ok) return { _meta: META, applied: false, ..._languageUpgradeResult(f, plan) };
|
|
4852
|
+
try { _confine(ctx.sessionRoot, plan.file, 'upgrade.file'); } catch (e) { return { _meta: META, applied: false, reason: String((e && e.message) || e) }; }
|
|
4853
|
+
const done = await _languageUpgrade(lc, f, ctx, true);
|
|
4854
|
+
return { _meta: META, applied: done.applied === true, ..._languageUpgradeResult(f, done), testsRun: false, testsNote: 'a constraint edit is not re-resolved or re-tested by this tool; resolve (for example `cabal build --dry-run`), rescan and run the project tests before relying on it' };
|
|
4855
|
+
}
|
|
4799
4856
|
const { applyScaUpgrade } = await _getScaUpgrade();
|
|
4800
4857
|
const result = await applyScaUpgrade({ scanRoot: ctx.sessionRoot, finding: f, runTests: run_tests });
|
|
4801
4858
|
return { _meta: META, ...result };
|