Skip to content

security: pk_/sk_ key rotation, origin-validation hardening, and CSP guidance for embedded widget - #50

Merged
aguilar1x merged 12 commits into
PACTO-LAT:mainfrom
diegocintron:feat/gateway-key-rotation-origin-csp
Jul 20, 2026
Merged

security: pk_/sk_ key rotation, origin-validation hardening, and CSP guidance for embedded widget#50
aguilar1x merged 12 commits into
PACTO-LAT:mainfrom
diegocintron:feat/gateway-key-rotation-origin-csp

Conversation

@diegocintron

@diegocintron diegocintron commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Closes #36

Summary

Implements #36 — key rotation with a grace period, hardened origin validation, and copy-paste CSP guidance for the embedded widget. Builds on the pk_/sk_ key model (#11) and the origin validation shipped in the first campaign.

Key rotation with grace period (services/connect-gateway)

  • rotateApiKey now issues a new pk_/sk_ pair as a successor ApiKey (linked via rotatedFromId) instead of overwriting the secret in place. The old key stays active with a graceExpiresAt window (default 24h, configurable via KEY_ROTATION_GRACE_MS), so in-flight sessions using the old key keep working during cutover.
  • cutoverApiKey + POST /admin/keys/:id/cutover revoke the predecessor on demand; keys also expire lazily once the grace window passes (no cron). Double-rotation is guarded.

Origin-validation hardening

  • New normalizeOrigin (rejects non-http(s) schemes, embedded credentials, path/query/fragment, and unparseable values) and matchOrigin (single-label wildcard https://*.example.com; apex, multi-level, and bare * are rejected case-insensitively; protocol and port must match).
  • Middleware now falls back to Referer when Origin is absent and returns explicit error codes: key_required, key_invalid, origin_required, invalid_origin, origin_not_allowed. allowedOrigins are validated at key-creation time too.

CSP + iframe-sandbox guidance (apps/docs, packages/connect-elements)

  • New security.mdx with mode-aware, copy-paste CSP snippets (in-page mount() vs isolated mountFrame()) and iframe-sandbox guidance.
  • FRAME_SANDBOX is exported as the single source of truth for the sandbox tokens; csp.test.ts asserts the built widget bundle contains no eval()/new Function()/inline <script> and that the documented sandbox string matches the constant.

Acceptance criteria

  • Rotating a key does not break in-flight sessions using the old key during the grace period.
  • A spoofed or malformed origin is rejected with a clear error code, with tests covering the edge cases.
  • Docs site includes a copy-paste CSP snippet, verified against the actual widget bundle.

Testing

  • Full suite green: 301 tests (core 67, react 16, elements 33, gateway 185).
  • npm run type-check (8/8) and npm run check (biome) clean.

Follow-ups (non-blocking)

  • Double-rotation guard runs outside the rotate transaction — a rare concurrent admin double-rotate would 500 (the DB unique constraint protects integrity); could be tightened to a 409.
  • KEY_ROTATION_GRACE_MS uses Number.parseInt, which is lenient about trailing garbage ("24abc" → 24).

@aguilar1x aguilar1x left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

@aguilar1x
aguilar1x merged commit f6c8f93 into PACTO-LAT:main Jul 20, 2026
2 checks passed
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.

security: pk_/sk_ key rotation, origin-validation hardening, and CSP guidance for embedded widget

2 participants