mirror of
https://github.com/bretzel-app/crumbs.git
synced 2026-10-12 12:06:57 +02:00
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:
co-authored by
Claude Opus 4.6
parent
3f81f22026
commit
4f4cc69b9f
@@ -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()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -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'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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()}`
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
Reference in New Issue
Block a user