Skip to content

.env-based secret param binding for Functions#10714

Open
Berlioz wants to merge 19 commits into
mainfrom
vsfan_env_params_use
Open

.env-based secret param binding for Functions#10714
Berlioz wants to merge 19 commits into
mainfrom
vsfan_env_params_use

Conversation

@Berlioz

@Berlioz Berlioz commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Pulls secrets from the Functions .env files and propagates them into the Functions Build's SecretEnvVars (and from there, into the resolved Backend, and from there, into the JSON uploaded to Cloud Functions/Cloud Run).

Params support not included in this PR, to reduce the size of the review. This is obviously going to be a terrible user experience before params are supported (hence the experiment flag) but this should be enough to test the actual binding behavior.

Quick guide to file change intent:

environments.ts - Adds an experiment flag which by default prevents any changes from being made to the Build. If the experiment is not enabled, FIREBASE_SECRET_REF_ envs will still be accepted by the parser, but they'll do nothing (and will not appear in process.env)

env.ts - Modifies the key validation logic to allow keys that begin with FIREBASE_SECRET_REF_ even though FIREBASE_ is reserved. Otherwise knows nothing about this feature.

prepare.ts - Partitions the key-value pairs passed from env.ts into the normal entries and the secrets, which have FIREBASE_SECRET_REF_ stripped from the key at this point. If feature flag enabled, calls build.applyEnvSecretOverrides()

build.ts - Implements applyEnvSecretOverrides, which mutates a Build's state to upsert .env-defined secrets into SecretEnvVars before the Build is compiled into a Functions Backend. These secrets contain an allowVersionPinning sentinel value if the version is specified, which will be relevant real soon now.

backend.ts - Adds the allowVersionPinning to a Backend's version of a SecretEnvVar so it can propagate to...

validate.ts - Bypasses the logic that forces secret versions to always resolve to the current latest if allowVersionPinning is set.

gemini-code-assist[bot]

This comment was marked as outdated.

@firebase firebase deleted a comment from gemini-code-assist Bot Jun 26, 2026
@wiz-9635d3485b

wiz-9635d3485b Bot commented Jun 26, 2026

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 1 Medium
Software Management Finding Software Management Findings -
Total 1 Medium

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension.

@firebase firebase deleted a comment from gemini-code-assist Bot Jun 26, 2026
@firebase firebase deleted a comment from gemini-code-assist Bot Jun 26, 2026
@Berlioz
Berlioz force-pushed the vsfan_env_params_use branch from 14c6bec to d479b2d Compare June 26, 2026 22:26
@Berlioz
Berlioz requested review from ajperel and inlined June 26, 2026 22:51
Comment thread src/deploy/functions/build.ts Outdated
"^" + // start of string
"csm:\/\/" + // scheme csm://
"([a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)" + // secret ID is a GCP resource ID
"(?:\/([-a-z0-9]+))?" + // capture an optional GCP label for version

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It is either a number or "latest"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Discussed over chat: GCP implemented a new feature where users could create arbitrary aliases for specific versions.

foo: "bar",
FIREBASE_SECRET_REF_baz: "quux",
} as Record<string, string>;
const { userEnvs: userEnvs, secretRefs: secretRefs } = prepare.partitionUserEnvs(input);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

FYI: functional.ts already has a partition function. It may be worth just making that support partitioning objects not just arrays.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Actually, I stand corrected; functional.ts already has partitionRecord which seems like it should be used instead of a custom function.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's almost there but the fact that partitionUserEnvs modifies the keys in the process of partitioning (to strip the special key prefix) makes it kind of annoying to use directly. You'd have to take a second pass with something that basically does a mapObject but mutates keys instead of values. Nothing I tried looked any more readable than just having a custom function, unfortunately.

