From 5e0239ff61c107e50c9d8e138927483258554152 Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Tue, 18 Aug 2026 18:00:53 +0000 Subject: [PATCH] fix: fallback to open rules when Storage emulator rules are missing ### Description Prevent missing configurations for the Storage emulator from crashing emulators:start and blocking the rest of the emulator suite: - Fall back to default open rules with a warning for Storage emulator when rules are unconfigured on non-demo projects. - Fall back to default open rules with a warning when storage target in firebase.json is not configured in .firebaserc. - Refactor duplicated warning logging logic into a helper function. Fixes b/546204399 ### Scenarios Tested - Unit tests in storage/rules/config.spec.ts for missing storage config, missing rules, and demo/non-demo projects. ### Sample Commands - firebase init emulators (select all emulators) - firebase emulators:start --- CHANGELOG.md | 1 + src/emulator/storage/rules/config.spec.ts | 33 +++++++++++----- src/emulator/storage/rules/config.ts | 46 ++++++++++++++--------- 3 files changed, 52 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b931c8eb79..8252c79447e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- Fall back to default open rules with a warning when Storage emulator rules or targets are unconfigured. - Added extensions replacement registry and scraper tool to track migrations for deprecated extensions ahead of the March 2027 decommission date. - [Added] Loads existing `.env` files and passes environment variables to functions discovery in `runtimeDelegate`. - Adds --immediate flag to ext:uninstall (#10921) diff --git a/src/emulator/storage/rules/config.spec.ts b/src/emulator/storage/rules/config.spec.ts index ba190342bca..0f904553fcd 100644 --- a/src/emulator/storage/rules/config.spec.ts +++ b/src/emulator/storage/rules/config.spec.ts @@ -92,20 +92,33 @@ describe("Storage Rules Config", () => { expect(result[2].rules.content).to.contain("allow read, write: if request.auth!=null"); }); - it("should throw FirebaseError when storage config is missing", () => { + it("should use default config when storage config is missing", () => { const config = getOptions({ data: {}, path: resolvePath }); - expect(() => getStorageRulesConfig(PROJECT_ID, config)).to.throw( - FirebaseError, - "Cannot start the Storage emulator without rules file specified in firebase.json: run 'firebase init' and set up your Storage configuration", - ); + const result = getStorageRulesConfig(PROJECT_ID, config) as SourceFile; + + expect(result.name).to.contain("templates/emulators/default_storage.rules"); + expect(result.content).to.contain("allow read, write;"); }); - it("should throw FirebaseError when rules file is missing", () => { + it("should use default config when rules file is missing", () => { const config = getOptions({ data: { storage: {} }, path: resolvePath }); - expect(() => getStorageRulesConfig(PROJECT_ID, config)).to.throw( - FirebaseError, - "Cannot start the Storage emulator without rules file specified in firebase.json: run 'firebase init' and set up your Storage configuration", - ); + const result = getStorageRulesConfig(PROJECT_ID, config) as SourceFile; + + expect(result.name).to.contain("templates/emulators/default_storage.rules"); + expect(result.content).to.contain("allow read, write;"); + }); + + it("should use default config when target is missing in .firebaserc", () => { + const config = getOptions({ + data: { + storage: [{ target: "missing-target", rules: "main.rules" }], + }, + path: resolvePath, + }); + const result = getStorageRulesConfig(PROJECT_ID, config) as SourceFile; + + expect(result.name).to.contain("templates/emulators/default_storage.rules"); + expect(result.content).to.contain("allow read, write;"); }); it("should throw FirebaseError when rules file is invalid", () => { diff --git a/src/emulator/storage/rules/config.ts b/src/emulator/storage/rules/config.ts index 5f333acd9d3..76f5d0bb51f 100644 --- a/src/emulator/storage/rules/config.ts +++ b/src/emulator/storage/rules/config.ts @@ -26,25 +26,15 @@ export function getStorageRulesConfig( const storageConfig = options.config.data.storage; const storageLogger = EmulatorLogger.forEmulator(Emulators.STORAGE); if (!storageConfig) { - if (Constants.isDemoProject(projectId)) { - storageLogger.logLabeled( - "BULLET", - "storage", - `Detected demo project ID "${projectId}", using a default (open) rules configuration.`, - ); - return defaultStorageRules(); - } - throw new FirebaseError( - "Cannot start the Storage emulator without rules file specified in firebase.json: run 'firebase init' and set up your Storage configuration", - ); + logDefaultRulesWarning(projectId, storageLogger); + return defaultStorageRules(); } // No target specified if (!Array.isArray(storageConfig)) { if (!storageConfig.rules) { - throw new FirebaseError( - "Cannot start the Storage emulator without rules file specified in firebase.json: run 'firebase init' and set up your Storage configuration", - ); + logDefaultRulesWarning(projectId, storageLogger); + return defaultStorageRules(); } return getSourceFile(storageConfig.rules, options); @@ -58,17 +48,21 @@ export function getStorageRulesConfig( } const targets = rc.target(projectId, "storage", targetConfig.target); if (targets.length === 0) { - // Fall back to open if this is a demo project + // Fall back to open if this is a demo project or targets are missing if (Constants.isDemoProject(projectId)) { storageLogger.logLabeled( "BULLET", "storage", `Detected demo project ID "${projectId}", using a default (open) rules configuration. Storage targets in firebase.json will be ignored.`, ); - return defaultStorageRules(); + } else { + storageLogger.logLabeled( + "WARN", + "storage", + `Storage target '${targetConfig.target}' in firebase.json is not configured in .firebaserc. The emulator will default to allowing all reads and writes.`, + ); } - // Otherwise, requireTarget will error out - rc.requireTarget(projectId, "storage", targetConfig.target); + return defaultStorageRules(); } results.push( ...rc.target(projectId, "storage", targetConfig.target).map((resource: string) => { @@ -79,6 +73,22 @@ export function getStorageRulesConfig( return results; } +function logDefaultRulesWarning(projectId: string, storageLogger: EmulatorLogger): void { + if (Constants.isDemoProject(projectId)) { + storageLogger.logLabeled( + "BULLET", + "storage", + `Detected demo project ID "${projectId}", using a default (open) rules configuration.`, + ); + } else { + storageLogger.logLabeled( + "WARN", + "storage", + "Did not find a Storage rules file specified in a firebase.json config file. The emulator will default to allowing all reads and writes. Learn more about this option: https://firebase.google.com/docs/emulator-suite/install_and_configure#security_rules_configuration.", + ); + } +} + function defaultStorageRules(): SourceFile { const defaultRulesPath = "emulators/default_storage.rules"; const name = absoluteTemplateFilePath(defaultRulesPath);