Skip to content

Fail closed on configured-but-invalid acceptance keys (#53) - #55

Open
FetchDoge wants to merge 1 commit into
mainfrom
fix/acceptance-key-53
Open

FetchDoge wants to merge 1 commit into
mainfrom
fix/acceptance-key-53

Conversation

@FetchDoge

Copy link
Copy Markdown
Owner

Closes the two bars REA's after-the-fact check of 9809cc7 (#44) failed, per the pass bar written in #53.

What changed

  • Whitespace-only keys rejected (bar 1): _validate_acceptance_key rejects empty, under-32-byte, and whitespace-only keys (32+ bytes included) for ReleaseVerifier, DurableReleaseVerifier, and sign_acceptance.
  • Configured-but-invalid is fatal (bar 3): an explicit key or a present PERMIT_ACCEPTANCE_KEY that is invalid raises even with the demo flag on. The demo fallback now applies only when NO key is configured at all.
  • Demo key is an explicit flag (bar 2): --demo-key, off by default, on server.py (mock incl. --db), demo.py, demos/*, python -m permit, and trace.py. Without a valid key or the flag, each exits non-zero naming the fix. CI and forge demo smokes opt in explicitly (demo.py --demo-key, permit demo --demo-key).
  • --db persistence (bar 4): server.py --db --demo-key persists the generated key in a 0600 <db>.demo-key sidecar (load_or_create_demo_key_file) so it cannot rotate silently across restarts. Documented in README.
  • Docs (bar 5): stale ledger.py docstring fixed; README notes the sign_acceptance API change (key argument required, verifier: remove DEMO_ACCEPTANCE_KEY fallback (fail closed on missing key) #35) and the new key rules. The README license badge already reads Apache-2.0 (docs: relicense under Apache-2.0 and add NOTICE #51), so no badge change was needed.

Verification (bar 6)

  • New tests/test_issue53_acceptance_key.py: 29 tests. All 29 fail on 9809cc7 and pass on this branch (verified in a pristine 9809cc7 worktree).
  • Full suite: 428 passed, 1 skipped, plain and under python -O.
  • Smokes: python demo.py --demo-key (ledger VERIFIED), python trace.py --demo-key (TRACE COMPLETE), six-beat --replay fixture path with --demo-key (ledger VERIFIED, 26 receipts).
  • Ruff on touched files: 19 findings, same as the pre-change baseline (report-only in CI).

Merge gate

Per #53, merging needs Marcus Richards's own yes plus an independent REA check. This PR is intentionally left unmerged.

- Whitespace-only keys (>=32 bytes included) rejected by the verifier,
  the durable verifier, and sign_acceptance.
- A configured-but-invalid key (explicit or PERMIT_ACCEPTANCE_KEY:
  empty, under 32 bytes, whitespace) raises even with the demo flag;
  the demo fallback now applies only when no key is configured at all.
- Demo key is an explicit --demo-key flag, off by default, on every
  entry point: server.py (mock incl. --db), demo.py, demos/*,
  python -m permit, trace.py. Without a valid key or the flag each
  exits non-zero naming the fix. CI/forge demo smokes opt in explicitly.
- server.py --db --demo-key persists the generated key in a 0600
  <db>.demo-key sidecar so it cannot rotate silently across restarts.
- Stale ledger.py docstring fixed; sign_acceptance API change and the
  new key rules documented in README.
- tests/test_issue53_acceptance_key.py: 29 tests covering bars 1-4 for
  every entry point; all 29 fail on 9809cc7, all pass here.
  Full suite: 428 passed, 1 skipped, plain and under -O.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-19T22:33:04.592277Z 76317cf PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 76317cf45a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread settlement/verifier.py
f"acceptance key from {source} is missing, empty, or under "
f"{MIN_ACCEPTANCE_KEY_BYTES} bytes (fail closed, issue #35/#53)"
)
if not resolved.strip():

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject Unicode-only whitespace keys

When an explicit string key or PERMIT_ACCEPTANCE_KEY consists of Unicode whitespace such as 32 non-breaking spaces (U+00A0), _coerce_key first UTF-8-encodes it and this bytes.strip() check no longer recognizes the whitespace, so the trivially guessable key is accepted and can forge Type A acceptance signatures. Validate string keys with Unicode-aware str.strip() before encoding, while retaining the byte-level check for byte inputs.

Useful? React with 👍 / 👎.

Comment thread settlement/verifier.py
Comment on lines +182 to +184
if _os.path.exists(p):
_os.chmod(p, 0o600)
raw = open(p, encoding="ascii").read().strip()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Publish the persisted demo key atomically

When two server.py --db <same-path> --demo-key processes start concurrently, the creator makes the sidecar visible before writing its contents, so the other process can enter this existing-file branch, read an empty/partial key, and exit with ValueError; alternatively, both can observe the file as absent and the O_EXCL loser exits with an uncaught FileExistsError. This breaks the documented second-writer durable setup, so creation should atomically publish complete contents and concurrent losers should retry loading the winning file.

Useful? React with 👍 / 👎.

Comment thread server.py
if args.db and "PERMIT_ACCEPTANCE_KEY" not in os.environ:
from settlement.verifier import load_or_create_demo_key_file
try:
key = load_or_create_demo_key_file(args.db + ".demo-key")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ignore generated demo-key sidecars

When a developer runs server.py --db ledger.db --demo-key from the checkout, this creates ledger.db.demo-key containing the complete acceptance-signing secret, but no *.demo-key pattern exists in .gitignore. The untracked file can therefore be accidentally added and committed, after which anyone with repository access can forge Type A acceptance signatures for that durable instance; add the generated sidecar suffix to the repository ignore rules.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant