@blamejs/exceptd-skills 0.18.6 → 0.18.8

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 (63) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/bin/exceptd.js +364 -119
  3. package/data/_indexes/_meta.json +22 -3
  4. package/data/cve-catalog.json +25 -0
  5. package/data/playbooks/framework.json +2 -2
  6. package/data/playbooks/post-quantum-migration.json +1 -1
  7. package/lib/auto-discovery.js +30 -10
  8. package/lib/collectors/ai-api.js +9 -2
  9. package/lib/collectors/cicd-pipeline-compromise.js +24 -5
  10. package/lib/collectors/cred-stores.js +17 -4
  11. package/lib/collectors/crypto.js +9 -2
  12. package/lib/collectors/hardening.js +9 -2
  13. package/lib/collectors/library-author.js +29 -5
  14. package/lib/collectors/mcp.js +9 -2
  15. package/lib/collectors/runtime.js +9 -2
  16. package/lib/collectors/sbom.js +28 -15
  17. package/lib/collectors/scan-excludes.js +25 -0
  18. package/lib/collectors/secrets.js +40 -4
  19. package/lib/cve-curation.js +84 -8
  20. package/lib/lint-skills.js +75 -3
  21. package/lib/playbook-runner.js +443 -50
  22. package/lib/prefetch.js +22 -2
  23. package/lib/refresh-external.js +32 -1
  24. package/lib/refresh-network.js +235 -26
  25. package/lib/schemas/cve-catalog.schema.json +5 -0
  26. package/lib/scoring.js +141 -21
  27. package/lib/sign.js +107 -29
  28. package/lib/source-advisories.js +23 -5
  29. package/lib/source-ghsa.js +25 -1
  30. package/lib/source-osv.js +26 -1
  31. package/lib/upstream-check.js +1 -1
  32. package/lib/validate-cve-catalog.js +30 -4
  33. package/lib/validate-indexes.js +135 -29
  34. package/lib/validate-playbooks.js +19 -7
  35. package/lib/validate-vendor.js +69 -8
  36. package/lib/verify.js +23 -6
  37. package/manifest.json +53 -53
  38. package/orchestrator/dispatcher.js +14 -3
  39. package/orchestrator/index.js +100 -20
  40. package/orchestrator/scanner.js +8 -0
  41. package/package.json +1 -1
  42. package/sbom.cdx.json +130 -130
  43. package/scripts/audit-cross-skill.js +1 -1
  44. package/scripts/bootstrap.js +1 -0
  45. package/scripts/build-indexes.js +84 -11
  46. package/scripts/check-agents-md-collectors.js +41 -13
  47. package/scripts/check-changelog-extract.js +4 -4
  48. package/scripts/check-codebase-patterns.js +19 -5
  49. package/scripts/check-manifest-snapshot.js +74 -30
  50. package/scripts/check-sbom-currency.js +25 -5
  51. package/scripts/check-test-count.js +26 -7
  52. package/scripts/check-test-coverage.js +44 -4
  53. package/scripts/check-version-tags.js +27 -8
  54. package/scripts/predeploy.js +1 -1
  55. package/scripts/refresh-manifest-snapshot.js +14 -4
  56. package/scripts/refresh-reverse-refs.js +7 -1
  57. package/scripts/refresh-sbom.js +1 -1
  58. package/scripts/release.js +3 -3
  59. package/scripts/run-e2e-scenarios.js +18 -8
  60. package/scripts/validate-vendor-online.js +28 -2
  61. package/scripts/verify-shipped-tarball.js +65 -6
  62. package/sources/validators/cve-validator.js +17 -1
  63. package/vendor/blamejs/_PROVENANCE.json +4 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.18.8 — 2026-06-20
