Skip to content

v2.2.0

Choose a tag to compare

@github-actions github-actions released this 28 Sep 10:10
· 149 commits to main since this release
9c6133c

[2.2.0] - 2026-09-28

Fixed

  • verify-enforcement and verify-enforcements read SEV-SNP report versions 3, 4 and 5. They accepted only version 2, so a report from current AMD firmware was marked unverified. Versions 3 to 5 only fill bytes that are reserved in version 2, so every field Vaara reads sits at the same offset.
  • vaara release-check --condition-key and vaara attribute-check --key given a key that is not Ed25519 now say "not an Ed25519 public key" instead of printing a Python attribute error.
  • The macOS approval card shows what will run. Approval requests have carried the call's full arguments and their SHA-256 since the destructive floor, but the app read only the tool name and the reason, so a held delete reached the human as "Bash" with no command. The card now shows a shell call's command, or any other call's arguments as JSON, with the first 16 hex characters of the digest. The notch view shows the first two lines. tests/test_approval_request_join.py fails if the engine writes a request field the app never reads.
  • A tool call that deletes or discards state is held for a human. The scorer read only the tool name, so every shell call got the same score, whether it listed a directory or deleted one, and none of them reached the escalate line. A floor in the scorer now reads the call's arguments. It holds shell commands that delete files, truncate, drop tables, force-push, delete a branch or tag, reset or clean a work tree, or delete cloud or cluster resources. It also holds SQL that drops, truncates or deletes rows, code that removes files, and tools whose name says they delete. The floor raises a score to the escalate line and never lowers one, so a denied call stays denied. It lives in the scorer, so every path that scores a call gets it.
  • On the pre-tool-use hook, shell and file calls that passed the deny rules were only recorded. A destructive one now goes through the enforced pipeline and waits for a human. If nobody answers, it is denied, and the trail records no human decision.
  • The floor reads only command-like arguments and skips heredoc bodies, quoted text and file contents. Writing a script that contains rm, or a note about it, passes.
  • A plain pip install vaara now writes a signed receipt for every decision. The signing libraries (cryptography, rfc8785, cbor2) were only in the attestation extra, so the base install recorded decisions with no receipts and said nothing. They are now base dependencies. vaara[attestation] still works and installs the same packages. An install made without them (--no-deps) logs a warning that receipts are off.

Added

  • A SEV-SNP report's signing key is checked against AMD's root. verify_sev_snp_chain pins the SHA-256 of AMD's ARK public key for Milan, Genoa and Turin, checks that the ARK signed the ASK and the ASK signed the VCEK, that the VCEK's chip ID and TCB match the report, and that the report signature verifies under it. It runs offline and also takes a VLEK chain. verify-enforcement --amd-chain, tee verify --amd-chain and a NAME.amd-chain.pem beside a record in verify-enforcements use it, and a verdict whose chain holds reads vcek_chain_basis: kds_verified. With a pinned measurement that is the attested tier, which --strict requires. The check passes on a report from real Milan hardware and the VCEK AMD issued for it, committed under tests/fixtures/sev_snp_milan.
  • SEVSNPHostAttester requests a report inside a SEV-SNP guest, through configfs-tsm (Linux 6.7 and later) or the SNP_GET_REPORT ioctl on /dev/sev-guest, and returns the certificate table the host published with it. vaara tee emit writes the report and that chain; vaara tee fetch-chain gets a report's VCEK, ASK and ARK from AMD's KDS. The emitter is written to the kernel UAPI and has not yet been run inside a guest by this project.
  • --seal-known-secrets now also seals credentials in no published format. A value is sealed when it is assigned to a key that ends in api_key, secret, token, password, access_key, private_key, credential or auth (API_KEY=..., "client_secret": "...", x-api-key: ...), or sent as a bearer token, and it reads as a generated token: 16 or more characters, at least two of lowercase, uppercase and digits, and 3.0 bits of entropy per character or more. Only the value is sealed, with the same stable placeholder a named secret gets, and records count it as assigned_secret or bearer_token. Hex digests, UUIDs, ids under ordinary keys, max_tokens, template text like YOUR_API_KEY_HERE and short or dictionary-word passwords are left as written.
  • Registration with an IETF SCITT Transparency Service. vaara.audit.scitt_service builds a SCITT Signed Statement (COSE_Sign1 with CWT claims and an x5chain under a did:x509 issuer), registers it over SCRAPI (POST /entries, then the 303 and 302 sequence to the receipt), and saves the service's /.well-known/scitt-keys. vaara receipt verify-transparency then checks the receipt with no network: the inclusion proof has to commit to the exact statement bytes, and the service's ES256 or ES384 signature has to hold over the recomputed ledger root (CCF_LEDGER_SHA256 profile).
  • CI job SCITT round-trip: builds Microsoft's scitt-ccf-ledger 0.20.1, registers a Vaara decision receipt with it, stops the ledger, and verifies the receipt offline. Runs weekly and on changes to the client.
  • An approval request carries the call's full arguments and their SHA-256. A surface that asks the human can now show the whole operation that will run. The request used to hold only the tool name and the risk reason.
  • Session start runs a canary through the installed engine with the configured thresholds: a delete must be held and a directory listing must pass. It scores against an in-memory trail, so nothing is written to the real one. If either check fails, session start says so and raises a notification.