[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
[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:highRequirements
1. Dynamic Origin Allowlisting
cors_originsJSONB column to themerchantstablePATCH /merchants/me/cors2. Route-Based CORS Policies
GET /v1/checkout/sessions/:id,POST /merchants/register): Allow all origins/v1/checkout/*,/v1/webhooks/*): Allow only the merchant's configured origins + platform domain/merchants/*,/auth/*): Allow only the platform domain3. CORS Middleware
Implement a
DynamicCorsMiddlewarethat:OriginheaderAccess-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Allow-HeadersOPTIONSwithAccess-Control-Max-Age: 86400403for disallowed origins (not just missing headers)4. Security Headers
Add these headers to all responses:
Strict-Transport-Security: max-age=31536000; includeSubDomainsX-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockReferrer-Policy: strict-origin-when-cross-originContent-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){ "origins": ["https://myshop.com", "https://staging.myshop.com"] }6. Testing