Skip to content

chore(examples): harden the demo's pages and Wallet Provider - #795

Merged
osanderson merged 4 commits into
mainfrom
chore/demo-hardening
Nov 1, 2026
Merged

osanderson merged 4 commits into
mainfrom
chore/demo-hardening

Conversation

@osanderson

Copy link
Copy Markdown
Contributor

Summary

passport-vdc

  • Framing. demotls.FrameGuard sends Content-Security-Policy: frame-ancestors 'self' and X-Frame-Options: SAMEORIGIN on every response from every demo server. Before, any site could frame the operator pages, and a form submitted inside a frame passes the pages' Origin checks. Same-origin frames, such as the issuer's progress iframe, still load.
  • Origin checks. The passport upload and sample POSTs now check Origin, as the issuer's other POSTs do.
  • Wallet Provider:
    • Challenge limit: POST /attestation-challenge is limited per client address (burst 20, then 1/s, 429 past that). One client can no longer fill the 10,000-challenge table and lock every Android device out. Behind the Cloudflare tunnel all clients share cloudflared's address, so the limit is shared there; the code and README say so.
    • Unanchored keys: keys whose chain isn't anchored to a trusted root are still recorded, because emulators renew their Wallet Attestation by re-sending it. They are now flagged, capped at 1,000 and evicted first. They never displace or overwrite a hardware-anchored key's record.
    • File writes: the verified-keys file is written at most once every 10 s, instead of in full on every record.
    • Flag help: -require-hardware-key-attestation now says a key sent without a chain is still attested.
    • Error text: an unknown, used or expired challenge now says so and asks for a new one.

CI

  • release-please.yml's Latest step now runs whenever any release is created. It marks the library's newest vX.Y.Z release Latest, so a sqlstore-only release no longer takes Latest; ci: keep the library's release marked Latest #789 only covered both releasing together.

Test plan

  • TestPagesRefuseCrossOriginFraming and TestServerGuardsFraming
  • TestOfferForms_RefuseCrossOrigin
  • TestEvidence_LimitsChallengesPerClient and TestEvidence_UnanchoredKeysDontDisplace; the challenge message is checked in TestEvidence_VerifiesChains
  • passport-vdc go vet ./..., go test -race ./..., pinned gofmt, golangci-lint
  • The workflow YAML parses and shellcheck is clean; the jq filter returns v0.58.0 against this repository

🤖 Generated with Claude Code

osanderson and others added 4 commits November 1, 2026 20:42
Every passport-vdc server now answers with Content-Security-Policy
frame-ancestors 'self' and X-Frame-Options SAMEORIGIN, set by
demotls.FrameGuard around the handler demotls.Server serves. The
operator pages' forms (revoke, approve, forget) and the verifier's and
web wallet's pages could otherwise be clickjacked from a framing page,
where their Origin checks pass. Same-origin frames, such as the
issuer's progress iframe, still load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The issuer's upload and sample forms now refuse a post whose Origin is
another site's, as its review, status, kept-passport and approval forms
already do, so another site can't start an offer through a visitor's
browser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… records

- /attestation-challenge is limited per client address (a burst of 20,
  then one a second; 429 past it), so one client can no longer fill the
  10,000-challenge table and lock every Android device out. The address
  is the connection's: no forwarded-address header is trusted, so
  behind a tunnel the limit is shared.
- Keys whose chains end at an untrusted root (an emulator's, or one
  the sender made) are still remembered for re-attestation, but marked
  unanchored: at most 1,000 are kept, they're evicted before any
  anchored key, and one never overwrites an anchored key's record.
- The verified keys' file is written at most once every 10 seconds
  rather than on every record.
- A challenge the provider doesn't know now reads "unknown, used or
  expired; request a new challenge" rather than suggesting forgery.
- -require-hardware-key-attestation's help says a key sent without a
  chain is still attested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Mark Latest step ran only when the root package released, so a
release of sqlstore alone became Latest. It now runs whenever any
release is created, marking the root's new tag Latest if it released
and otherwise its newest existing release (a vX.Y.Z tag without a
component prefix, not a prerelease). Outputs reach the script through
env only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@osanderson
osanderson force-pushed the chore/demo-hardening branch from 966961f to dfb1980 Compare November 1, 2026 12:43
@sonarqubecloud

sonarqubecloud Bot commented Nov 1, 2026

Copy link
Copy Markdown

@osanderson
osanderson merged commit e290c24 into main Nov 1, 2026
19 checks passed
@osanderson
osanderson deleted the chore/demo-hardening branch November 1, 2026 13:00
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