diff --git a/CHANGELOG.md b/CHANGELOG.md index 68f89b8d140..c5cadec611d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Pass down `functions.ignore` values in `firebase.json` to functions emulator so that supposedly ignored directories/files will not trigger reload. (#7414) +- Fixes bug where secret values provided in env files were parsed as list (#7422) diff --git a/src/deploy/functions/build.spec.ts b/src/deploy/functions/build.spec.ts index bff09f94301..c1d09b9c000 100644 --- a/src/deploy/functions/build.spec.ts +++ b/src/deploy/functions/build.spec.ts @@ -1,6 +1,6 @@ import { expect } from "chai"; import * as build from "./build"; -import { ParamValue } from "./params"; +import { ParamValue, Param } from "./params"; import { FirebaseError } from "../../error"; describe("toBackend", () => { @@ -186,3 +186,71 @@ describe("toBackend", () => { }).to.throw(FirebaseError, /Value "INVALID" is an invalid egress setting./); }); }); + +describe("envWithType", () => { + it("converts raw environment variables to params with correct type", () => { + const params: Param[] = [ + { + name: "A_STR", + type: "string", + }, + { + name: "AN_INT", + type: "int", + }, + { + name: "A_BOOL", + type: "boolean", + }, + ]; + const rawEnvs: Record = { + A_STR: "foo", + AN_INT: "1", + A_BOOL: "true", + NOT_PARAM: "not-a-param", + }; + const out = build.envWithTypes(params, rawEnvs); + + expect(out).to.include.keys(["A_STR", "AN_INT", "A_BOOL"]); + + expect(out.A_STR.legalString).to.be.true; + expect(out.A_STR.legalBoolean).to.be.false; + expect(out.A_STR.legalNumber).to.be.false; + expect(out.A_STR.legalList).to.be.false; + expect(out.A_STR.asString()).to.equal("foo"); + + expect(out.AN_INT.legalString).to.be.false; + expect(out.AN_INT.legalBoolean).to.be.false; + expect(out.AN_INT.legalNumber).to.be.true; + expect(out.AN_INT.legalList).to.be.false; + expect(out.AN_INT.asNumber()).to.equal(1); + + expect(out.A_BOOL.legalString).to.be.false; + expect(out.A_BOOL.legalBoolean).to.be.true; + expect(out.A_BOOL.legalNumber).to.be.false; + expect(out.A_BOOL.legalList).to.be.false; + expect(out.A_BOOL.asBoolean()).to.be.true; + }); + + it("converts raw environment variable for secret param with correct type", () => { + const params: Param[] = [ + { + name: "WHOOPS_SECRET", + type: "secret", + }, + ]; + const rawEnvs: Record = { + A_STR: "foo", + WHOOPS_SECRET: "super-secret", + }; + const out = build.envWithTypes(params, rawEnvs); + + expect(out).to.include.keys(["WHOOPS_SECRET"]); + + expect(out.WHOOPS_SECRET.legalString).to.be.true; + expect(out.WHOOPS_SECRET.legalBoolean).to.be.false; + expect(out.WHOOPS_SECRET.legalNumber).to.be.false; + expect(out.WHOOPS_SECRET.legalList).to.be.false; + expect(out.WHOOPS_SECRET.asString()).to.equal("super-secret"); + }); +}); diff --git a/src/deploy/functions/build.ts b/src/deploy/functions/build.ts index 7b1519f28eb..e528e85023a 100644 --- a/src/deploy/functions/build.ts +++ b/src/deploy/functions/build.ts @@ -73,7 +73,7 @@ export interface HttpsTrigger { // Trigger definitions for RPCs servers using the HTTP protocol defined at // https://firebase.google.com/docs/functions/callable-reference // eslint-disable-next-line -interface CallableTrigger { } +interface CallableTrigger {} // Trigger definitions for endpoints that should be called as a delegate for other operations. // For example, before user login. @@ -301,7 +301,8 @@ export async function resolveBackend( return { backend: toBackend(build, paramValues), envs: paramValues }; } -function envWithTypes( +// Exported for testing +export function envWithTypes( definedParams: params.Param[], rawEnvs: Record, ): Record { @@ -344,6 +345,16 @@ function envWithTypes( number: false, list: true, }; + } else if (param.type === "secret") { + // NOTE(danielylee): Secret values are not supposed to be + // provided in the env files. However, users may do it anyway. + // Secret values will be provided as strings in those cases. + providedType = { + string: true, + boolean: false, + number: false, + list: false, + }; } } }