4
+
5
+ Signature verification now fails closed on the two states an attacker with write access to `keys/` can engineer: a missing `keys/public.pem` makes `loadManifestValidated()` throw rather than return an unverified manifest, and a missing `keys/EXPECTED_FINGERPRINT` makes the manifest-signature check refuse rather than verify against a possibly-swapped key. The pin ships in the tarball and is committed, so its absence is treated as tampering. A missing pin fails closed unconditionally and the error directs you to restore the committed pin; `KEYS_ROTATED=1` governs a key-fingerprint mismatch (a deliberate rotation), which is the only state it applies to — a rotation updates the pin in place, it never removes it.
6
+
7
+ The compliance close phase no longer crashes when a jurisdiction obligation entry is malformed: a null/non-object entry is skipped during notification enrichment and synthesis instead of throwing; an obligation whose `window_hours` is not a finite number falls back to the pending-clock sentinel instead of computing an invalid date and aborting the phase; a `notification_action` whose `obligation_ref` resolves to nothing is dropped (the unmatched ref is surfaced as a diagnostic) rather than emitted with null jurisdiction/regulation; and a notify obligation with a non-number `window_hours` is surfaced as a diagnostic and skipped rather than synthesized into a malformed record. The threat-currency gate hard-blocks a non-numeric `threat_currency_score` (absent / null / NaN) instead of letting it slip past as "not less than 50"; `forceStale` still overrides. Air-gap mode flags an artifact that has no `air_gap_alternative` so its fall-back to the original (possibly network-bound) source is visible instead of a silent bypass. A CVE marked both `not_affected` and `fixed` in a VEX statement no longer appears as fixed while absent from the matched/baseline sets.
8
+
9
+ The `theater_score` exposed to `feeds_into` / escalation conditions now follows the same direction as the rest of the engine — a `theater` verdict (a compliance gap exists) scores high and a clear verdict scores low — so a condition such as `theater_score >= 50` chains exactly when a gap is found rather than the reverse.
10
+
11
+ RWEP scoring is consistent across its three surfaces: the derived score counts the `reboot_required` / `patch_required_reboot` alias once (no double-count), `validateFactors` normalizes `active_exploitation` and accepts the `patch_required_reboot` alias (matching the scorer), and the `compare()` explanation lists the AI factor for `ai_assisted_weaponization`, a stray-cased confirmed exploitation, and the reboot alias.
12
+
13
+ The orchestrator CSAF report emits a real `cvss_v3` block (base score + vector) for a catalogued CVE instead of a hardcoded `base_score: 0`, and never emits a null description or threat detail. The dispatch plan omits the per-entry `evidence` object when it has no content rather than emitting a bare `{}`, and it now keeps a plan entry for every distinct finding that routes to a skill — two different CVEs that route to the same skill each appear with their own evidence instead of all but the first being silently dropped; a parse-error finding routes directly to its skill rather than relying solely on the domain table. `attest diff`'s human output classifies the sidecar by tamper class first, so an unsigned-substitution attestation is not mislabeled as benign explicitly-unsigned, and prints `(no detail)` instead of `(null)` for an absent timestamp.
14
+
15
+ Empty-string flag values are rejected instead of silently degrading the command: `--mode ""`, `brief --phase ""`, and `--playbook ""` error rather than running with no scope. The catalog and playbook validators fail loud when a reference catalog or the playbook schema is absent, instead of silently skipping the framework-control, attack-ref, clock-start, and frameworks-in-scope checks — a partial tree can no longer pass an unresolvable reference or an invalid closed-vocabulary value.
16
+
17
+ `refresh --network`'s upstream prefetch retries a timed-out request (re-classified as a retryable network error) instead of dropping it, so a slow or rate-limiting upstream no longer pushes the scheduled refresh past its error budget. The tarball fetch builds its destination from the canonical npm URL plus a strict-semver-validated version rather than the registry-supplied `dist.tarball`, so a tampered metadata response cannot steer the download's host or path (content trust is unchanged: SHA-512/SRI + shasum + Ed25519); the vendor-source online check likewise validates every URL component (owner/repo shape, hex commit, traversal-free path) before fetching.
18
+
19
+ The publish-workflow audit now flags a floating (non-SHA) action reference even when the `uses:` line carries a trailing YAML comment — previously such refs were silently missed, reading as compliant.
20
+
21
+ The index gates fail closed on a degraded tree: a source that vanished between discovery and hashing, or a corrupted hash entry, is reported rather than crashing the validation; and `build-indexes --only` preserves the prior source hashes so a partial rebuild can no longer record stale outputs as fresh. Index files are written atomically.
22
+
23
+ Internal: the host OS-release lookup reads the file directly instead of spawning `cat`; the orchestrator watch-lock and the playbook mutex lock files are created with an explicit owner-only mode; the evidence-dir entry is opened through a single no-follow descriptor and validated on that descriptor (no check-then-reopen window); the index-output validator and a lock-reentrancy test read through a single descriptor; and a help-text assertion uses a literal substring instead of a partially-escaped regex.
24
+
25
+ ## 0.18.7 — 2026-06-20
26
+
27
+ Escalation and feeds_into conditions that use an `any`/`all` quantifier prefix, a quoted `matches '…'` pattern, or a quoted `contains` member now evaluate instead of silently failing closed. Several catalog chains that depended on these forms — most visibly the compliance-theater → SBOM correlation — were dead and now fire; the feeds_into evaluation context also exposes the bare `blast_radius_score`, `theater_verdict`, and `compliance_theater_check` tokens the catalog references, so an engine-computed value is no longer lost to a `null` lookup. The SBOM → kernel / MCP / AI-API follow-ups now fire too: a matched CVE carries an `attack_class`, the analyze and close contexts expose the matched-CVE array the `any matched_cve.attack_class == …` rules quantify over, and the CVEs that map unambiguously to one of those deep-dive classes are tagged in the catalog — so an SBOM scan that surfaces an MCP-server RCE or a prompt-injection-to-RCE routes the operator into the matching deep dive, while an unclassified match routes nowhere rather than guessing. `IN […]` member lists, AND/OR joins, and outer parentheses are parsed quote-aware, so a comma, bracket, or operator inside a quoted value no longer tears the clause, and a `contains`/`IN` against a path that doesn't resolve records a diagnostic instead of evaluating to an invisible false.
28
+
29
+ RWEP scoring no longer silently drops the active-exploitation weight when a CVE's `active_exploitation` value is outside the recognised vocabulary or merely stray-cased (`Confirmed`, ` CONFIRMED `): canonical values are case-normalised, and a genuinely unrecognised value contributes zero **and** emits a warning so the dropped weight is visible rather than quietly lowering the priority. Curation now rejects a mixed-shape `rwep_factors` block (post-weight integers with a stray non-numeric value) at apply time instead of letting it route through the wrong scoring path and write a silently-wrong RWEP to the catalog. The scorer's two factor shapes no longer disagree on that block — the derive path applies the same mixed-shape guard the validator does, so it can't fall back to the boolean path and under-score — the Shape-B sum clamps each weight to its ceiling, and an SLA timeline is derived only from a real number rather than treating a NaN as the least-urgent tier.
30
+
31
+ `attest diff` no longer manufactures phantom drift when one attestation carries real evidence and the other is empty — the full-catalog placeholder now stands in only when both sides are empty, so a populated side diffs against nothing and yields the genuine added/removed instead of every catalog id reading as drift. It also no longer masks a real change when an artifact's secret or path is carried under a key other than `value`: that evidence is preserved through capture and compared, so two attestations recording different secrets at different paths no longer diff as unchanged.
32
+
33
+ An empty-string flag value is rejected rather than silently degrading the command. `--evidence ""`, `--evidence-dir ""`, `--phase ""`, `--against ""`, `--playbook ""`, and `--session-id ""` now error instead of running with no scope or bypassing their phase / target / dispatcher validation, and `attest export` redacts `signal_overrides` in its labeled output instead of emitting them verbatim.
34
+
35
+ The secrets collector no longer double-reports an Anthropic `sk-ant-…` key as both an OpenAI and an Anthropic key, and now surfaces subtrees skipped for exceeding the depth cap instead of leaving them silently unscanned. The collectors and the CVE-cache reader read each file through to end-of-file, so a key or misconfiguration sitting past a short-read boundary on a network or sync-backed filesystem is no longer dropped and the scan no longer reports clean over content it never decoded. The library-author collector no longer flags npm-workspaces lockfile links (`link: true`) as missing integrity — a false positive on every workspaces monorepo. The CI/CD collector matches the GitHub OIDC issuer and a `GITHUB_TOKEN` secret name exactly, so a look-alike issuer host or a custom `GITHUB_TOKEN_*` secret is no longer mis-handled.
36
+
37
+ The network-backed pollers (OSV, GHSA, the Datatracker freshness probe, the package prefetch, and the watchlist org scan) retry transient failures with backoff and cap response size instead of buffering an unbounded body into memory; the prefetch retry classifier reads the error status under the field the retry helper sets, so a 429/5xx is retried rather than treated as fatal. The vendored-tree integrity check fails closed when a license hash or a per-file hash is absent — a stripped hash no longer reads as verified.
38
+
39
+ CVE-catalog validation rejects impossible calendar dates (for example `2026-02-30`, or Feb 29 in a non-leap year) on KEV deadline fields instead of letting them roll over to a valid-looking date, and the public-exploit URL classifier is anchored so a look-alike host such as `exploit-db.com.attacker.example` is no longer treated as a public-exploit source.
40
+
41
+ The index-freshness gate now verifies that every derived index file still exists and parses, not only that the source hashes match — a deleted or truncated index no longer passes as current. `refresh --network` and the vendored-file online validator pin their fetches to the npm registry / GitHub hosts, so a tampered metadata response or redirect cannot steer a download at an arbitrary address. `refresh --network` additionally verifies the npm registry's signature over the fetched tarball and authenticates the `data/` tree and manifest snapshot before swapping them in, so a forged metadata response that recomputes the transport hashes is still refused. The live CVE-validation downgrade guard now reads the upstream metric's declared CVSS version, so a legacy v2 score NVD returns without a vector string is still recognised as a downgrade and held back from a curated v3.x entry — matching the cached refresh path, which already used the declared version.
42
+
43
+ Internal: the diff-coverage export extractor and the codebase-pattern detectors are now string-aware, so a brace or `//` inside a string literal can no longer hide a new export from the coverage gate or disarm the process-exit / dynamic-regex detectors. Temp files in the test suite are created inside owner-only directories. The signing-key bootstrap creates each key with an exclusive (`O_EXCL`) open so an existing key is refused atomically rather than via a separate existence check, and the index validator opens derived files with `O_NOFOLLOW` and reads them through the descriptor, removing the check-then-read windows static analysis flagged. The signer schema-validates the manifest before writing a signature, so it can no longer bless a manifest the verifier would reject at install time. Several gate detectors close blind spots: the test-count gate ignores `test(` inside a multiline template literal, the export extractor sees method-shorthand and single-identifier `module.exports`, the process-exit detector covers `scripts/` and `bin/`, the version-tag detector no longer counts an IPv4 octet run, and `build-indexes --changed` regenerates a deleted index instead of trusting a clean source hash and no longer rewrites a timestamp on a no-op run.
44
+
3
45
  ## 0.18.6 — 2026-06-14
4
46
 
5
47
  `attest prune` now ages out sessions that hold only replay records (whose attestation was removed). Such a session had no datable timestamp, so it was kept indefinitely and the attestation store could grow without bound; it is now dated by its newest replay timestamp and pruned past the cutoff like any other session.