From 673c7fc630b88de325c74ccca83624a13aa5b38d Mon Sep 17 00:00:00 2001 From: walshy Date: Mon, 19 Oct 2026 19:30:07 -0400 Subject: [PATCH] Fetch the remote config diff from the configured compliance region When a Worker was last deployed from the dashboard, deploy and versions upload fetch its remote configuration (bindings, routes, custom domains, subdomain, environment metadata and cron triggers) to diff against the local config. fetchWorkerConfig passed COMPLIANCE_REGION_CONFIG_UNKNOWN for all six requests, so only the CLOUDFLARE_COMPLIANCE_REGION env var was honoured and the compliance_region field in the Wrangler config was ignored. A FedRAMP High Worker configured via the config file sent these requests to api.cloudflare.com and the deploy failed with 401s. fetchWorkerConfig and downloadWorkerConfig now take a ComplianceConfig. The deploy validation passes the loaded config, and `wrangler init --from-dash` keeps passing COMPLIANCE_REGION_CONFIG_UNKNOWN since it has no config file. --- .changeset/fedramp-remote-config-diff.md | 8 ++++ .../deploy/helpers/download-worker-config.ts | 27 ++++++----- .../deploy/helpers/validate-worker-props.ts | 1 + .../__tests__/deploy/config-remote.test.ts | 46 +++++++++++++++++++ packages/wrangler/src/init.ts | 1 + .../src/utils/download-worker-config.ts | 12 ++++- 6 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 .changeset/fedramp-remote-config-diff.md diff --git a/.changeset/fedramp-remote-config-diff.md b/.changeset/fedramp-remote-config-diff.md new file mode 100644 index 00000000000..9d606dc0112 --- /dev/null +++ b/.changeset/fedramp-remote-config-diff.md @@ -0,0 +1,8 @@ +--- +"@cloudflare/deploy-helpers": patch +"wrangler": patch +--- + +Fetch the remote config diff from the configured compliance region + +When a Worker was last deployed from the dashboard, `wrangler deploy` and `wrangler versions upload` download its remote configuration to compare with the local one. These requests ignored the `compliance_region` field in the Wrangler configuration file and only honoured the `CLOUDFLARE_COMPLIANCE_REGION` environment variable. A FedRAMP High Worker configured through the config file would send them to `api.cloudflare.com` instead of `api.fed.cloudflare.com`, and the deploy would fail with authentication errors. They now use the same compliance region as the rest of the deploy. diff --git a/packages/deploy-helpers/src/deploy/helpers/download-worker-config.ts b/packages/deploy-helpers/src/deploy/helpers/download-worker-config.ts index d98737385c7..ed6b40b6df2 100644 --- a/packages/deploy-helpers/src/deploy/helpers/download-worker-config.ts +++ b/packages/deploy-helpers/src/deploy/helpers/download-worker-config.ts @@ -1,9 +1,7 @@ -import { - COMPLIANCE_REGION_CONFIG_UNKNOWN, - constructWranglerConfig, -} from "@cloudflare/workers-utils"; +import { constructWranglerConfig } from "@cloudflare/workers-utils"; import { fetchResult } from "../../shared/context"; import type { + ComplianceConfig, RawConfig, ServiceMetadataRes, WorkerMetadata, @@ -44,6 +42,7 @@ type RoutesRes = { * Downloads all information required to construct a Wrangler config file for a Worker from the API */ export async function fetchWorkerConfig( + complianceConfig: ComplianceConfig, accountId: string, workerName: string, environment: string @@ -57,27 +56,27 @@ export async function fetchWorkerConfig( cronTriggers, ] = await Promise.all([ fetchResult( - COMPLIANCE_REGION_CONFIG_UNKNOWN, + complianceConfig, `/accounts/${accountId}/workers/services/${workerName}/environments/${environment}/bindings` ), fetchResult( - COMPLIANCE_REGION_CONFIG_UNKNOWN, + complianceConfig, `/accounts/${accountId}/workers/services/${workerName}/environments/${environment}/routes?show_zonename=true` ), fetchResult( - COMPLIANCE_REGION_CONFIG_UNKNOWN, + complianceConfig, `/accounts/${accountId}/workers/domains/records?page=0&per_page=5&service=${workerName}&environment=${environment}` ), fetchResult( - COMPLIANCE_REGION_CONFIG_UNKNOWN, + complianceConfig, `/accounts/${accountId}/workers/services/${workerName}/environments/${environment}/subdomain` ), fetchResult( - COMPLIANCE_REGION_CONFIG_UNKNOWN, + complianceConfig, `/accounts/${accountId}/workers/services/${workerName}/environments/${environment}` ), fetchResult( - COMPLIANCE_REGION_CONFIG_UNKNOWN, + complianceConfig, `/accounts/${accountId}/workers/scripts/${workerName}/schedules` ), ]).catch((e) => { @@ -101,6 +100,7 @@ export async function fetchWorkerConfig( * approximates what a wrangler config object for the worker was/would have been. */ export async function downloadWorkerConfig( + complianceConfig: ComplianceConfig, workerName: string, environment: string, entrypoint: string, @@ -113,7 +113,12 @@ export async function downloadWorkerConfig( subdomainStatus, serviceEnvMetadata, cronTriggers, - } = await fetchWorkerConfig(accountId, workerName, environment); + } = await fetchWorkerConfig( + complianceConfig, + accountId, + workerName, + environment + ); return constructWranglerConfig({ name: workerName, diff --git a/packages/deploy-helpers/src/deploy/helpers/validate-worker-props.ts b/packages/deploy-helpers/src/deploy/helpers/validate-worker-props.ts index b7b71bc869f..123211d6b2f 100644 --- a/packages/deploy-helpers/src/deploy/helpers/validate-worker-props.ts +++ b/packages/deploy-helpers/src/deploy/helpers/validate-worker-props.ts @@ -232,6 +232,7 @@ export async function preUploadApiChecks( if (script.last_deployed_from === "dash") { const remoteWorkerConfig = await downloadWorkerConfig( + config, name, serviceMetaData.default_environment.environment, props.entry.file, diff --git a/packages/wrangler/src/__tests__/deploy/config-remote.test.ts b/packages/wrangler/src/__tests__/deploy/config-remote.test.ts index 95f4a82db88..261827cbc88 100644 --- a/packages/wrangler/src/__tests__/deploy/config-remote.test.ts +++ b/packages/wrangler/src/__tests__/deploy/config-remote.test.ts @@ -706,5 +706,51 @@ describe("deploy", () => { // to set a non-zero exit code expect(process.exitCode).not.toBe(0); }); + + it.for([ + { source: "the config file", complianceRegionInConfig: true }, + { source: "an env var", complianceRegionInConfig: false }, + ])( + "should fetch the remote config from the FedRAMP High region when it is set in $source", + async ({ complianceRegionInConfig }, { expect }) => { + if (!complianceRegionInConfig) { + vi.stubEnv("CLOUDFLARE_COMPLIANCE_REGION", "fedramp_high"); + } + + writeWorkerSource(); + mockGetServiceByName("test-name", "production", "dash"); + writeWranglerConfig({ + compatibility_date: "2024-04-24", + main: "./index.js", + ...(complianceRegionInConfig && { + compliance_region: "fedramp_high", + }), + }); + mockSubDomainRequest(); + mockUploadWorkerRequest({ expectedBaseUrl: "api.fed.cloudflare.com" }); + mockGetServiceBindings("test-name", []); + mockGetServiceRoutes("test-name", []); + mockGetServiceCustomDomainRecords([]); + mockGetServiceSubDomainData("test-name", { + enabled: true, + previews_enabled: true, + }); + mockGetServiceSchedules("test-name", { schedules: [] }); + mockGetServiceMetadata("test-name", { + script: { compatibility_date: "2024-04-24" }, + } as unknown as ServiceMetadataRes["default_environment"]); + + const requestedHosts = new Set(); + msw.use( + http.all("*", ({ request }) => { + requestedHosts.add(new URL(request.url).host); + }) + ); + + await runWrangler("deploy"); + + expect([...requestedHosts]).toEqual(["api.fed.cloudflare.com"]); + } + ); }); }); diff --git a/packages/wrangler/src/init.ts b/packages/wrangler/src/init.ts index 111d97b4000..16b655fdaa7 100644 --- a/packages/wrangler/src/init.ts +++ b/packages/wrangler/src/init.ts @@ -195,6 +195,7 @@ export async function downloadWorker(accountId: string, workerName: string) { ); const config = await downloadWorkerConfig( + COMPLIANCE_REGION_CONFIG_UNKNOWN, workerName, defaultEnvironment, entrypoint, diff --git a/packages/wrangler/src/utils/download-worker-config.ts b/packages/wrangler/src/utils/download-worker-config.ts index cdfc29ae2d7..bdf514beb6f 100644 --- a/packages/wrangler/src/utils/download-worker-config.ts +++ b/packages/wrangler/src/utils/download-worker-config.ts @@ -2,23 +2,31 @@ import { downloadWorkerConfig as downloadWorkerConfigBase, fetchWorkerConfig as fetchWorkerConfigBase, } from "@cloudflare/deploy-helpers"; -import type { RawConfig } from "@cloudflare/workers-utils"; +import type { ComplianceConfig, RawConfig } from "@cloudflare/workers-utils"; export async function fetchWorkerConfig( + complianceConfig: ComplianceConfig, accountId: string, workerName: string, environment: string ) { - return fetchWorkerConfigBase(accountId, workerName, environment); + return fetchWorkerConfigBase( + complianceConfig, + accountId, + workerName, + environment + ); } export async function downloadWorkerConfig( + complianceConfig: ComplianceConfig, workerName: string, environment: string, entrypoint: string, accountId: string ): Promise { return downloadWorkerConfigBase( + complianceConfig, workerName, environment, entrypoint,