Conversation
…v0.3.2) verifyApprovalSignature previously built a canonical payload from the current grant and spread it into the attestation object, but the ssh provider only checks signature-against-stored-signed_payload and never used the rebuilt value. Post-sign edits to approver, reason, expires_at, or task_hash in approvals.ndjson would go undetected. Now the rebuilt payload is compared to signature.signed_payload and any divergence returns verified: false with reason "grant fields do not match signed payload (possible tampering)". Tests: - ssh-signed grant + mutations to approver/reason/expires_at each fail verification (skipped cleanly if no local ssh key available) - multi-workflow manifest: throws without --workflow, throws on unknown workflow id, scopes grants per workflow with distinct task hashes Docs: - contract.max_cost_usd: clarified as runtime-enforced (scheduler for cost-attributed tasks), declarative-only for shell exec - sandbox/network on non-macOS: recommend containerized execution as the production-grade isolation path until native Linux/Windows sandbox adapters ship 665/665 tests pass. No schema changes, no CLI surface changes, no behavioral change for unsigned grants.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Patch release bundling two real fixes and two doc clarifications surfaced during the 0.3.x audit. No schema or CLI surface changes.
Fix 1 — Approval tamper detection
verifyApprovalSignaturepreviously rebuilt the canonical payload from the current grant and spread it into the attestation object, but the ssh provider only verifiessignatureagainstsigned_payload— the rebuilt value was never used. Post-sign edits toapprover,reason,expires_at, ortask_hashinapprovals.ndjsonwould go undetected (the ssh signature still matches the original signed payload).Now the rebuilt payload is compared to
signature.signed_payload. Any divergence returnsverified: falsewith reasongrant fields do not match signed payload (possible tampering). Flagged by the 0.3.0 implementation audit as a confidence-below-threshold observation; converting the dead code path into an actual defensive check.Fix 2 — Multi-workflow disambiguation tests
grantApprovalcorrectly requires--workflowon multi-workflow manifests and scopes grants per workflow, but no test exercised those paths. Added a test that:--workflowon a multi-workflow manifestfindValidApprovalis correctly scoped per workflowDoc 1 —
contract.max_cost_usdClarified: enforced by runtimes that track cost-attributed operations (openclaw-scheduler for LLM tasks reporting token usage); declarative-only for shell-target
agentcli exec(no cost signal to enforce against). Prevents users from expectingexecto cap shell-command spend.Doc 2 — Sandbox on non-macOS
Added recommendation to
docs/guide-identity.mdthat production-grade isolation on Linux / Windows should runagentcli execinside a container until native OS sandbox adapters ship. Manifest declaration remains valid metadata; the OS-level boundary comes from the container namespace.Test plan
npm run lintcleannpm test— 665/665 (was 663 + 2 new tests)lint-testpassesFiles
src/approvals.js— tamper check inverifyApprovalSignature, explanatory commenttest/approvals.test.js— tamper test + multi-workflow testdocs/field-reference.md—max_cost_usdclarificationdocs/guide-identity.md— sandbox/container recommendationCHANGELOG.md— new 0.3.2 sectionpackage.json— 0.3.1 → 0.3.2