Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ module.exports = {
],
},
],
"no-useless-escape": "warn", // TODO(bkendall): remove, allow to error.
},
overrides: [
{
Expand Down
12 changes: 6 additions & 6 deletions scripts/webframeworks-deploy-tests/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
const DEFAULT_LANG = "en";
const LOG_FILE = "firebase-debug.log";
const NEXT_SOURCE = `${__dirname}/nextjs`;
const PATH_SEPARATOR = IS_WINDOWS ? `\\\\` : `\/`;
const PATH_SEPARATOR = IS_WINDOWS ? `\\\\` : `/`;

async function getFilesListFromDir(dir: string): Promise<string[]> {
const files = await glob(`${dir}/**/*`, { nodir: true });
Expand All @@ -35,12 +35,12 @@

before(async () => {
expect(FIREBASE_EMULATOR_HUB, "$FIREBASE_EMULATOR_HUB").to.not.be.empty;
const hubResponse = await fetch(`http://${FIREBASE_EMULATOR_HUB}/emulators`);

Check warning on line 38 in scripts/webframeworks-deploy-tests/tests.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Invalid type "string | undefined" of template literal expression
const {

Check warning on line 39 in scripts/webframeworks-deploy-tests/tests.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unsafe assignment of an `any` value
hosting: { port, host },
} = await hubResponse.json();
NEXTJS_HOST = `http://${host}:${port}/${NEXT_BASE_PATH}`.replace(/\/$/, "");

Check warning on line 42 in scripts/webframeworks-deploy-tests/tests.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Invalid type "any" of template literal expression

Check warning on line 42 in scripts/webframeworks-deploy-tests/tests.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Invalid type "any" of template literal expression
ANGULAR_HOST = `http://${host}:${port + 5}/${ANGULAR_BASE_PATH}`.replace(/\/$/, "");

Check warning on line 43 in scripts/webframeworks-deploy-tests/tests.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Operands of '+' operation with any is possible only with string, number, bigint or any

Check warning on line 43 in scripts/webframeworks-deploy-tests/tests.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Invalid type "any" of template literal expression

Check warning on line 43 in scripts/webframeworks-deploy-tests/tests.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Invalid type "any" of template literal expression
});

after(() => {
Expand Down Expand Up @@ -265,14 +265,14 @@
describe("next.js", () => {
describe("app directory", () => {
it("should have working static routes", async () => {
const apiStaticJSON = JSON.parse(

Check warning on line 268 in scripts/webframeworks-deploy-tests/tests.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unsafe assignment of an `any` value
readFileSync(
join(NEXT_OUTPUT_PATH, "hosting", NEXT_BASE_PATH, "app", "api", "static"),
).toString(),
);
const apiStaticResponse = await fetch(`${NEXTJS_HOST}/app/api/static`);

const jsonResponse = await apiStaticResponse.json();

Check warning on line 275 in scripts/webframeworks-deploy-tests/tests.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unsafe assignment of an `any` value

expect(apiStaticResponse.ok).to.be.true;

Expand Down Expand Up @@ -447,8 +447,8 @@
const distDir = ".next";

const UNEXPECTED_PATTERNS = [
`${distDir}\/cache\/.*-development`,
`${distDir}\/cache\/eslint`,
`${distDir}/cache/.*-development`,
`${distDir}/cache/eslint`,
].map((it) => new RegExp(it));

const files = await getFilesListFromDir(`${NEXT_OUTPUT_PATH}/functions/${distDir}`);
Expand Down Expand Up @@ -503,9 +503,9 @@

const EXPECTED_PATTERNS = ["", "en", "fr", "es"]
.flatMap((locale) => [
[I18N_BASE, locale, ANGULAR_BASE_PATH, `main\.[^\.]+\.js`],
[I18N_BASE, locale, ANGULAR_BASE_PATH, `polyfills\.[^\.]+\.js`],
[I18N_BASE, locale, ANGULAR_BASE_PATH, `styles\.[^\.]+\.css`],
[I18N_BASE, locale, ANGULAR_BASE_PATH, `main.[^.]+.js`],
[I18N_BASE, locale, ANGULAR_BASE_PATH, `polyfills.[^.]+.js`],
[I18N_BASE, locale, ANGULAR_BASE_PATH, `styles.[^.]+.css`],
])
.map((it) => new RegExp(it.filter(Boolean).join(PATH_SEPARATOR)));

Expand Down
2 changes: 1 addition & 1 deletion src/appdistribution/options-parser-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
}

// Ensures a the file path that the user input is valid
export function ensureFileExists(file: string, message = ""): void {

Check warning on line 39 in src/appdistribution/options-parser-util.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Missing JSDoc comment
if (!fs.existsSync(file)) {
throw new FirebaseError(`File ${file} does not exist: ${message}`);
}
Expand Down Expand Up @@ -188,7 +188,7 @@
return (value1 && !value2) || (!value1 && value2);
}

const APP_NAME_REGEX = /^projects\/(?<projectNumber>[^\/]+)\/apps\/(?<appId>[^\/]+)$/;
const APP_NAME_REGEX = /^projects\/(?<projectNumber>[^/]+)\/apps\/(?<appId>[^/]+)$/;
const BUCKET_NAME_FORMAT_REGEX = /^[a-z0-9_.-]+$/;

/**
Expand Down
2 changes: 1 addition & 1 deletion src/apphosting/githubConnections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ interface ConnectionNameParts {

const APPHOSTING_CONN_PATTERN = /.+\/apphosting-github-conn-.+$/;
const CONNECTION_NAME_REGEX =
/^projects\/(?<projectId>[^\/]+)\/locations\/(?<location>[^\/]+)\/connections\/(?<id>[^\/]+)$/;
/^projects\/(?<projectId>[^/]+)\/locations\/(?<location>[^/]+)\/connections\/(?<id>[^/]+)$/;

/**
* Exported for unit testing.
Expand Down
6 changes: 3 additions & 3 deletions src/dataconnect/names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface serviceName {
}

const serviceNameRegex =
/projects\/(?<projectId>[^\/]+)\/locations\/(?<location>[^\/]+)\/services\/(?<serviceId>[^\/]+)/;
/projects\/(?<projectId>[^/]+)\/locations\/(?<location>[^/]+)\/services\/(?<serviceId>[^/]+)/;

export function parseServiceName(serviceName: string): serviceName {
const res = serviceNameRegex.exec(serviceName);
Expand Down Expand Up @@ -38,7 +38,7 @@ interface connectorName {
}

const connectorNameRegex =
/projects\/(?<projectId>[^\/]+)\/locations\/(?<location>[^\/]+)\/services\/(?<serviceId>[^\/]+)\/connectors\/(?<connectorId>[^\/]+)/;
/projects\/(?<projectId>[^/]+)\/locations\/(?<location>[^/]+)\/services\/(?<serviceId>[^/]+)\/connectors\/(?<connectorId>[^/]+)/;

export function parseConnectorName(connectorName: string): connectorName {
const res = connectorNameRegex.exec(connectorName);
Expand Down Expand Up @@ -69,7 +69,7 @@ interface cloudSQLInstanceName {
}

const cloudSQLInstanceNameRegex =
/projects\/(?<projectId>[^\/]+)\/locations\/(?<location>[^\/]+)\/instances\/(?<instanceId>[^\/]+)/;
/projects\/(?<projectId>[^/]+)\/locations\/(?<location>[^/]+)\/instances\/(?<instanceId>[^/]+)/;

export function parseCloudSQLInstanceName(cloudSQLInstanceName: string): cloudSQLInstanceName {
const res = cloudSQLInstanceNameRegex.exec(cloudSQLInstanceName);
Expand Down
2 changes: 1 addition & 1 deletion src/dataconnect/schemaMigration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -866,5 +866,5 @@ function diffsToString(diffs: Diff[]): string {
}

function diffToString(diff: Diff) {
return `\/** ${diff.destructive ? clc.red("Destructive: ") : ""}${diff.description}*\/\n${format(diff.sql, { language: "postgresql" })}`;
return `/** ${diff.destructive ? clc.red("Destructive: ") : ""}${diff.description}*/\n${format(diff.sql, { language: "postgresql" })}`;
}
2 changes: 1 addition & 1 deletion src/deploy/functions/cel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ function preprocessLists(
params: Record<string, ParamValue>,
): CelExpression {
let rv = expr;
const listMatcher = /\[[^\[\]]*\]/g;
const listMatcher = /\[[^[\]]*\]/g;
let match: RegExpMatchArray | null;
while ((match = listMatcher.exec(expr)) != null) {
const list = match[0];
Expand Down
2 changes: 1 addition & 1 deletion src/deploy/functions/runtimes/discovery/v1alpha1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ export function buildFromV1Alpha1(
}

const ROLE_REGEX =
/^(roles\/[a-zA-Z0-9_\.\-]+|projects\/[a-zA-Z0-9\-]+\/roles\/[a-zA-Z0-9_\.\-]+|organizations\/[0-9]+\/roles\/[a-zA-Z0-9_\.\-]+)$/;
/^(roles\/[a-zA-Z0-9_.-]+|projects\/[a-zA-Z0-9-]+\/roles\/[a-zA-Z0-9_.-]+|organizations\/[0-9]+\/roles\/[a-zA-Z0-9_.-]+)$/;

function parseRequiredRoles(manifest: WireManifest): string[] {
const roles: string[] = manifest.requiredRoles || [];
Expand Down
2 changes: 1 addition & 1 deletion src/deploy/lifecycleHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Options } from "../options";
import { isVSCodeExtension } from "../vsCodeUtils";

function runCommand(command: string, childOptions: childProcess.SpawnOptions) {
const escapedCommand = command.replace(/\"/g, '\\"');
const escapedCommand = command.replace(/"/g, '\\"');
Comment thread
joehan marked this conversation as resolved.
Dismissed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION (security)

No change requested here. CodeQL's "Incomplete string escaping or encoding" alert on this line is a re-report, not something this PR introduced. The same alert is already open on main: code-scanning #456 (js/incomplete-sanitization, lifecycleHooks.ts:12, open since 2025-02-26). It shows as "new" only because the line text changed. The behavior is identical: /\"/g and /"/g parse to the same regexpp AST, and sample commands produce byte-identical escapedCommand at both revisions.

It also isn't a real security risk. The input is the developer's own predeploy/postdeploy hook from firebase.json, which is deliberately run through a shell (spawn(translatedCommand, [], { shell: true })), so no trust boundary is crossed. What remains is a correctness quirk: a hook containing \" gets mangled by the outer "..." wrapper (echo \"hi\" becomes echo \\"hi\\").

"Escape backslashes first" is the wrong fix. It would change how every existing hook that contains a backslash or \$ is parsed, and in different ways on POSIX sh and cmd.exe. The right fix is to stop building a shell string, e.g. spawn(nodeExecutable, [crossEnvShellPath, command], { ...childOptions, shell: false }). That should be a separate PR tested on Windows (#11233 also edits this file). CodeQL is not a required check, so a maintainer can dismiss the PR-scoped alert as pre-existing.

Rule: general/codeql-triage

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged. Thank you for the detailed context and CodeQL triage analysis! Leaving this line as-is in this PR.

const nodeExecutable = isVSCodeExtension() ? "node" : process.execPath;
const crossEnvShellPath = isVSCodeExtension()
? path.resolve(__dirname, "./cross-env/dist/bin/cross-env-shell.js")
Expand Down
2 changes: 1 addition & 1 deletion src/emulator/auth/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ export async function createApp(
`Multiple projectIds are not recommended in single project mode. ` +
`Requested project ID ${projectId}, but the emulator is configured for ` +
`${defaultProjectId}. To opt-out of single project mode add/set the ` +
`\'"singleProjectMode"\' false' property in the firebase.json emulators config.`;
`'"singleProjectMode"' false' property in the firebase.json emulators config.`;
EmulatorLogger.forEmulator(Emulators.AUTH).log("WARN", errorString);
if (singleProjectMode === SingleProjectMode.ERROR) {
throw new BadRequestError(errorString);
Expand Down
10 changes: 10 additions & 0 deletions src/emulator/auth/widget_ui.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { expect } from "chai";

import { WIDGET_UI } from "./widget_ui";

describe("WIDGET_UI", () => {
it("should only treat provider IDs with a literal 'saml.' prefix as SAML providers", () => {
// The script is built from a template literal, so the source needs `\\.` for the browser to receive `\.`.
expect(WIDGET_UI).to.include("providerId.match(/^saml\\./)");
});
});
2 changes: 1 addition & 1 deletion src/emulator/auth/widget_ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var firebaseAppId = query.get('appId');
var apn = query.get('apn');
var ibi = query.get('ibi');
var appIdentifier = apn || ibi;
var isSamlProvider = !!providerId.match(/^saml\./);
var isSamlProvider = !!providerId.match(/^saml\\./);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION (reliability)

The fix is correct and intended. I evaluated the cooked SCRIPT at both revisions. The browser now receives /^saml\./ instead of /^saml./, which matches the backend's own check (operations.ts:2867 uses /^saml\./) and the documented saml.* provider-ID format.

Effect on provider IDs:

  • Still treated as SAML: saml.okta, saml..
  • No longer treated as SAML: samlfoo, saml-foo, saml_foo, samlp.x.
  • Unaffected: saml, SAML.okta, oidc.*, and the built-in IDs.

Before, those IDs got an extra SAMLResponse param, which the backend parsed but ignored outside its saml. branch. So no valid configuration changes behavior.

Nothing tests WIDGET_UI, though, and this bug (a template literal silently eating a \) is easy to reintroduce. Optional: add a tiny spec. I tried this one in a scratch worktree: it passes at this head, fails against the base widget_ui.ts, and lints clean.

// src/emulator/auth/widget_ui.spec.ts
import { expect } from "chai";

import { WIDGET_UI } from "./widget_ui";

describe("WIDGET_UI", () => {
  it("should only treat provider IDs with a literal 'saml.' prefix as SAML providers", () => {
    // The script is built from a template literal, so the source needs `\\.` for the browser to receive `\.`.
    expect(WIDGET_UI).to.include("providerId.match(/^saml\\./)");
  });
});

Rule: firebase-tools-pr-review/testing-discipline

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added src/emulator/auth/widget_ui.spec.ts asserting that WIDGET_UI includes providerId.match(/^saml\\./) to prevent template-literal escape regressions.

assert(
appName || clientId || firebaseAppId || appIdentifier,
'Missing one of appName / clientId / appId / apn / ibi query params.'
Expand Down
12 changes: 6 additions & 6 deletions src/emulator/extensions/postinstall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,27 @@ export function replaceConsoleLinks(postinstall: string): string {
let subbedPostinstall = postinstall;
const linkReplacements = new Map<RegExp, string>([
[
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/storage[A-Za-z0-9\/-]*(?=[\)\]\s])/,
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/storage[A-Za-z0-9/-]*(?=[)\]\s])/,
`${uiUrl}${Emulators.STORAGE}`,
], // Storage console links
[
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/firestore[A-Za-z0-9\/-]*(?=[\)\]\s])/,
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/firestore[A-Za-z0-9/-]*(?=[)\]\s])/,
`${uiUrl}${Emulators.FIRESTORE}`,
], // Firestore console links
[
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/database[A-Za-z0-9\/-]*(?=[\)\]\s])/,
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/database[A-Za-z0-9/-]*(?=[)\]\s])/,
`${uiUrl}${Emulators.DATABASE}`,
], // RTDB console links
[
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/authentication[A-Za-z0-9\/-]*(?=[\)\]\s])/,
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/authentication[A-Za-z0-9/-]*(?=[)\]\s])/,
`${uiUrl}${Emulators.AUTH}`,
], // Auth console links
[
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/functions[A-Za-z0-9\/-]*(?=[\)\]\s])/,
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/functions[A-Za-z0-9/-]*(?=[)\]\s])/,
`${uiUrl}logs`, // There is no functions page in the UI, so redirect to logs.
], // Functions console links
[
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/extensions[A-Za-z0-9\/-]*(?=[\)\]\s])/,
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/extensions[A-Za-z0-9/-]*(?=[)\]\s])/,
`${uiUrl}${Emulators.EXTENSIONS}`,
], // Extensions console links
]);
Expand Down
8 changes: 4 additions & 4 deletions src/emulator/functionsEmulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,10 @@ interface EmulatedTriggerRecord {

export function createIgnoredFilter(ignore?: string[]): chokidar.Matcher[] {
const ignored: chokidar.Matcher[] = [
/(^|[\/\\])\../, // Ignore hidden files/dirs (covers .dart_tool, .git, etc.)
/(^|[/\\])\../, // Ignore hidden files/dirs (covers .dart_tool, .git, etc.)
/.+\.log/, // Ignore log files
/.+?[\\\/]node_modules[\\\/].+?/, // Ignore node_modules
/.+?[\\\/]venv[\\\/].+?/, // Ignore venv
/.+?[\\/]node_modules[\\/].+?/, // Ignore node_modules
/.+?[\\/]venv[\\/].+?/, // Ignore venv
];
if (ignore) {
const minimatchInstances = ignore.map((pattern) => {
Expand Down Expand Up @@ -1948,7 +1948,7 @@ export class FunctionsEmulator implements EmulatorInstance {
// req.url = /:projectId/:region/:trigger_name/*
const url = new URL(`${req.protocol}://${req.hostname}${req.url}`);
let path = `${url.pathname}${url.search}`.replace(
new RegExp(`\/${this.args.projectId}\/[^\/]*\/${req.params.trigger_name}\/?`),
new RegExp(`/${this.args.projectId}/[^/]*/${req.params.trigger_name}/?`),
Comment thread
joehan marked this conversation as resolved.
Dismissed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION (security)

No change requested here. The "Regular expression injection" alert is also a re-report. It is open on main as code-scanning #480 (js/regex-injection, functionsEmulator.ts:1951, since 2026-03-18). The cooked template string is byte-identical at both revisions for any projectId/trigger_name. I checked inputs containing ., (, and (a+)+$: an invalid pattern throws the same way at base and head.

In practice it isn't exploitable. handleHttpsTrigger returns 404 at L1910 unless the trigger key is an existing entry in this.triggers: ${region}-${trigger_name}, or just trigger_name on the background route. Prototype keys like constructor stop at the record.enabled check with a 204. So only developer-defined function IDs reach this RegExp, and only on the local emulator. What remains is a robustness issue: regex metacharacters in the interpolated values (e.g. the . in a legacy google.com: project ID) are unescaped.

Proper follow-up: wrap both interpolations in lodash's escapeRegExp (lodash is already a direct dependency), or drop the regex and strip the known /${projectId}/${region}/${trigger} prefix from url.pathname with string operations. Keep that out of this lint-only PR, per b/576834330's scope boundaries, and dismiss the PR-scoped alert as pre-existing.

Rule: general/codeql-triage

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged. Keeping this regex replacement strictly to the useless escape removal and leaving further sanitization/prefix trimming for a dedicated follow-up per b/576834330's scope boundaries.

"/",
);

Expand Down
2 changes: 1 addition & 1 deletion src/emulator/storage/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,6 @@ export class StorageLayer {
/** Returns file separator used in given path, either '\\' or '/'. */
function getPathSep(decodedPath: string): string {
// Checks for the first matching file separator
const firstSepIndex = decodedPath.search(/[\/|\\\\]/g);
const firstSepIndex = decodedPath.search(/[/|\\\\]/g);
return decodedPath[firstSepIndex];
}
4 changes: 2 additions & 2 deletions src/extensions/extensionsHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const resourceTypeToNiceName: Record<string, string> = {
"firebaseextensions.v1beta.function": "Cloud Function",
};
export type ReleaseStage = "alpha" | "beta" | "rc" | "stable";
const repoRegex = new RegExp(`^https:\/\/github\.com\/[^\/]+\/[^\/]+$`);
const repoRegex = new RegExp(`^https://github.com/[^/]+/[^/]+$`);
const stageOptions = ["rc", "alpha", "beta", "stable"];

/**
Expand Down Expand Up @@ -362,7 +362,7 @@ export function validateSpec(spec: any) {
export async function promptForValidInstanceId(instanceId: string): Promise<string> {
let instanceIdIsValid = false;
let newInstanceId = "";
const instanceIdRegex = /^[a-z][a-z\d\-]*[a-z\d]$/;
const instanceIdRegex = /^[a-z][a-z\d-]*[a-z\d]$/;
while (!instanceIdIsValid) {
newInstanceId = await input({
default: instanceId,
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/paramHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ export function readEnvFile(envPath: string): Record<string, string> {
}

export function isSystemParam(paramName: string): boolean {
const regex = /^firebaseextensions\.[a-zA-Z0-9\.]*\//;
const regex = /^firebaseextensions\.[a-zA-Z0-9.]*\//;
return regex.test(paramName);
}

Expand Down
2 changes: 1 addition & 1 deletion src/extensions/provisioningHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export function getUsedProducts(spec: ExtensionSpec): DeferredProduct[] {
* Parses out trigger eventType from the propertiesYaml.
*/
function getTriggerType(propertiesYaml: string | undefined) {
return propertiesYaml?.match(/eventType:\ ([\S]+)/)?.[1];
return propertiesYaml?.match(/eventType: ([\S]+)/)?.[1];
}

async function isStorageProvisioned(projectId: string): Promise<boolean> {
Expand Down
6 changes: 3 additions & 3 deletions src/firestore/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ interface FieldName {
}

// projects/$PROJECT_ID/databases/$DATABASE_ID
const DATABASE_NAME_REGEX = /projects\/([^\/]+?)\/databases\/([^\/]+)/;
const DATABASE_NAME_REGEX = /projects\/([^/]+?)\/databases\/([^/]+)/;

// projects/$PROJECT_ID/databases/$DATABASE_ID/collectionGroups/$COLLECTION_GROUP_ID/indexes/$INDEX_ID
const INDEX_NAME_REGEX =
/projects\/([^\/]+?)\/databases\/([^\/]+?)\/collectionGroups\/([^\/]+?)\/indexes\/([^\/]*)/;
/projects\/([^/]+?)\/databases\/([^/]+?)\/collectionGroups\/([^/]+?)\/indexes\/([^/]*)/;

// projects/$PROJECT_ID/databases/$DATABASE_ID/collectionGroups/$COLLECTION_GROUP_ID/fields/$FIELD_ID
const FIELD_NAME_REGEX =
/projects\/([^\/]+?)\/databases\/([^\/]+?)\/collectionGroups\/([^\/]+?)\/fields\/([^\/]*)/;
/projects\/([^/]+?)\/databases\/([^/]+?)\/collectionGroups\/([^/]+?)\/fields\/([^/]*)/;

/**
* Parse a Database name into useful pieces.
Expand Down
2 changes: 1 addition & 1 deletion src/frameworks/angular/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ async function localesForTarget(
for (const [locale, { baseHref }] of Object.entries(i18n.locales)) {
if (baseHref !== "")
throw new FirebaseError(
`All your i18n locales must have a baseHref of \"\" on Firebase, errored on ${locale}.`,
`All your i18n locales must have a baseHref of "" on Firebase, errored on ${locale}.`,
);
locales.push(locale);
}
Expand Down
Loading
Loading