@clear-capabilities/agentic-security-scanner 0.195.26 → 0.195.28
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 +27 -0
- package/bin/agentic-security.js +39 -7
- package/dist/396.index.js +936 -0
- package/dist/{60f58f89664e53fc2f96.json → 3eabd90d6d9c8c8dbbd9.json} +4 -3
- package/dist/5856.index.js +634 -0
- package/dist/7033.index.js +74 -14
- package/dist/7838.index.js +22 -0
- package/dist/9220.index.js +24 -1
- package/dist/agentic-security.mjs +9 -9
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +4 -3
- package/src/dataflow/catalog.js +42 -1
- package/src/engine.js +90 -10
- package/src/language/CLAUDE.md +6 -0
- package/src/language/assurance.js +53 -2
- package/src/language/haskell-budget-state.js +85 -0
- package/src/language/haskell-coverage.js +230 -0
- package/src/language/haskell-project-models.js +107 -0
- package/src/language/nix-eval-isolation.js +74 -14
- package/src/llm-validator/bounded-context.js +87 -0
- package/src/llm-validator/claim-groups.js +105 -0
- package/src/llm-validator/disposition.js +264 -0
- package/src/llm-validator/index.js +52 -26
- package/src/pipeline/evidence-record.js +112 -0
- package/src/pipeline/scan-health.js +12 -1
- package/src/posture/CLAUDE.md +10 -2
- package/src/posture/artifact-registry.js +2 -0
- package/src/posture/custom-rules.js +7 -2
- package/src/posture/drift.js +19 -1
- package/src/posture/provenance/coordinator.js +6 -2
- package/src/posture/provenance/dag-walk.js +7 -3
- package/src/posture/provenance/origin-resolver.js +16 -2
- package/src/posture/provenance/providers/github.js +6 -3
- package/src/posture/provenance/providers/gitlab.js +5 -3
- package/src/posture/reachability-filter.js +3 -3
- package/src/posture/rule-compiler/cli.js +76 -21
- package/src/posture/rule-compiler/negative-corpus.js +39 -0
- package/src/posture/rule-compiler/promotion.js +21 -0
- package/src/posture/rule-compiler/regression.js +39 -9
- package/src/posture/rule-compiler/store.js +72 -6
- package/src/posture/suppressions.js +21 -0
- package/src/posture/watch-mode.js +22 -1
- package/src/report/disposition-view.js +90 -0
- package/src/report/index.js +47 -0
- package/src/runScan.js +60 -6
- package/src/sast/oidc-trust.js +74 -0
- package/src/sast/pipeline.js +14 -1
- package/src/sast/workflow-flow.js +272 -0
- package/src/sca/basis.js +88 -0
- package/src/scope/flags.js +55 -0
- package/src/scope/inventory.js +241 -0
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,33 @@
|
|
|
9
9
|
> make the history less accurate, not more.
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
## 0.195.28 - Git-aware scope, Haskell coverage accounting, and findings that keep their evidence
|
|
13
|
+
|
|
14
|
+
**Measured, 0.195.27:** `TIMINGS preflight=4s push=8s pr=4s checks=15s merge=6s verify=6s tag=2s release=131s npm=268s total=444s release-clock=145s`, shipped from the merge to the published release in 145 s.
|
|
15
|
+
|
|
16
|
+
The buildable part of the Haskell coverage and reliable findings plan (FR 01 to FR 03 and FR 05 to FR 11). FR 04's independently labelled benchmark and the pilot measurements are not in this release: every Haskell family still reports `insufficient-cases`. Nothing moved a maturity. Three defaults change, listed first.
|
|
17
|
+
|
|
18
|
+
- **Default changes.** A dependency finding whose reachability is unknown (`manifest-only`, `transitive-only`) keeps its severity; only an `unreachable` function or a `build-only` dependency lowers it. A model `reject` no longer removes a finding: each finding carries a disposition (`confirmed`, `needs-review`, `refuted`, `hardening`, `unvalidated`), and only evidence or a reviewed suppression refutes. Findings from different detectors about the same weakness at the same line are grouped under one primary with the others listed in `claims`, and the report reconciles raw, grouped, refuted and disposition counts (`candidateReconciliation`); drift compares grouped members too, so grouping never shows a finding as fixed.
|
|
19
|
+
- **Git-aware scope (FR 01).** The scan inventory follows Git: an ignored file is never opened (an instrumented test proves it), a tracked file under a default-skipped directory such as `dist/` or `vendor/` stays eligible, submodules and nested repositories are reported as unscanned units, and `--explain-scope` says why each path is in or out. A finding outside a narrower scan's coverage is out of scope, never fixed. `scan --watch` keeps the scope flags on every rescan and asks the same inventory, rebuilt when `.gitignore` changes.
|
|
20
|
+
- **Haskell coverage (FR 02, FR 03).** Coverage is accounted by stage, package and rule family, listing completed and remaining files. `AGENTIC_SECURITY_HASKELL_PARSE_BUDGETS` raises named parse bounds (unknown names are refused and reported), and `.agentic-security/haskell-budget.json` records the work left so the next scan states what completed, what will hit the same bound again and what changed. There is no resume: every scan re-parses every file. Reviewed project models describe wrappers, and are matched only by the scan of their own root.
|
|
21
|
+
- **Model validation (FR 05).** The model sees numbered spans (the finding, source, sink, path steps, related Haskell and Nix files), each redacted under its own path, plus a stated list of what was not collected (callers, configuration). It must cite span lines: a citation outside what it was shown voids the reply (`escalate`), no citation is recorded as missing evidence, and valid citations are evidence that never confirms on its own. Prompt version `v2.1-cited`, so cached verdicts are not reused. Accepted suppressions are bound to a content fingerprint and reopen when the code changes.
|
|
22
|
+
- **Workflows (FR 06).** GitHub Actions files are parsed as YAML: composite actions, `github-script`, inputs to unknown actions, `workflow_run` artifacts, agent actions with write authority on untrusted triggers, and OIDC trust policy evidence (`restrictive`, `permissive`, `mismatched`, `absent`) with this repository's name taken from its `origin` remote. Reference: `docs/reference/workflow-analysis.md`.
|
|
23
|
+
- **Linux isolated Nix evaluation, tested for the first time.** The hosted runner image moved to Ubuntu 26.04, which ships `bwrap`, so the NIX-011 suite exercised the Linux sandbox instead of reporting `unsupported`, and found three defects. Namespaces did not stop process creation: the probe spawned `/bin/echo`, so evaluation was correctly refused as `blocked`. A seccomp filter now denies fork, vfork and non-thread clone (threads still work) on x86_64 and arm64; another architecture gets no filter, and the probe refuses it. `--clearenv` dropped the clean environment, so the evaluator ran with no `HOME`, `PATH` or `LANG`; it is now set inside the sandbox. The memory ceiling summed the process group, which `--new-session` leaves, so a memory balloon was never counted; it now sums the process tree. On 26.04 the GHC 9.4.8 bindist still asks for the `gold` linker (in its settings and in the `hsc2hs` wrapper), which binutils no longer ships; CI now removes the flag from the GHC install when gold is absent.
|
|
24
|
+
- **Noise, evidence and views (FR 07 to FR 10).** Comment-only developer notes and missing-control findings are classified (`hardening`); dependency findings record their basis and merge advisory aliases with every occurrence kept; JSON output carries a scan record, finding record and disposition; `--exclude-kind` and view filters change what is shown, never the stored evidence.
|
|
25
|
+
|
|
26
|
+
## 0.195.27 - GA programme, release G: provenance adverse histories with a frozen contract and an absolute latency budget, promoted-rule regressions that run in CI
|
|
27
|
+
|
|
28
|
+
**Measured, 0.195.26:** `TIMINGS preflight=3s push=7s pr=4s checks=393s merge=6s verify=5s tag=2s release=161s npm=636s total=1217s release-clock=174s`, shipped from the merge to the published release in 174 s; the npm listing lagged the publish and `ship --resume` confirmed it.
|
|
29
|
+
|
|
30
|
+
Two items of the GA plan that are code and tests: GA-05 (adverse Git histories in the origin corpus, the provenance contract frozen, the old ratio target replaced by an absolute budget) and GA-07 §2 and §3 (promoted-rule regressions that run from every CI template on a machine that never promoted the rule, a shared negative corpus, the reviewed scope and evidence recorded). Nothing moved a maturity.
|
|
31
|
+
|
|
32
|
+
- **Ten adverse histories in the origin corpus, 23 of 23 resolved** (`bench/provenance-accuracy`, `npm run bench:provenance-accuracy:check`, in the pre-push gate). A revert of a fix (bounded at the first introduction, the origin is the revert and `findingOrigin.revertOf` names what it reverted), a cherry-pick with `-x` (`cherryPickOf` names the source), a squash merge, an amended introducing commit, a `git mv` after the introduction, a dirty file whose vulnerable line is committed (origin resolved, `analysisBasis.dirty` set), a merge whose first parent carries the flaw, a conflict resolution that writes the flaw, a transitive lockfile bump (`express` to `qs`, the chain re-derived at the origin commit) and a configured forge provider that cannot be reached. A fixture may now declare `mode: 'deep'` and hand the scan an environment (a `--provenance-since` boundary at one of its own commits) and a `verify` of the record beyond the origin commit, and the corpus runs the deep-mode code it never ran before.
|
|
33
|
+
- **Two provenance fixes the corpus found.** Deep mode's revert and cherry-pick annotation ran only in the retry after a standard-walk miss, so a resolved origin (the common case) never carried `revertOf` or `cherryPickOf`; it is applied to every origin deep mode resolves. A revert of a revert is titled `Reapply "..."` by git since 2.43 and was not recognised as a revert; the diff inversion still decides. And a provider that erred or did not answer returned the same `null` as "no pull request names this commit"; it now returns `{unavailable, reason}` and the coordinator records a limitation naming the provider and the reason, with the origin, status and confidence untouched.
|
|
34
|
+
- **The provenance contract is frozen** (`docs/reference/finding-provenance-contract.md`, `scanner/test/contracts/finding-provenance.contract.test.js`, in `test:posture` and run by `check-ga-contracts` on every release). The record and its parts field by field, the six terminal states, the fifteen confidence reason codes (derived from the sources by the test, so a new code must be documented), the eleven eligibility categories behind the coverage figure, the identity rules (email withheld, stable pseudonyms), the default behaviour per command, the shared timeout (60 s, 300 s deterministic, a 2 s per-finding floor, 20 provider calls at 8 s each) and the latency budget; exercised for real on git fixtures: a shallow clone is `partial`, a root commit is `complete` capped at `medium`, an uncommitted line is `uncommitted`, an expired budget is `budget_exhausted` and not cached, `scan` leaves provenance off until a flag asks. The `finding-provenance` registry row now carries the contract, the contract test, `gate: provenance-gate`, the limits section and `since`; its remaining GA-bar gap is a hosted platform job.
|
|
35
|
+
- **An absolute latency budget replaces the 1.3x ratio target** (`bench/provenance`, schema `provenance/v4`). The bench records the wall time provenance ADDS to the same iteration's provenance-disabled scan and holds its p95 to a budget frozen in the runner with its derivation (cold 2000 ms, warm 250 ms on the 15-finding, 45-commit fixture), beside the ratio gates; a re-baseline cannot loosen it. Recorded on the maintainer host (60 iterations): cold added p50 1196 ms, p95 1215 ms; warm added p50 39 ms, p95 49 ms; the base scan is 189 ms (it was 44 ms when the baseline was recorded in August, a four-fold change in the engine's own cost on this fixture since, recorded here and not explained by this release). The guide now carries the per-finding figure (about 80 ms cold) as the number to budget with.
|
|
36
|
+
- **Promoted-rule regressions run in CI, on any machine** (`rules regress`). Every CI template (GitLab, CircleCI, Buildkite, Jenkins, which also moves to Node 24) and the GitHub Actions workflow `/setup --ci` generates run it, before the scan gate so a failing gate cannot hide a rule regression; the template test asserts the order and the key variable. `rules promote --sign-key` now also signs the lifecycle ledger with the operator's Ed25519 key (`ledger.json.author.sig`, renewed by a later command run with the same key) and prints the public key; on a machine whose per-install HMAC key differs, every `rules` command accepts the ledger when its chain verifies and the author signature verifies against `AGENTIC_SECURITY_RULES_PUBLIC_KEY` (a CI variable, never a file in the checkout) or the rule-pack trust store, and refuses it otherwise, including with the last entry dropped. `rules regress --json` reports `ledgerVerifiedBy`.
|
|
37
|
+
- **A shared negative corpus, and the reviewed scope and evidence.** The scanner ships 27 safe snippets across nine languages (`rule-compiler/negative-corpus.js`, the same everywhere); they sit in front of the shared false-positive corpus with the other promoted rules' safe code, the project's `fp-corpus/` and directories a fleet shares through `AGENTIC_SECURITY_RULES_FP_CORPUS`, and evaluation, promotion and regression refuse a rule that fires on any of them. `--scope` is required at promotion, and the promotion event and ledger entry record the evidence the reviewer looked at (digests of the evaluation, the provenance and both corpus halves); `regress` re-derives the corpus digests and reports a rule whose corpus changed after the review. An "independent" example that is the original snippet, its patch or a generated mutation (whitespace aside) is refused as not independent. `rules deprecate` retires a promoted rule (the ledger's terminal edge, which no command reached), a rolled-back rule re-enters as a candidate with its history kept (the edge existed and `synthesize` could not take it), and `rule test` exits 1 on a missed vulnerable fixture or a hit on a clean one (it exited 0 whatever the fixtures said). The `rule-compiler` registry row gains `gate`, `limits` and `since`.
|
|
38
|
+
|
|
12
39
|
## 0.195.26 - GA programme, release F: composed router mounts, stale citations refused, frozen graph artifact contracts with a revision check
|
|
13
40
|
|
|
14
41
|
**Measured, 0.195.25:** `TIMINGS preflight=3s push=7s pr=5s checks=433s merge=6s verify=6s tag=2s release=223s npm=237s total=922s release-clock=237s`, shipped from the merge to the published release in 237 s; the npm listing lagged the publish and `ship --resume` confirmed it.
|
package/bin/agentic-security.js
CHANGED
|
@@ -11,6 +11,9 @@ const PKG_VERSION = __require('../package.json').version;
|
|
|
11
11
|
import { signLastScan as _signLastScan, verifyLastScan as _verifyLastScanShared } from '../src/posture/integrity.js';
|
|
12
12
|
import { isProvenanceHealthy, sanitizeForTerminal } from '../src/posture/provenance/schema.js';
|
|
13
13
|
import { runScan } from '../src/runScan.js';
|
|
14
|
+
import { parseScopeFlags, renderScopePreview } from '../src/scope/flags.js';
|
|
15
|
+
import { buildDispositionView, parseExcludeKinds } from '../src/report/disposition-view.js';
|
|
16
|
+
import { buildScanInventory } from '../src/scope/inventory.js';
|
|
14
17
|
import { persistGraphSnapshot, loadSnapshots, loadSnapshot, mostRecentPriorSnapshot } from '../src/lineage/graph-snapshot.js';
|
|
15
18
|
|
|
16
19
|
// Every command is dispatched as `process.exit(await cmdX(args))`, and
|
|
@@ -339,6 +342,9 @@ Options:
|
|
|
339
342
|
--pr [ref] Diff-aware: scan only files changed since ref (auto-detects PR base)
|
|
340
343
|
--deterministic Reproducible scan: stable sort, no-network, lockfile-checked
|
|
341
344
|
--incremental Reuse taint summaries from prior scans (speeds up deep mode in CI)
|
|
345
|
+
--explain-scope Preview included/skipped paths and ignore reasons, then exit (also --include-path, --exclude-path, --include-ignored-path, --ignore-source all|repository, --no-respect-gitignore)
|
|
346
|
+
--exclude-kind <k[,k]> Hide sca, generated, secrets or logic findings from the report view only (still stored; not marked assessed)
|
|
347
|
+
--view-category/-language/-package/-path/-disposition <v>, --view-min-confidence <n> Filter the report view
|
|
342
348
|
--set-baseline Save current findings as baseline (suppresses pre-existing issues)
|
|
343
349
|
--since-baseline Only show findings NOT in the saved baseline
|
|
344
350
|
--hide-proven-safe Drop findings discharged by a flow proof (provably safe)
|
|
@@ -551,6 +557,7 @@ async function writeMachineOutput(targetAbs, scan, meta, profile, args) {
|
|
|
551
557
|
}
|
|
552
558
|
}
|
|
553
559
|
|
|
560
|
+
const SCOPE_BOOLEAN_FLAGS = new Set(['explain-scope', 'respect-gitignore', 'no-respect-gitignore']);
|
|
554
561
|
function parseArgs(argv) {
|
|
555
562
|
const args = { _: [], flags: {} };
|
|
556
563
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -559,6 +566,8 @@ function parseArgs(argv) {
|
|
|
559
566
|
const [k, v] = a.slice(2).split('=', 2);
|
|
560
567
|
if (v !== undefined) { args.flags[k] = v; continue; }
|
|
561
568
|
const next = argv[i + 1];
|
|
569
|
+
// Scope switches never take a value, so `--explain-scope .` keeps `.` as the target.
|
|
570
|
+
if (SCOPE_BOOLEAN_FLAGS.has(k)) { args.flags[k] = true; continue; }
|
|
562
571
|
if (next && !next.startsWith('--')) { args.flags[k] = next; i++; }
|
|
563
572
|
else args.flags[k] = true;
|
|
564
573
|
} else {
|
|
@@ -688,6 +697,14 @@ async function cmdScan(args) {
|
|
|
688
697
|
console.error(`agentic-security scan: "${target}" does not exist (resolved to ${targetAbs}); nothing was scanned.`);
|
|
689
698
|
process.exit(4);
|
|
690
699
|
}
|
|
700
|
+
// FR 01: Git-aware scope flags. Any of them opts this run into the inventory-based scope;
|
|
701
|
+
// --explain-scope previews it and exits before any analysis or file read.
|
|
702
|
+
const scopeFlags = parseScopeFlags(process.argv.slice(2));
|
|
703
|
+
if (scopeFlags.errors.length) { for (const e of scopeFlags.errors) process.stderr.write(`agentic-security scan: ${e}\n`); return 2; }
|
|
704
|
+
if (scopeFlags.explain) {
|
|
705
|
+
process.stdout.write(renderScopePreview(buildScanInventory(targetAbs, scopeFlags.scope)));
|
|
706
|
+
return 0;
|
|
707
|
+
}
|
|
691
708
|
// Load persona profile (R1). Persona-aware defaults flow from here.
|
|
692
709
|
const profile = loadPersonaProfile(targetAbs, args);
|
|
693
710
|
const format = args.flags.format || (profile.profile === 'pro' ? 'cli' : 'ship');
|
|
@@ -722,14 +739,18 @@ async function cmdScan(args) {
|
|
|
722
739
|
// normal one-shot scan.
|
|
723
740
|
if (args.flags['watch']) {
|
|
724
741
|
process.env.AGENTIC_SECURITY_INCREMENTAL = '1';
|
|
725
|
-
const { watchProject, computeDelta, persistStatus, renderStatusLine } = await import('../src/posture/watch-mode.js');
|
|
742
|
+
const { watchProject, computeDelta, persistStatus, renderStatusLine, createScopeFilter } = await import('../src/posture/watch-mode.js');
|
|
726
743
|
process.stderr.write(`[watch] scanning ${targetAbs} on change — Ctrl-C to stop. Status → .agentic-security/watch-status.md\n`);
|
|
727
|
-
|
|
744
|
+
// The scope flags apply to the first scan, every rescan and the watcher itself (PRD FR 01): the watcher asks the same
|
|
745
|
+
// inventory, so an ignored file never triggers a rescan and an eligible one always does.
|
|
746
|
+
const _watchScoped = scopeFlags.active || process.env.AGENTIC_SECURITY_GIT_SCOPE === '1';
|
|
747
|
+
const _watchScanOpts = scopeFlags.active ? { gitScope: true, scope: scopeFlags.scope } : {};
|
|
748
|
+
const seed = await runScan(targetAbs, { onProgress: scanProgressReporter(), ..._watchScanOpts });
|
|
728
749
|
clearScanProgressLine();
|
|
729
750
|
let prevFindings = seed.scan.findings || [];
|
|
730
751
|
await watchProject(targetAbs, async () => {
|
|
731
752
|
try {
|
|
732
|
-
const { scan } = await runScan(targetAbs, { onProgress: scanProgressReporter() });
|
|
753
|
+
const { scan } = await runScan(targetAbs, { onProgress: scanProgressReporter(), ..._watchScanOpts });
|
|
733
754
|
clearScanProgressLine();
|
|
734
755
|
const curr = scan.findings || [];
|
|
735
756
|
const delta = computeDelta(prevFindings, curr);
|
|
@@ -739,7 +760,7 @@ async function cmdScan(args) {
|
|
|
739
760
|
} catch (e) {
|
|
740
761
|
process.stderr.write(`[watch] rescan failed: ${e.message}\n`);
|
|
741
762
|
}
|
|
742
|
-
});
|
|
763
|
+
}, _watchScoped ? { isEligible: createScopeFilter(targetAbs, scopeFlags.active ? scopeFlags.scope : {}) } : {});
|
|
743
764
|
return 0; // watchProject blocks until aborted
|
|
744
765
|
}
|
|
745
766
|
|
|
@@ -797,7 +818,10 @@ async function cmdScan(args) {
|
|
|
797
818
|
|
|
798
819
|
const { scan, meta } = await runScan(target, {
|
|
799
820
|
changedSince,
|
|
821
|
+
invocation: process.argv.slice(2),
|
|
800
822
|
onProgress: scanProgressReporter(),
|
|
823
|
+
...(scopeFlags.active ? { gitScope: true, scope: scopeFlags.scope } : {}),
|
|
824
|
+
...(args.flags['haskell-source-map'] ? { haskellSourceMap: String(args.flags['haskell-source-map']) } : {}),
|
|
801
825
|
});
|
|
802
826
|
// --require-provenance: flag (never fail) any finding whose provenance
|
|
803
827
|
// isn't resolved, via scanHealth — deliberately independent of the
|
|
@@ -962,6 +986,13 @@ async function cmdScan(args) {
|
|
|
962
986
|
if (_line) process.stderr.write(`⚠️ agentic-security: ${_line}\n`);
|
|
963
987
|
}
|
|
964
988
|
} catch { /* reporting must never fail a scan */ }
|
|
989
|
+
// PRD FR 09: disposition view. Display only; stored buckets are not touched.
|
|
990
|
+
{ const ek = parseExcludeKinds(args.flags['exclude-kind']);
|
|
991
|
+
if (ek.errors.length) { for (const e of ek.errors) process.stderr.write(`agentic-security scan: ${e}\n`); return 2; }
|
|
992
|
+
const flt = {};
|
|
993
|
+
for (const k of ['category', 'language', 'package', 'path', 'disposition']) if (args.flags[`view-${k}`]) flt[k] = String(args.flags[`view-${k}`]);
|
|
994
|
+
if (args.flags['view-min-confidence'] !== undefined) flt.minConfidence = Number(args.flags['view-min-confidence']);
|
|
995
|
+
scan.reportView = buildDispositionView(scan, { excludeKinds: ek.kinds, filters: flt }); }
|
|
965
996
|
|
|
966
997
|
// Curated rule packs: --pack <name> (repeatable). Narrows findings to the
|
|
967
998
|
// CWEs covered by the requested pack(s).
|
|
@@ -1790,7 +1821,7 @@ async function cmdRules(args) {
|
|
|
1790
1821
|
// Self-learning finding-to-rule compiler (PRD 3): synthesize/evaluate/promote/status/rollback a DETECTION rule
|
|
1791
1822
|
// from one confirmed finding — distinct from validate/lock below (rules.yml override linting and pack locking).
|
|
1792
1823
|
// Dispatches to its own module rather than inlining here, same convention as `triage exploit`/`proof`.
|
|
1793
|
-
if (sub === 'synthesize' || sub === 'evaluate' || sub === 'promote' || sub === 'status' || sub === 'rollback' || sub === 'cost' || sub === 'regress') {
|
|
1824
|
+
if (sub === 'synthesize' || sub === 'evaluate' || sub === 'promote' || sub === 'status' || sub === 'rollback' || sub === 'deprecate' || sub === 'cost' || sub === 'regress') {
|
|
1794
1825
|
return (await import('../src/posture/rule-compiler/cli.js')).cmdRules(args, path.resolve(args.flags.root || '.'));
|
|
1795
1826
|
}
|
|
1796
1827
|
const target = path.resolve(args._[2] || '.');
|
|
@@ -1807,7 +1838,7 @@ async function cmdRules(args) {
|
|
|
1807
1838
|
console.log(` scanner: ${lock.scannerVersion} rulePackHash: ${lock.rulePackHash}`);
|
|
1808
1839
|
return 0;
|
|
1809
1840
|
}
|
|
1810
|
-
console.error('rules validate | rules lock | rules synthesize <finding-id> --spec <file.json> [--auto] | rules evaluate|promote|status|rollback|cost <rule-id>'); return 4;
|
|
1841
|
+
console.error('rules validate | rules lock | rules synthesize <finding-id> --spec <file.json> [--auto] | rules evaluate|promote|status|rollback|deprecate|cost <rule-id> | rules regress [rule-id]'); return 4;
|
|
1811
1842
|
}
|
|
1812
1843
|
|
|
1813
1844
|
// `agentic-security secure [--launch]` — smart router. One command picks the
|
|
@@ -1970,7 +2001,8 @@ async function cmdRule(args) {
|
|
|
1970
2001
|
if (!glob) { console.error('Usage: agentic-security rule test <fixture-glob>'); return 4; }
|
|
1971
2002
|
const target = path.resolve(args.flags.root || '.');
|
|
1972
2003
|
const r = await runRuleTests(target, glob);
|
|
1973
|
-
|
|
2004
|
+
// GA-07 §3 (0.195.27): a missed vulnerable fixture or a false positive on a clean one is a failed test (exit 1); it used to exit 0 always.
|
|
2005
|
+
return r.ok ? 0 : 1;
|
|
1974
2006
|
}
|
|
1975
2007
|
if (sub === 'list') {
|
|
1976
2008
|
const target = path.resolve(args.flags.root || '.');
|