fix: replace hardcoded test passwords with runtime-generated UUIDs

Generate random passwords in global-setup and share via temp file so
all Playwright workers use the same credentials. Screenshot scripts
use module-level crypto.randomUUID() since they run single-process.
Unit test auth passwords also use randomUUID().

Eliminates security scanner alerts for passwords committed to git.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Frederic Cabassut
2026-03-23 10:29:45 +01:00
co-authored by Claude Opus 4.6
parent 3f81f22026
commit 4f4cc69b9f
5 changed files with 31 additions and 11 deletions
+2 -2
View File
@@ -8,11 +8,11 @@ export const MOBILE_VIEWPORT = { width: 390, height: 844 };
export const ADMIN = { export const ADMIN = {
email: 'admin@bretzel.app', email: 'admin@bretzel.app',
displayName: 'Bretzel Admin', displayName: 'Bretzel Admin',
password: 'bretzel2026!' password: crypto.randomUUID()
}; };
export const COLLABORATOR = { export const COLLABORATOR = {
email: 'salzig@bretzel.app', email: 'salzig@bretzel.app',
displayName: 'Salzig Bretzel', displayName: 'Salzig Bretzel',
password: 'salzig2026!!' password: crypto.randomUUID()
}; };
+5 -5
View File
@@ -3,7 +3,7 @@ import { createTestDb } from './db/test-helpers.js';
import { users, sessions } from './db/schema.js'; import { users, sessions } from './db/schema.js';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import * as argon2 from 'argon2'; import * as argon2 from 'argon2';
import { randomBytes } from 'crypto'; import { randomBytes, randomUUID } from 'crypto';
import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'; import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
import type * as schema from './db/schema.js'; import type * as schema from './db/schema.js';
@@ -16,16 +16,16 @@ beforeEach(() => {
describe('Auth - Password Hashing', () => { describe('Auth - Password Hashing', () => {
it('should hash and verify a password correctly', async () => { it('should hash and verify a password correctly', async () => {
const password = 'mysecurepassword'; const password = randomUUID();
const hash = await argon2.hash(password); const hash = await argon2.hash(password);
expect(hash).not.toBe(password); expect(hash).not.toBe(password);
expect(await argon2.verify(hash, password)).toBe(true); expect(await argon2.verify(hash, password)).toBe(true);
expect(await argon2.verify(hash, 'wrongpassword')).toBe(false); expect(await argon2.verify(hash, randomUUID())).toBe(false);
}); });
it('should produce different hashes for the same password', async () => { it('should produce different hashes for the same password', async () => {
const password = 'mysecurepassword'; const password = randomUUID();
const hash1 = await argon2.hash(password); const hash1 = await argon2.hash(password);
const hash2 = await argon2.hash(password); const hash2 = await argon2.hash(password);
@@ -37,7 +37,7 @@ describe('Auth - Password Hashing', () => {
describe('Auth - User Setup', () => { describe('Auth - User Setup', () => {
it('should create a user with hashed password', async () => { it('should create a user with hashed password', async () => {
const password = 'testpassword123'; const password = randomUUID();
const hash = await argon2.hash(password); const hash = await argon2.hash(password);
await db.insert(users).values({ await db.insert(users).values({
+5 -1
View File
@@ -1,4 +1,8 @@
import { test, expect, setupAndLogin } from './helpers/fixtures.js'; import { test, expect, setupAndLogin } from './helpers/fixtures.js';
import { readFileSync } from 'fs';
import { TEST_CREDENTIALS_FILE } from './global-setup';
const { userPassword } = JSON.parse(readFileSync(TEST_CREDENTIALS_FILE, 'utf-8'));
test.describe.serial('Authentication', () => { test.describe.serial('Authentication', () => {
test('Scenario: Unauthenticated user is prompted to set up or log in', async ({ page }) => { test('Scenario: Unauthenticated user is prompted to set up or log in', async ({ page }) => {
@@ -78,7 +82,7 @@ test.describe('Multi-user', () => {
data: { data: {
email: 'user2@test.com', email: 'user2@test.com',
displayName: 'User Two', displayName: 'User Two',
password: 'userpassword123', password: userPassword,
role: 'user' role: 'user'
} }
}); });
+13 -1
View File
@@ -1,6 +1,8 @@
import { existsSync, unlinkSync } from 'fs'; import { existsSync, unlinkSync, writeFileSync } from 'fs';
import { randomUUID } from 'crypto';
const TEST_DB = './data/test-crumbs.db'; const TEST_DB = './data/test-crumbs.db';
export const TEST_CREDENTIALS_FILE = './data/test-credentials.json';
export default function globalSetup() { export default function globalSetup() {
// Clean test database before E2E run so each run starts fresh // Clean test database before E2E run so each run starts fresh
@@ -10,4 +12,14 @@ export default function globalSetup() {
unlinkSync(path); unlinkSync(path);
} }
} }
// Generate random passwords for this test run, shared across all workers
writeFileSync(
TEST_CREDENTIALS_FILE,
JSON.stringify({
testPassword: `test-${randomUUID()}`,
collabPassword: `collab-${randomUUID()}`,
userPassword: `user-${randomUUID()}`
})
);
} }
+6 -2
View File
@@ -1,12 +1,16 @@
import { test as base, expect, type Page, type Locator } from '@playwright/test'; import { test as base, expect, type Page, type Locator } from '@playwright/test';
import { readFileSync } from 'fs';
import { collectCoverage } from './coverage'; import { collectCoverage } from './coverage';
import { TEST_CREDENTIALS_FILE } from '../global-setup';
const { testPassword, collabPassword } = JSON.parse(readFileSync(TEST_CREDENTIALS_FILE, 'utf-8'));
const TEST_EMAIL = 'admin@test.com'; const TEST_EMAIL = 'admin@test.com';
const TEST_PASSWORD = 'testpassword123'; const TEST_PASSWORD = testPassword;
const TEST_DISPLAY_NAME = 'Test Admin'; const TEST_DISPLAY_NAME = 'Test Admin';
const COLLAB_EMAIL = 'collab@test.com'; const COLLAB_EMAIL = 'collab@test.com';
const COLLAB_PASSWORD = 'collabpass123'; const COLLAB_PASSWORD = collabPassword;
const COLLAB_NAME = 'Collaborator User'; const COLLAB_NAME = 'Collaborator User';
/** Find a specific note card by its title text */ /** Find a specific note card by its title text */