v0.8.1 — Security hardening
A security-hardening release. It closes every finding from a full adversarial audit of the v0.8.0 surface — the attestation trust model, policy enforcement, deserialization, resource bounds, information disclosure, credential transport, and on-disk permissions. No new capabilities; drop-in from 0.8.0 except for the two changes under Breaking changes and Migration. Every fix ships with tests (Python 611 passing, full TypeScript suite green, mypy --strict clean).
Security
- [Critical] Signature-aware chain verification with a trust anchor.
verify_chain()only proves hash linkage, andcontent_hashis an unkeyed SHA-256 — an attacker who controls a serialized chain can edit any record, recompute the links, and passverify_chain(). Per-recordverify()also checked the signature against the key embedded in that same record, so a chain forged with an attacker key self-verified. NewAttestationChain.verify_signatures(trusted_keys=…)verifies every Ed25519 signature and rejects records whose embedded key isn't in a pinned set. - [Critical]
verify-decisionsCLI no longer blesses unsigned chains. It previously printedchain valid: yes/verified: yesand exited 0 for an unsigned or forged chain. It now reports signature status, prints unsigned records asunsigned, accepts repeatable--trusted-key <pub.hex>, and exits non-zero unless signatures verify. - [High] Enforcement fails closed on
escalate, not onlyblock. Underenforce=True, the built-inREQUIRE_APPROVALpolicies (high-risk tool, code-execution, financial threshold, multi-agent), cortical drift, and recovery chain-tamper all emitescalate, which enforcement ignored — "require approval" silently proceeded._BaseAdapterandIntegrityMonitornow take anapproval_handler:blockalways denies,escalatedenies unless the handler approves it (no handler ⇒ fail closed). - [High] Embedding-similarity cache moved from
pickleto JSON. The cache was unpickled before its signature check, so a filesystem-write attacker (T-T2) could poison it into arbitrary code execution on load. JSON deserializes to inert data. - [High]
chain_validno longer overstates the guarantee — see Breaking changes. - [Medium] Bounded context buffers (memory-exhaustion). All accumulating adapter buffers are capped at 1000/session via a shared helper (previously only broadcasts were capped); overflow emits a one-time
<channel>_overflowevent. - [Medium] Exception text no longer leaks into serialized records. The exported
capture_failureevent and a crashing governance rule'sreasonnow carry only the exception class, not the raw message (which can embed tokens/URLs/PII); full detail stays in local logs. - [Medium] Allow-list validation for filesystem-bound identifiers.
checkpoint_id,agent_id, androlenow use a non-empty ASCII alphanumeric/underscore/hyphen allow-list, closing empty-id →.json, dotfiles, NUL bytes, reserved names, and a__role-separator collision (baseline misattribution). - [Low] TLS-gated Bearer token in the TypeScript reporter. The
Authorizationheader is sent only over HTTPS or loopback; a misconfiguredhttp://<remote>no longer leaks the token. - [Low] Restrictive store-file permissions. Freshly-created store dirs are 0700 and sqlite DB files 0600 (best-effort; pre-existing dirs untouched).
- [Low] Race-free store reads/deletes.
load/deletenow read/unlink-then-catchFileNotFoundErrorinstead ofexists()-then-act (TOCTOU).
Added
AttestationChain.verify_signatures(trusted_keys=…)— trust-anchored chain verification.approval_handleron_BaseAdapterandIntegrityMonitor.GovernanceLayer(sensitive_tools=…)+ per-callcontext["sensitive_tools"]; exportedDEFAULT_SENSITIVE_TOOLS.--trusted-key <pub.hex>onpython -m agentegrity verify-decisions.
Changed
- BREAKING — session-summary field
chain_validrenamed tochain_hash_linkedacross the Python adapters, the TypeScript client, andschemas/exporter/common.json. The old name read as cryptographic proof; the value only reflects hash linkage. - Enforcement under
enforce=Truetreatsescalateas blocking-pending-approval, not advisory.
CI / tooling
- Added a
dependency-auditCI job (pip-audit+bun audit, advisory) and Dependabot config for pip, npm, and github-actions.
Migration from 0.8.0
chain_valid→chain_hash_linked: update any exporter/get_summary()consumer (Python + TS) reading the field; value unchanged, key renamed. Backends validating againstschemas/exporter/common.jsonmust adopt the renamed required field.enforce=True+ escalating policies: pass anapproval_handler(profile, score, action) -> boolif you need escalations to proceed; otherwise they now deny (fail closed).- Chain verification:
verify_chain()is unchanged. Trust-boundary consumers should adoptverify_signatures(trusted_keys=…)with an out-of-band key; the CLI needs--trusted-keyto report a chain as cryptographically verified.