Skip to content

Commit a363640

Browse files
Follow password policy for Cloud SQL temporary user (#8971)
* feat: Use secure password for Cloud SQL temporary user The temporary password generated for the Cloud SQL superuser now includes at least one uppercase letter, one lowercase letter, one number, and one special character to meet security best practices. Also includes a unit test for the new password generation function and fixes for the ESLint configuration to allow tests to run. * Pr cleanup * revert package.json * use crypto * changelog * More crypto * fencepost --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 6156145 commit a363640

File tree

4 files changed

+47
-2
lines changed

4 files changed

+47
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
- Updated untime config deprecation warning to no longer shows for v1 functions that only use default Firebase config. (#8963)
1+
- Updated runtime config deprecation warning to no longer shows for v1 functions that only use default Firebase config. (#8963)
2+
- Fixed an issue where 'dataconnect:migrate' would use an invalid temporary password when connecting to enterprise plus CloudSQL instances.
23
- Updated Data Connect emulator to v2.11.1, which:
34
- [added] Add an app watch that collects embedded GQL into the connector folder.
45
- [fixed] Handle foreign key constraint error as FailedPrecondition.

src/gcp/cloudsql/connect.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export async function executeSqlCmdsAsSuperUser(
155155
const projectId = needProjectId(options);
156156
// 1. Create a temporary builtin user
157157
const superuser = "firebasesuperuser";
158-
const temporaryPassword = utils.generateId(20);
158+
const temporaryPassword = utils.generatePassword(20);
159159
await cloudSqlAdminClient.createUser(
160160
projectId,
161161
instanceId,

src/utils.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,19 @@ describe("utils", () => {
491491
});
492492
});
493493

494+
describe("generatePassword", () => {
495+
it("should generate a password with the correct properties", () => {
496+
for (let i = 0; i < 100; i++) {
497+
const pw = utils.generatePassword(20);
498+
expect(pw.length).to.equal(20);
499+
expect(pw).to.match(/[a-z]/);
500+
expect(pw).to.match(/[A-Z]/);
501+
expect(pw).to.match(/[0-9]/);
502+
expect(pw).to.match(/[!@#$%^&*()_+~`|}{[\]:;?><,./-=]/);
503+
}
504+
});
505+
});
506+
494507
describe("deepEqual", () => {
495508
it("should return true for identical primitives", () => {
496509
expect(utils.deepEqual(1, 1)).to.be.true;

src/utils.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as tty from "tty";
33
import * as path from "node:path";
44
import * as yaml from "yaml";
55
import { Socket } from "node:net";
6+
import * as crypto from "node:crypto";
67

78
import * as _ from "lodash";
89
import * as url from "url";
@@ -855,6 +856,36 @@ export function generateId(n = 6): string {
855856
return id;
856857
}
857858

859+
/**
860+
* Generate a password meeting the following criterias:
861+
* - At least one lowercase, one uppercase, one number, and one special character.
862+
*/
863+
export function generatePassword(n = 20): string {
864+
const lower = "abcdefghijklmnopqrstuvwxyz";
865+
const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
866+
const numbers = "0123456789";
867+
const special = "!@#$%^&*()_+~`|}{[]:;?><,./-=";
868+
const all = lower + upper + numbers + special;
869+
870+
let pw = "";
871+
pw += lower[crypto.randomInt(lower.length)];
872+
pw += upper[crypto.randomInt(upper.length)];
873+
pw += numbers[crypto.randomInt(numbers.length)];
874+
pw += special[crypto.randomInt(special.length)];
875+
876+
for (let i = 4; i < n; i++) {
877+
pw += all[crypto.randomInt(all.length)];
878+
}
879+
880+
// Shuffle the password to randomize character order using Fisher-Yates shuffle
881+
const pwArray = pw.split("");
882+
for (let i = pwArray.length - 1; i > 0; i--) {
883+
const j = crypto.randomInt(i);
884+
[pwArray[i], pwArray[j]] = [pwArray[j], pwArray[i]];
885+
}
886+
return pwArray.join("");
887+
}
888+
858889
/**
859890
* Reads a secret value from either a file or a prompt.
860891
* If dataFile is falsy and this is a tty, uses prompty. Otherwise reads from dataFile.

0 commit comments

Comments
 (0)