From bd39c736724a29d5944cf9c662ffec6ab6577b24 Mon Sep 17 00:00:00 2001 From: Kouji Takao Date: Mon, 19 Jan 2026 00:14:54 +0900 Subject: [PATCH] fix: add WAF rule for CORS preflight OPTIONS requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added AllowPreflightOptions rule (priority: 0) to allow OPTIONS requests - Fixed SingleHeader property name (name → Name) - Fixed TextTransformationType (UPPERCASE → NONE for method matching) - Ensures AppSync can return CORS headers for preflight requests This fixes the CORS error reported on Chromebook when connecting to production mesh v2 API (https://graphql.api.smalruby.app/graphql). WAF rules now properly handle: 1. Preflight OPTIONS requests from allowed origins (priority 0) 2. Regular POST requests from allowed origins (priority 1) 3. Block all other requests (default action) Tested on stg2 environment: - OPTIONS from https://smalruby.app → 200 + CORS headers ✅ - POST from https://smalruby.app → 200 + data ✅ - POST from https://example.com → 403 (blocked) ✅ Fixes #30 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Sonnet 4.5 --- lib/mesh-v2-stack.ts | 54 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/lib/mesh-v2-stack.ts b/lib/mesh-v2-stack.ts index aa2645d..17abe6c 100644 --- a/lib/mesh-v2-stack.ts +++ b/lib/mesh-v2-stack.ts @@ -151,6 +151,58 @@ export class MeshV2Stack extends cdk.Stack { sampledRequestsEnabled: true, }, rules: [ + { + name: 'AllowPreflightOptions', + priority: 0, + action: { allow: {} }, + statement: { + andStatement: { + statements: [ + { + byteMatchStatement: { + fieldToMatch: { + method: {}, + }, + positionalConstraint: 'EXACTLY', + searchString: 'OPTIONS', + textTransformations: [ + { + priority: 0, + type: 'NONE', + }, + ], + }, + }, + { + orStatement: { + statements: allowedOrigins.map(origin => ({ + byteMatchStatement: { + fieldToMatch: { + singleHeader: { + Name: 'origin', + }, + }, + positionalConstraint: 'EXACTLY', + searchString: origin, + textTransformations: [ + { + priority: 0, + type: 'LOWERCASE', + }, + ], + }, + })), + }, + }, + ], + }, + }, + visibilityConfig: { + cloudWatchMetricsEnabled: true, + metricName: 'AllowPreflightOptions', + sampledRequestsEnabled: true, + }, + }, { name: 'AllowSpecificOrigins', priority: 1, @@ -161,7 +213,7 @@ export class MeshV2Stack extends cdk.Stack { byteMatchStatement: { fieldToMatch: { singleHeader: { - name: 'origin', + Name: 'origin', }, }, positionalConstraint: 'EXACTLY',