Skip to content

[SEC] Dynamic CORS + CSP + Merchant Origin Allowlisting #15

Description

@oomokaro1

[SEC] Dynamic CORS + CSP + Merchant Origin Allowlisting

Priority: High

Difficulty: Hard
Estimated Effort: 2-3 days
Relevant Packages: OrbitStream_backend/, orbitstream_docs/
Labels: security, enhancement, priority:high

Requirements

1. Dynamic Origin Allowlisting

  • Add a cors_origins JSONB column to the merchants table
  • Each merchant can configure allowed origins via PATCH /merchants/me/cors
  • The CORS middleware reads from a Redis-backed cache (refreshed every 5 minutes)
  • If the merchant has no configured origins, only the platform domain is allowed

2. Route-Based CORS Policies

  • Public routes (GET /v1/checkout/sessions/:id, POST /merchants/register): Allow all origins
  • Merchant API routes (/v1/checkout/*, /v1/webhooks/*): Allow only the merchant's configured origins + platform domain
  • Dashboard routes (/merchants/*, /auth/*): Allow only the platform domain

3. CORS Middleware

Implement a DynamicCorsMiddleware that:

  • Reads the Origin header
  • Checks against the allowed list for the route group
  • Sets correct headers: Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers
  • Handles preflight OPTIONS with Access-Control-Max-Age: 86400
  • Returns 403 for disallowed origins (not just missing headers)

4. Security Headers

Add these headers to all responses:

  • Strict-Transport-Security: max-age=31536000; includeSubDomains
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • X-XSS-Protection: 1; mode=block
  • Referrer-Policy: strict-origin-when-cross-origin
  • Content-Security-Policy: default-src 'self' (adjust for checkout page)

5. API Endpoints

  • GET /merchants/me/cors — list configured origins (JWT auth)
  • PUT /merchants/me/cors — set allowed origins (JWT auth)
  • Body: { "origins": ["https://myshop.com", "https://staging.myshop.com"] }

6. Testing

  • Unit test: valid origin matches, invalid origin rejected
  • Unit test: preflight OPTIONS handled correctly
  • Unit test: public routes allow all origins
  • Unit test: merchant routes only allow configured origins
  • Unit test: security headers present on all responses
  • Integration test: request from unknown origin gets 403
  • Integration test: request from allowed origin succeeds

Metadata

Metadata

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions