@clear-capabilities/agentic-security-scanner 0.195.20 → 0.195.22
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 +5 -2
- package/dist/agentic-security.mjs +2 -2
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/{de59e9dc044605ecc699.json → c1e282247b06d51c8ffb.json} +3 -3
- package/package.json +3 -3
- package/src/discovery/CLAUDE.md +4 -0
- package/src/discovery/detector-spec-synthesis.js +203 -0
- package/src/engine.js +9 -0
- package/src/integrations/index.js +18 -9
- package/src/pipeline/scan-health.js +15 -2
- package/src/posture/CLAUDE.md +1 -0
- package/src/posture/assurance/config.js +2 -0
- package/src/posture/assurance/decision.js +45 -0
- package/src/posture/assurance/rollout.js +3 -0
- package/src/report/index.js +7 -6
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,33 @@
|
|
|
9
9
|
> make the history less accurate, not more.
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
## 0.195.22 - GA programme, release B: every model path through the egress policy, one assurance decision on every surface, and unknown is not fresh
|
|
13
|
+
|
|
14
|
+
**Measured, 0.195.21:** `TIMINGS preflight=3s push=5s pr=4s checks=393s merge=5s verify=6s tag=2s release=223s npm=145s total=786s release-clock=236s`, shipped in 14 min 56 s on the third attempt (the first stopped at verify on the new expected-jobs rule because the check-runs query had never been paginated; the second stopped at the warm gate because the CodeQL summary check is not a workflow job). The npm check was re-run once with `--resume`.
|
|
15
|
+
|
|
16
|
+
The safety and semantics items of the GA plan that are code (GA-07.4, GA-07.5, GA-11.7, its day 5 and day 6, and the two contract failures its review found), built on the 0.195.21 ledger.
|
|
17
|
+
|
|
18
|
+
- **Detector-spec synthesis goes through the shared bounded model call.** `scripts/synthesize-detector.mjs` posted its prompt with a bare `fetch`: no egress policy, no redaction, no deadline, a regular-expression JSON extractor that could not parse the nested array it required, and an output path written anywhere. It is now a thin front over `scanner/src/discovery/detector-spec-synthesis.js`: the `detector-spec-synthesis` assurance feature (off by default), the egress policy evaluated BEFORE any sample file is read (a denied call reads nothing), bounded reads, a CSPRNG nonce, `guardedModelCall` (redaction, request cap, deadline with bounded retries, audit entry, output cap), the string-aware JSON parser the validator uses, and a closed-world validation of the proposal (identifiers only, so no field can carry a pattern; unknown fields, a wrong nonce and a bad argument index are refused with reasons, never with the raw reply). A proposal is written only inside the working directory, never under `scanner/src`, never over a file without `--force`; nothing installs it. Exit codes 0, 2, 3, 4. Eleven pins with an injected transport, and the `model-egress-policy` contract test now covers this path.
|
|
19
|
+
- **The autopilot's fix proposals too.** `scripts/autopilot.mjs` was the second model path the 0.195.21 egress check found: its whole-file fix prompt now goes through the egress policy (a denial leaves the finding NEEDS_REVIEW), the redactor and the audit log. The `model-egress-policy:EGRESS-ALL-PATHS` ledger record moves from `failed` to `passed`.
|
|
20
|
+
- **One assurance decision on every surface.** `posture/assurance/decision.js`: `deployable`, `needs_review`, `not_deployable`, or `unknown` when no scan health was recorded. The CLI ship verdict, the Slack, Discord and Teams digests and the pull-request comment all derive their wording from it; `digest` reads the scan health the last scan recorded. A findings file with no scan health now reads "cannot confirm", never "safe to deploy", and a scan object without scan health in the tests was re-pinned to that. The default wording of the three existing states is unchanged (the bounded wording stays the `portfolio-assurance` opt-in).
|
|
21
|
+
- **Unknown feed freshness is a condition.** `freshness.<feed>.stale` is tri-state and documented so; `null` for a feed the scan consulted (`consulted` items for the KEV catalog, `cvesChecked` for EPSS) because it was unavailable, never loaded or cached without a timestamp is a condition like staleness, the result is `partial` and strict mode fails closed on it. A feed the scan had nothing to look up in is not a condition, and neither is `offline-skipped`, the operator's own declared configuration. The catalog metadata is reset per scan so one scan cannot inherit another's. The test that asserted a boolean, and the offline clean-scan pin, now assert the tri-state and the feed-only conditions.
|
|
22
|
+
- **A suppression pragma now reaches privacy-flow findings.** The self-scan of the new synthesis module showed that `agentic-security-ignore` on a credential-to-HTTP line was inert: the privacy flows (`dataflow/privacy-taint.js`) are appended after both pragma passes, against the documented rule that a stage appending findings runs the filter after itself. A third pass runs after the privacy stage; pinned both ways with a deep-mode fixture, and the suppression is logged like every other.
|
|
23
|
+
- **The `proof doctor` exit code is documented.** 0 when an execution environment is usable, 1 when none is (the report is still printed), `--json` always 0. The contract test that demanded 0 on every host, and so failed on one without a confinement backend, now pins the documented contract; the CLI's behaviour is unchanged.
|
|
24
|
+
|
|
25
|
+
## 0.195.21 - The GA programme ledger: every shipped surface owned, every acceptance record machine-checked, every expected hosted job required
|
|
26
|
+
|
|
27
|
+
**Measured, 0.195.20:** `TIMINGS preflight=3s push=6s pr=1s checks=363s merge=5s verify=5s tag=2s release=192s npm=267s total=844s release-clock=204s`, shipped in 15 min 53 s on the second attempt (the first stopped in the hosted checks: the version bump had regenerated the scorecard before the support registry was re-measured, so the two carried different measurement dates; the scorecard was regenerated on the clean tree). The npm check was re-run once with `--resume`.
|
|
28
|
+
|
|
29
|
+
The plan to bring every shipped capability to GA (the GA plan PRD, an untracked root working document) is a 24 to 32 week programme, most of which needs data, hardware, people and weeks. This release builds the part of it that is code, GA-01 (inventory and release evidence), and records the rest as unmet so that the distance is visible instead of assumed.
|
|
30
|
+
|
|
31
|
+
- **The acceptance ledger** (`docs/ga-program/ledger.json`, `scripts/ga-program/ledger.mjs`, guide `docs/ga-program/README.md`). One record per shipped registry capability (36: 19 GA, 15 Preview, 2 Labs), in the record shape and the six states the PRD's section 8 specifies, with the workstreams of its section 5 tables and the GA-12 surfaces as tracked subcapabilities under a parent capability. Mechanical records (the GA bar, evidence identity, a current-engine independent record, every model path through the egress policy) are computed from the committed artifacts at render time and never typed; a manual `passed` is counted only with a source commit, a command, an evidence path that exists and a date inside the 30 day review window. Owners are roles until a person is named; none is invented. The generated `STATUS.md` reports verified GA against the registry's GA: **0 of 36**, because no capability has a current-engine independent accuracy record with identity (the scorecard says unmeasured; the independent record is engine 0.141.0 with no commit or bundle digest), and says so.
|
|
32
|
+
- **Every shipped surface is owned.** `scripts/ga-program/inventory.mjs` reads 193 surfaces from the source that ships them (the CLI's dispatch switch, the format sets, `ALL_TOOLS`, slash commands, hooks, agents, skills, integration targets, ticket providers, distributions; environment variables are counted, not mapped) and the ledger maps each to a capability or a tracked subcapability. `ledger.mjs --check` fails on an orphan surface, a mapping whose surface no longer ships, a declared pass without evidence, a ledger that disagrees with the registry's maturity, a stale status page, or a documented count the checkout contradicts. It runs in `check-doc-drift --gate` (so in the hosted docs checks) and in `test:lifecycle`. Five documents and two plugin manifests said the MCP server has 17 tools; it registers 25. Corrected, and now gated.
|
|
33
|
+
- **A missing expected hosted job fails the release gate.** `evaluateRemoteCi` judged only the check runs that reported for the commit, so a blocking workflow that silently stopped triggering could not turn it red (a pure-function probe with one green job and one required-but-absent job returned ok). Every blocking name in `.github/required-checks.json` is now expected: one that did not report is an error unless the file declares it path-filtered (`applicability`; `dependency-currency`'s paths, pinned equal to the workflow's own filter) and none of the commit's changed paths matches, in which case the gate prints why it was skipped; unknown changed paths fail closed; `--allow-unverified-ci` downgrades it to the UNVERIFIED warning and the result records the bypass. Eleven negative and positive pins.
|
|
34
|
+
- **The all-GA milestone gate.** A new fast release check, `ga-milestone-gate`, evaluates the ledger on every release: not declared, every unmet reason is one warning (83 today); declared (`program.milestone.declared`), each is an error, and a release published with `--allow-unverified-ci` cannot claim the milestone. `release-prepare` now regenerates the inventory and the status page.
|
|
35
|
+
- **What the new rule found on its first release.** The first ship of this version stopped at the post-merge verify: seven blocking jobs "did not report" for a commit whose pull-request checks were all green. The cause was in the gate, not the workflows: the check-runs query had never been paginated, and the release commit had 38 check runs against a page size of 30, so every earlier release gate had judged only the first page without knowing it. The query is paginated now (one run per line across pages), the one unclassified check run (`CodeQL`, the code-scanning service's summary, not a workflow job) stays deliberately unlisted and is judged whenever it reports, and the rule is pinned against a parsed paginated output.
|
|
36
|
+
- **Side fixes.** `test:lifecycle` had been passing four test files (`workflow-confinement`, `check-bundle-source`, `ship`, `release-prepare`) to the description lint instead of to the test runner; the combined `npm test` still ran them through the union, the scoped script did not. They run in the scope now. The release-check header comments still said `--fast` skips three gates; it skips twenty.
|
|
37
|
+
- **What the mechanical checks found.** `egress-all-model-paths` names two scripts that send a prompt without evaluating the egress policy, `scripts/synthesize-detector.mjs` (the PRD's finding) and `scripts/autopilot.mjs` (a second one the check found); the `model-egress-policy` record is `failed` until the next release routes both through the shared bounded call. Nothing in this release changes a maturity label.
|
|
38
|
+
|
|
12
39
|
## 0.195.20 - Haskell remediation: the password-hash migration over the SHA package, and every refused class names its decision
|
|
13
40
|
|
|
14
41
|
**Measured, 0.195.19:** `TIMINGS preflight=3s push=7s pr=5s checks=683s merge=5s verify=5s tag=2s release=315s npm=145s total=1170s release-clock=327s`, shipped in 21 min 10 s (the npm check was re-run once with `--resume`; the registry listed the version on the second check).
|
package/bin/agentic-security.js
CHANGED
|
@@ -3578,14 +3578,17 @@ async function cmdDigest(args) {
|
|
|
3578
3578
|
const summary = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
|
|
3579
3579
|
for (const f of (last.findings || [])) summary[f.severity || 'medium']++;
|
|
3580
3580
|
const project = args.flags.project || path.basename(target);
|
|
3581
|
+
// The digest states the shared assurance decision, which needs the scan health the last scan recorded; a findings file alone says "cannot confirm".
|
|
3582
|
+
let scanHealth = null;
|
|
3583
|
+
try { const ls = statePath(target, 'last-scan.json'); if (fs.existsSync(ls)) scanHealth = JSON.parse(await fsp.readFile(ls, 'utf8')).scanHealth || null; } catch { scanHealth = null; }
|
|
3581
3584
|
if (args.flags.slack) {
|
|
3582
|
-
const payload = buildSlackDigest(findings, summary, { project });
|
|
3585
|
+
const payload = buildSlackDigest(findings, summary, { project, scanHealth });
|
|
3583
3586
|
const r = await postWebhook(args.flags.slack, payload);
|
|
3584
3587
|
console.log(r.ok ? `✓ Slack digest sent` : `✗ Slack failed: ${r.reason || r.status}`);
|
|
3585
3588
|
return r.ok ? 0 : 4;
|
|
3586
3589
|
}
|
|
3587
3590
|
if (args.flags.discord) {
|
|
3588
|
-
const payload = buildDiscordDigest(findings, summary, { project });
|
|
3591
|
+
const payload = buildDiscordDigest(findings, summary, { project, scanHealth });
|
|
3589
3592
|
const r = await postWebhook(args.flags.discord, payload);
|
|
3590
3593
|
console.log(r.ok ? `✓ Discord digest sent` : `✗ Discord failed: ${r.reason || r.status}`);
|
|
3591
3594
|
return r.ok ? 0 : 4;
|