security: reject known-insecure default passwords during admin seeding (#123)

The admin seeding function previously fell back to password 'admin' when
AUTH_PASS was unset, and accepted any value from .env.example including
the documented default 'change-me-on-first-login'. This meant a user who
copied .env.example without changing the password (or forgot to set
AUTH_PASS entirely) would have an instance running with publicly known
credentials.

The seeding function now:
- Skips seeding entirely if AUTH_PASS is not set (instead of defaulting
  to 'admin')
- Checks AUTH_PASS against a blocklist of known insecure values
  (admin, password, change-me-on-first-login, changeme, testpass123)
- Logs a clear warning explaining what to do in both cases

Existing instances that already have users in the database are not
affected — the seeding function only runs when the users table is empty.

Signed-off-by: Mark Liu <mark@prove.com.au>
This commit is contained in:
Mark Liu 2026-03-04 12:03:11 +11:00 committed by GitHub
parent 498cb2f8d5
commit 720872a391
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 29 additions and 1 deletions

View File

@ -73,6 +73,16 @@ function initializeSchema() {
interface CountRow { count: number } interface CountRow { count: number }
// Known-insecure passwords that should never be used in production.
// Includes the .env.example default and common placeholder values.
const INSECURE_PASSWORDS = new Set([
'admin',
'password',
'change-me-on-first-login',
'changeme',
'testpass123',
])
function seedAdminUserFromEnv(dbConn: Database.Database): void { function seedAdminUserFromEnv(dbConn: Database.Database): void {
// Skip seeding during `next build` — env vars may not be available yet // Skip seeding during `next build` — env vars may not be available yet
if (process.env.NEXT_PHASE === 'phase-production-build') return if (process.env.NEXT_PHASE === 'phase-production-build') return
@ -81,7 +91,25 @@ function seedAdminUserFromEnv(dbConn: Database.Database): void {
if (count > 0) return if (count > 0) return
const username = process.env.AUTH_USER || 'admin' const username = process.env.AUTH_USER || 'admin'
const password = process.env.AUTH_PASS || 'admin' const password = process.env.AUTH_PASS
if (!password) {
logger.warn(
'AUTH_PASS is not set — skipping admin user seeding. ' +
'Set AUTH_PASS in your .env file to create the initial admin account.'
)
return
}
if (INSECURE_PASSWORDS.has(password)) {
logger.warn(
`AUTH_PASS matches a known insecure default ("${password}"). ` +
'Please set a strong, unique password in your .env file. ' +
'Skipping admin user seeding until credentials are changed.'
)
return
}
const displayName = username.charAt(0).toUpperCase() + username.slice(1) const displayName = username.charAt(0).toUpperCase() + username.slice(1)
dbConn.prepare(` dbConn.prepare(`