Require uppercase and numeric setup passwords

This commit is contained in:
2026-07-31 18:47:32 +02:00
parent 6d360ec558
commit e48b034678
3 changed files with 59 additions and 17 deletions
+20 -9
View File
@@ -20,14 +20,25 @@ import { collectSystemStatus } from "./status.js";
import { readUpdateStatus, triggerSystemUpdate } from "./update.js";
import "./types.js";
const credentialsSchema = z.object({
username: z
const usernameSchema = z
.string()
.trim()
.min(3, "Username must contain at least 3 characters")
.max(48, "Username must contain at most 48 characters")
.regex(/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/, "Use letters, numbers, dots, hyphens, or underscores");
const loginCredentialsSchema = z.object({
username: usernameSchema,
password: z.string().min(1).max(256)
});
const setupCredentialsSchema = loginCredentialsSchema.extend({
password: z
.string()
.trim()
.min(3, "Username must contain at least 3 characters")
.max(48, "Username must contain at most 48 characters")
.regex(/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/, "Use letters, numbers, dots, hyphens, or underscores"),
password: z.string().min(12, "Password must contain at least 12 characters").max(256)
.min(8, "Password must contain at least 8 characters")
.max(256)
.regex(/[A-Z]/, "Password must contain at least one uppercase letter")
.regex(/[0-9]/, "Password must contain at least one number")
});
type UserRow = { id: number; username: string; role: "admin"; password_hash: string };
@@ -162,7 +173,7 @@ export async function buildApp(config: AppConfig): Promise<FastifyInstance> {
"/api/auth/setup",
{ config: { rateLimit: { max: 5, timeWindow: "1 minute" } } },
async (request, reply) => {
const parsed = credentialsSchema.safeParse(request.body);
const parsed = setupCredentialsSchema.safeParse(request.body);
if (!parsed.success) {
return reply.code(400).send({
error: { code: "INVALID_CREDENTIALS", message: parsed.error.issues[0]?.message ?? "Invalid account details" }
@@ -203,7 +214,7 @@ export async function buildApp(config: AppConfig): Promise<FastifyInstance> {
"/api/auth/login",
{ config: { rateLimit: { max: 8, timeWindow: "1 minute" } } },
async (request, reply) => {
const parsed = credentialsSchema.safeParse(request.body);
const parsed = loginCredentialsSchema.safeParse(request.body);
const user = parsed.success
? (database
.prepare("SELECT id, username, role, password_hash FROM users WHERE username = ? COLLATE NOCASE")