From 0693a4195b866d1e0db089331acf6beb99a4dcf5 Mon Sep 17 00:00:00 2001 From: Ephraim Duncan <55143799+ephraimduncan@users.noreply.github.com> Date: Wed, 16 Sep 2026 04:10:55 +0000 Subject: [PATCH] refactor(openpage): simplify the cors policy (#3338) --- apps/openpage-api/lib/cors.ts | 137 +++------------------------------- 1 file changed, 9 insertions(+), 128 deletions(-) diff --git a/apps/openpage-api/lib/cors.ts b/apps/openpage-api/lib/cors.ts index 78dcebde5..707a258b3 100644 --- a/apps/openpage-api/lib/cors.ts +++ b/apps/openpage-api/lib/cors.ts @@ -1,142 +1,23 @@ /** - * Multi purpose CORS lib. - * Note: Based on the `cors` package in npm but using only web APIs. - * Taken from: https://github.com/vercel/examples/blob/main/edge-functions/cors/lib/cors.ts + * Apply the public statistics API's wildcard CORS policy, without credentials. */ - -type StaticOrigin = boolean | string | RegExp | (boolean | string | RegExp)[]; - -type OriginFn = (origin: string | undefined, req: Request) => StaticOrigin | Promise; - -interface CorsOptions { - origin?: StaticOrigin | OriginFn; - methods?: string | string[]; - allowedHeaders?: string | string[]; - exposedHeaders?: string | string[]; - credentials?: boolean; - maxAge?: number; - preflightContinue?: boolean; - optionsSuccessStatus?: number; -} - -const defaultOptions: CorsOptions = { - origin: '*', - methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', - preflightContinue: false, - optionsSuccessStatus: 204, -}; - -function isOriginAllowed(origin: string, allowed: StaticOrigin): boolean { - return Array.isArray(allowed) - ? allowed.some((o) => isOriginAllowed(origin, o)) - : typeof allowed === 'string' - ? origin === allowed - : allowed instanceof RegExp - ? allowed.test(origin) - : !!allowed; -} - -function getOriginHeaders(reqOrigin: string | undefined, origin: StaticOrigin) { - const headers = new Headers(); - - if (origin === '*') { - headers.set('Access-Control-Allow-Origin', '*'); - } else if (typeof origin === 'string') { - headers.set('Access-Control-Allow-Origin', origin); - headers.append('Vary', 'Origin'); - } else { - const allowed = isOriginAllowed(reqOrigin ?? '', origin); - - if (allowed && reqOrigin) { - headers.set('Access-Control-Allow-Origin', reqOrigin); - } - headers.append('Vary', 'Origin'); - } - - return headers; -} - -async function originHeadersFromReq(req: Request, origin: StaticOrigin | OriginFn) { - const reqOrigin = req.headers.get('Origin') || undefined; - const value = typeof origin === 'function' ? await origin(reqOrigin, req) : origin; - - if (!value) { - return; - } - return getOriginHeaders(reqOrigin, value); -} - -function getAllowedHeaders(req: Request, allowed?: string | string[]) { - const headers = new Headers(); - - if (!allowed) { - allowed = req.headers.get('Access-Control-Request-Headers')!; - headers.append('Vary', 'Access-Control-Request-Headers'); - } else if (Array.isArray(allowed)) { - allowed = allowed.join(','); - } - if (allowed) { - headers.set('Access-Control-Allow-Headers', allowed); - } - - return headers; -} - -export default async function cors(req: Request, res: Response, options?: CorsOptions) { - const opts = { ...defaultOptions, ...options }; +export default async function cors(req: Request, res: Response): Promise { const { headers } = res; - const originHeaders = await originHeadersFromReq(req, opts.origin ?? false); - const mergeHeaders = (v: string, k: string) => { - if (k === 'Vary') { - headers.append(k, v); - } else { - headers.set(k, v); - } - }; + headers.set('Access-Control-Allow-Origin', '*'); - // If there's no origin we won't touch the response - if (!originHeaders) { - return res; - } - - originHeaders.forEach(mergeHeaders); - - if (opts.credentials) { - headers.set('Access-Control-Allow-Credentials', 'true'); - } - - const exposed = Array.isArray(opts.exposedHeaders) ? opts.exposedHeaders.join(',') : opts.exposedHeaders; - - if (exposed) { - headers.set('Access-Control-Expose-Headers', exposed); - } - - // Handle the preflight request if (req.method === 'OPTIONS') { - if (opts.methods) { - const methods = Array.isArray(opts.methods) ? opts.methods.join(',') : opts.methods; + headers.set('Access-Control-Allow-Methods', 'GET,HEAD,PUT,PATCH,POST,DELETE'); + headers.set('Vary', 'Access-Control-Request-Headers'); - headers.set('Access-Control-Allow-Methods', methods); - } + const allowedHeaders = req.headers.get('Access-Control-Request-Headers'); - getAllowedHeaders(req, opts.allowedHeaders).forEach(mergeHeaders); - - if (typeof opts.maxAge === 'number') { - headers.set('Access-Control-Max-Age', String(opts.maxAge)); - } - - if (opts.preflightContinue) { - return res; + if (allowedHeaders) { + headers.set('Access-Control-Allow-Headers', allowedHeaders); } headers.set('Content-Length', '0'); - return new Response(null, { status: opts.optionsSuccessStatus, headers }); + return new Response(null, { status: 204, headers }); } - // If we got here, it's a normal request return res; } - -export function initCors(options?: CorsOptions) { - return async (req: Request, res: Response) => cors(req, res, options); -}