@clear-capabilities/agentic-security-scanner 0.195.25 → 0.195.27
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 +13 -3
- package/dist/396.index.js +936 -0
- package/dist/5752.index.js +15 -2
- package/dist/7838.index.js +22 -0
- package/dist/8218.index.js +19 -2
- package/dist/{809c900a90284017aaff.json → 90ebba38d8860ec77376.json} +4 -4
- package/dist/9382.index.js +315 -174
- package/dist/9560.index.js +19 -2
- package/dist/agentic-security.mjs +9 -9
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +4 -4
- package/src/context-intelligence/CLAUDE.md +4 -0
- package/src/context-intelligence/entrypoints.js +141 -9
- package/src/context-intelligence/imports.js +1 -1
- package/src/context-intelligence/review.js +8 -3
- package/src/engine.js +10 -0
- package/src/lineage/CLAUDE.md +4 -0
- package/src/lineage/graph-builder.js +8 -0
- package/src/lineage/index.js +2 -0
- package/src/posture/CLAUDE.md +10 -2
- package/src/posture/custom-rules.js +7 -2
- package/src/posture/dag-commands.js +15 -2
- package/src/posture/provenance/coordinator.js +6 -2
- package/src/posture/provenance/dag-walk.js +7 -3
- package/src/posture/provenance/git-evidence.js +21 -0
- 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/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/server/graph-loader.js +19 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,33 @@
|
|
|
9
9
|
> make the history less accurate, not more.
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
## 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
|
|
13
|
+
|
|
14
|
+
**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.
|
|
15
|
+
|
|
16
|
+
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.
|
|
17
|
+
|
|
18
|
+
- **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.
|
|
19
|
+
- **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.
|
|
20
|
+
- **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.
|
|
21
|
+
- **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.
|
|
22
|
+
- **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`.
|
|
23
|
+
- **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`.
|
|
24
|
+
|
|
25
|
+
## 0.195.26 - GA programme, release F: composed router mounts, stale citations refused, frozen graph artifact contracts with a revision check
|
|
26
|
+
|
|
27
|
+
**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.
|
|
28
|
+
|
|
29
|
+
Three items of the GA plan's graph workstream that are code and tests: GA-03.2 (route-mount composition and hash-bound citations in context intelligence), GA-03.7 (frozen contracts for the graph artifacts, with wrong-revision refusal and partial-state propagation), and GA-03.6 (the incremental 3x target, measured and not met, with the reason recorded). Nothing moved a maturity.
|
|
30
|
+
|
|
31
|
+
- **Router mounts compose.** `router.get('/:id', h)` with `app.use('/api', auth, router)` is now the entry point `/api/:id`, not `/:id` with a `router-mount-not-resolved` reason. A mount is found in the same file, through the import map (`import users from './users.js'`, `const users = require('./users')`) or an inline `require('./users')`, when the target module exports its router receiver; mounts compose through intermediate routers (`/api` then `/users` then `/:id`), a router mounted at several prefixes is one entry point per prefix, and the mount statement's other identifier arguments are inherited middleware scoped to that mount and that mount only. A dynamic prefix mounts nothing and the route keeps its honest reason. The record keeps `registeredPath` beside the composed `pathTemplate` and carries the `mount` chain. Measured on `bench/dci-real-app` (committed outputs regenerated): the mounted-router case `GET /api/orders/:id` moved from unresolved to resolved, 26 resolved and 1 unresolved of 27 across the cases, precision and recall unchanged at 100%.
|
|
32
|
+
- **A stale citation is refused.** A review citation inside a context segment whose lines no longer hash to the segment's `spanHash` is declined as `stale-context`, beside the existing refusals for a citation outside the provided context or not grounded in its lines; a manifest validated against the current snapshot already reported `stale-span`, and now the review answers the same way.
|
|
33
|
+
- **Frozen artifact contracts** (`docs/reference/code-boundaries-contract.md`, `scanner/test/contracts/code-boundaries.contract.test.js`). The signed lineage graph, witness bundles, boundary contracts, context manifests, sink query keys and cut plans each have a field table the test holds in both directions, and each rule is exercised for real: a tampered or unsigned graph is refused by the one loader; a witness bundle verifies as `replayed` against its artifacts, `integrity-only` without them, and `invalid` when tampered, signed under another key or replayed against another graph; a boundary contract's digest covers its meaning and not its declarer; a context manifest with a changed span is stale and a forged one fails its hash; a sink query key changes with the graph and configuration digests; a cut plan is hypothetical and never verified. The `code-boundaries`, `graph-precision` and `context-intelligence` registry rows now name the contract page, the contract test, the limits section and a `since`, so `check-ga-contracts` runs the test on every release; the remaining GA-bar gaps on those Preview rows are a gate and hosted platform jobs.
|
|
34
|
+
- **The graph records its revision and a consumer can refuse another.** A scan inside a git repository writes `graph.scope.revision = { commit, worktreeDirty }` (and the graph id depends on the commit); `dataflow view --expect-revision <sha|HEAD>` refuses with `wrong-revision` a graph built from another commit, and also one that records no revision, since it cannot be shown to match. A build without a commit is byte-identical to before.
|
|
35
|
+
- **Incremental 3x end to end: measured, not met, reason recorded.** On the 300x8 synthetic workload the warm IR stage is about 165 ms against a 350 ms cold parse; the IR cache's load path is 79 ms of that (JSON parsing 41 ms is its floor; V8 deserialization of the same records measured slower, 99 ms) and the rest is the call graph build and resolution, rebuilt on every scan. With the deep stage already at 3x, end to end reaches 3x only if the warm IR stage drops to about 120 ms, which needs an incremental call graph rather than a cache tweak. The invalidation cases the plan lists (edit, rename, deletion, dependency, configuration, model) are already pinned against a cold scan; listed under open items on the DAG status page.
|
|
36
|
+
|
|
37
|
+
Gates, from the runs recorded for this entry: `test:lineage` 1590/1590 (two new files, `boundary-join`'s sibling `code-boundaries.contract.test.js` and `route-mounts.test.js` in their scopes), `test:discovery` 196/196, the dataflow command tests 19/19, full `npm test` 12660 pass, 1 fail, 35 skipped, where the one failure is `egress-transport.test.js`'s wall-budget case measuring 0 ms spent against a 1 ms budget under full-suite load and passing three of three runs alone (a timing flake in a file this release does not touch); `bench:mutation:check` 460/460; `bench:self-scan:check` no drift; `check-doc-drift --gate` exit 0; `bench:dci-real-app:check` current after regeneration.
|
|
38
|
+
|
|
12
39
|
## 0.195.25 - GA programme, release E: the lineage graph joins a queue publish to its receive and a store write to its read
|
|
13
40
|
|
|
14
41
|
**Measured, 0.195.24:** `TIMINGS preflight=3s push=7s pr=4s checks=355s merge=6s verify=6s tag=2s release=162s npm=267s total=812s release-clock=176s`, shipped from the merge to the published release in 176 s; the npm listing lagged the publish and `ship --resume` confirmed it.
|
package/bin/agentic-security.js
CHANGED
|
@@ -1790,7 +1790,7 @@ async function cmdRules(args) {
|
|
|
1790
1790
|
// Self-learning finding-to-rule compiler (PRD 3): synthesize/evaluate/promote/status/rollback a DETECTION rule
|
|
1791
1791
|
// from one confirmed finding — distinct from validate/lock below (rules.yml override linting and pack locking).
|
|
1792
1792
|
// 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') {
|
|
1793
|
+
if (sub === 'synthesize' || sub === 'evaluate' || sub === 'promote' || sub === 'status' || sub === 'rollback' || sub === 'deprecate' || sub === 'cost' || sub === 'regress') {
|
|
1794
1794
|
return (await import('../src/posture/rule-compiler/cli.js')).cmdRules(args, path.resolve(args.flags.root || '.'));
|
|
1795
1795
|
}
|
|
1796
1796
|
const target = path.resolve(args._[2] || '.');
|
|
@@ -1807,7 +1807,7 @@ async function cmdRules(args) {
|
|
|
1807
1807
|
console.log(` scanner: ${lock.scannerVersion} rulePackHash: ${lock.rulePackHash}`);
|
|
1808
1808
|
return 0;
|
|
1809
1809
|
}
|
|
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;
|
|
1810
|
+
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
1811
|
}
|
|
1812
1812
|
|
|
1813
1813
|
// `agentic-security secure [--launch]` — smart router. One command picks the
|
|
@@ -1970,7 +1970,8 @@ async function cmdRule(args) {
|
|
|
1970
1970
|
if (!glob) { console.error('Usage: agentic-security rule test <fixture-glob>'); return 4; }
|
|
1971
1971
|
const target = path.resolve(args.flags.root || '.');
|
|
1972
1972
|
const r = await runRuleTests(target, glob);
|
|
1973
|
-
|
|
1973
|
+
// 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.
|
|
1974
|
+
return r.ok ? 0 : 1;
|
|
1974
1975
|
}
|
|
1975
1976
|
if (sub === 'list') {
|
|
1976
1977
|
const target = path.resolve(args.flags.root || '.');
|
|
@@ -6745,6 +6746,15 @@ async function cmdFederateDeclare(args) {
|
|
|
6745
6746
|
// hash-equivalent to a graph that never had the field at all,
|
|
6746
6747
|
// regardless of how the hashing internals treat undefined values.
|
|
6747
6748
|
const { crossRepoLinks: _omitForDigest, ...localGraphForDigest } = local.graph;
|
|
6749
|
+
// GA-03.7 (0.195.26): the graph also records the scanned revision
|
|
6750
|
+
// (`scope.revision`, with a dirty flag that flips on any untracked
|
|
6751
|
+
// file). The commit is already part of the graph id; the dirty flag is
|
|
6752
|
+
// not part of what the link names, so the revision record is left out of
|
|
6753
|
+
// this digest for the same reason crossRepoLinks is.
|
|
6754
|
+
if (localGraphForDigest.scope && localGraphForDigest.scope.revision) {
|
|
6755
|
+
const { revision: _omitRevision, ...scopeForDigest } = localGraphForDigest.scope;
|
|
6756
|
+
localGraphForDigest.scope = scopeForDigest;
|
|
6757
|
+
}
|
|
6748
6758
|
const localGraphDigest = computeGraphDigest(localGraphForDigest);
|
|
6749
6759
|
const idInputs = {
|
|
6750
6760
|
localGraphId: local.graph.graphId, localGraphDigest, localNodeId: localNodeFlag,
|