Comment thread src/deploy/functions/prepare.ts
Comment thread src/experiments.ts
default: false,
public: true,
},
secretEnvParams: {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is the rationale for keeping this experiment guarded? This won't affect any existing behavior because the key was previously disallowed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Uh at first this was written before API review so I had to guard it in case it was merged before formal approval. I actually do like the idea of guarding it until the params implementation is done though, since anyone who tries to use this without param prompting is in for some undefined and unpleasant behavior.

Comment thread src/deploy/functions/build.ts Outdated
Comment thread src/deploy/functions/build.ts Outdated
Comment thread src/deploy/functions/build.spec.ts Outdated
Comment thread src/deploy/functions/build.spec.ts Outdated
Comment thread src/deploy/functions/build.spec.ts
export const SECRET_REF_PREFIX = "FIREBASE_SECRET_REF_";
// prettier-ignore
const GCP_RESOURCE_ID_PATTERN = "([a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)"
export const SECRET_REF_SHORT_RE = new RegExp(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Wow... took me so long to find something canonical but https://docs.cloud.google.com/secret-manager/docs/reference/rest/v1/projects.secrets/create#query-parameters does say that upper case letters are allowed. Also, there's no mention that it must start with an alpha

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good find. The validation predicator on the internal proto also matches that.

Comment thread src/functions/env.ts Outdated
const FUNCTIONS_EMULATOR_DOTENV = ".env.local";

const RESERVED_PREFIXES = ["X_GOOGLE_", "FIREBASE_", "EXT_"];
const RESERVED_KEY_PREFIXES = ["FIREBASE_SECRET_REF"];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: isn't a trailing _ part of the prefix too? Also, I wonder if there's a better name for this that makes it clearer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Honestly, everything I thought of sounded clunky. RESERVED_PREFIX_ALLOWLIST, maybe?

Comment thread src/functions/env.ts Outdated
Comment thread src/deploy/functions/prepare.ts Outdated
Comment thread src/deploy/functions/build.ts Outdated
Comment thread src/deploy/functions/prepare.ts Outdated
.filter((e) => e.codebase === codebase || e.codebase === undefined);
await resolveDefaultRegionsForBuild(wantBuild, backend.of(...relevantEndpoints));

if (experiments.isEnabled("secretEnvParams")) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think I was wrong regarding whether this is good to have as an experiment because it can change existing behavior, but not here. I would assume that the best place to secret-guard is when we prompt for a secret value in resolving the build, but if they already have a codebase that uses secret refs, just move forwards with it. This prevents weird errors where we're collaborating on a codebase and it works on my computer but not yours.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I can see that. Didn't consider the collaboration angle.

@Berlioz
Berlioz force-pushed the vsfan_env_params_use branch from e9833dd to 32ec1ee Compare July 8, 2026 22:31
@Berlioz
Berlioz requested a review from inlined July 9, 2026 20:58
Comment thread src/deploy/functions/build.ts Outdated
);
}
let notFound = true;
endpoint.secretEnvironmentVariables?.forEach((envVar) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

style guide says to use for ... of last I checked.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ack.

* @return An object populated with project id, secret id, and version, with a field undefined if not provided.
*/
export function parseSecretRef(ref: string): ParsedSecretRef {
const shortMatch = SECRET_REF_SHORT_RE.exec(ref);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: Can JS REs do named captures? Would that be a better simplification here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I considered it, but the way i have the last capture group for the version jankily split up between multiple lines of the composed regex (so I can deal with the #/@ special cases) would make it kind of awkward to keep consistent here.

Comment thread src/deploy/functions/prepare.ts Outdated
.filter((e) => e.codebase === codebase || e.codebase === undefined);
await resolveDefaultRegionsForBuild(wantBuild, backend.of(...relevantEndpoints));

const parsedSecretRefs = Object.fromEntries(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is already in functional.ts as mapObject

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ack

foo: "bar",
FIREBASE_SECRET_REF_baz: "quux",
} as Record<string, string>;
const { userEnvs: userEnvs, secretRefs: secretRefs } = prepare.partitionUserEnvs(input);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Actually, I stand corrected; functional.ts already has partitionRecord which seems like it should be used instead of a custom function.

Comment thread src/functions/env.ts Outdated
@@ -10,6 +10,7 @@ import { logBullet, logWarning } from "../utils";
const FUNCTIONS_EMULATOR_DOTENV = ".env.local";

const RESERVED_PREFIXES = ["X_GOOGLE_", "FIREBASE_", "EXT_"];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe it would be good to add KIT_ to this now and see if anyone yells before we start depending on it being a reserved prefix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oooh, that's a good idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants