From 1fc0e271e8b964a92f2e56c871d5a00c8c0fd5d1 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Thu, 1 May 2025 08:40:56 -0700 Subject: [PATCH 1/2] Allow CORS to be a parameter --- spec/v2/providers/https.spec.ts | 66 +++++++++++++++++++++++++++++++++ src/v2/providers/https.ts | 23 ++++++++++-- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/spec/v2/providers/https.spec.ts b/spec/v2/providers/https.spec.ts index 3685e22b1..9ab5d52a8 100644 --- a/spec/v2/providers/https.spec.ts +++ b/spec/v2/providers/https.spec.ts @@ -32,6 +32,7 @@ import { FULL_ENDPOINT, MINIMAL_V2_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from " import { onInit } from "../../../src/v2/core"; import { Handler } from "express"; import { genkit } from "genkit"; +import { clearParams, defineList, Expression } from "../../../src/params"; function request(args: { data?: any; @@ -227,6 +228,43 @@ describe("onRequest", () => { }); }); + it("should allow cors params", async () => { + const origins = defineList("ORIGINS"); + + try { + process.env.ORIGINS = '["example.com","example2.com"]'; + const func = https.onRequest( + { + cors: origins, + }, + (req, res) => { + res.send("42"); + } + ); + const req = request({ + headers: { + referrer: "example.com", + "content-type": "application/json", + origin: "example.com", + }, + method: "OPTIONS", + }); + + const response = await runHandler(func, req); + + expect(response.status).to.equal(204); + expect(response.headers).to.deep.equal({ + "Access-Control-Allow-Origin": "example.com", + "Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE", + "Content-Length": "0", + Vary: "Origin, Access-Control-Request-Headers", + }); + } finally { + delete process.env.ORIGINS; + clearParams(); + } + }); + it("should add CORS headers if debug feature is enabled", async () => { sinon.stub(debug, "isDebugFeatureEnabled").withArgs("enableCors").returns(true); @@ -301,13 +339,19 @@ describe("onRequest", () => { }); describe("onCall", () => { + let origins: Expression; beforeEach(() => { + origins = defineList("ORIGINS"); + process.env.ORIGINS = '["example.com","example2.com"]'; + options.setGlobalOptions({}); process.env.GCLOUD_PROJECT = "aProject"; }); afterEach(() => { delete process.env.GCLOUD_PROJECT; + delete process.env.ORIGINS; + clearParams(); }); it("should return a minimal trigger/endpoint with appropriate values", () => { @@ -441,6 +485,28 @@ describe("onCall", () => { }); }); + it("should allow cors params", async () => { + const func = https.onCall({ cors: origins }, () => 42); + const req = request({ + headers: { + referrer: "example.com", + "content-type": "application/json", + origin: "example.com", + }, + method: "OPTIONS", + }); + + const response = await runHandler(func, req); + + expect(response.status).to.equal(204); + expect(response.headers).to.deep.equal({ + "Access-Control-Allow-Origin": "example.com", + "Access-Control-Allow-Methods": "POST", + "Content-Length": "0", + Vary: "Origin, Access-Control-Request-Headers", + }); + }); + it("overrides CORS headers if debug feature is enabled", async () => { sinon.stub(debug, "isDebugFeatureEnabled").withArgs("enableCors").returns(true); diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 76ac1e06e..ce468611e 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -72,7 +72,13 @@ export interface HttpsOptions extends Omit; + cors?: + | string + | Expression + | Expression + | boolean + | RegExp + | Array; /** * Amount of memory to allocate to a function. @@ -314,7 +320,7 @@ export function onRequest( } if (isDebugFeatureEnabled("enableCors") || "cors" in opts) { - let origin = opts.cors; + let origin = opts.cors instanceof Expression ? opts.cors.value() : opts.cors; if (isDebugFeatureEnabled("enableCors")) { // Respect `cors: false` to turn off cors even if debug feature is enabled. origin = opts.cors === false ? false : true; @@ -424,7 +430,18 @@ export function onCall, Stream = unknown>( opts = optsOrHandler as CallableOptions; } - let origin = isDebugFeatureEnabled("enableCors") ? true : "cors" in opts ? opts.cors : true; + let cors: string | boolean | RegExp | Array | undefined; + if ("cors" in opts) { + if (opts.cors instanceof Expression) { + cors = opts.cors.value(); + } else { + cors = opts.cors; + } + } else { + cors = true; + } + + let origin = isDebugFeatureEnabled("enableCors") ? true : cors; // Arrays cause the access-control-allow-origin header to be dynamic based // on the origin header of the request. If there is only one element in the // array, this is unnecessary. From 7f356735478d2396a00313abadfe21d45f06a239 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Thu, 1 May 2025 08:43:23 -0700 Subject: [PATCH 2/2] Changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22fb6aa6a..0f2a98365 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ -- Add @deprecated annotation to callable functions's auth policy. +- Add @deprecated annotation to callable functions's auth policy (#1675) +- Allows CORS to be a parameter. (#1688)