security: bound password inputs, configure pino redact, patch deps (#36 #46 #58)

#36 CRITICAL: add .max(128) to all password Zod schemas to prevent
Argon2-based DoS from unbounded password strings.

#46 HIGH: configure pino redact paths so passwords/tokens/cookies/TOTP
secrets are never serialized in logs.

#58 MEDIUM: upgrade dompurify to ^3.4.0 and add pnpm overrides for
brace-expansion (>=5.0.5) and esbuild (>=0.25.0) to patch known CVEs.
Vite moderate (path traversal, dev-only) remains — requires vitest 3.x
major upgrade, deferred.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-17 08:13:25 +02:00
parent 0ef9add935
commit 534945f6e3
8 changed files with 110 additions and 304 deletions
+15 -7
View File
@@ -27,8 +27,8 @@ export class InvalidTotpError extends CredentialsSignin {
const LoginSchema = z.object({
email: z.string().email(),
password: z.string().min(1),
totp: z.string().optional(),
password: z.string().min(1).max(128),
totp: z.string().max(16).optional(),
});
const config = {
@@ -83,7 +83,10 @@ const config = {
}
if (!user.isActive) {
logger.warn({ email, userId: user.id, reason: "account_deactivated" }, "Login blocked — account deactivated");
logger.warn(
{ email, userId: user.id, reason: "account_deactivated" },
"Login blocked — account deactivated",
);
void createAuditEntry({
db: prisma,
entityType: "Auth",
@@ -148,10 +151,10 @@ const config = {
}
// Replay-attack prevention: reject if the same 30-second window was already used
const userWithTotp = await prisma.user.findUnique({
const userWithTotp = (await prisma.user.findUnique({
where: { id: user.id },
select: { lastTotpAt: true },
}) as { lastTotpAt: Date | null } | null;
})) as { lastTotpAt: Date | null } | null;
if (
userWithTotp?.lastTotpAt != null &&
Date.now() - userWithTotp.lastTotpAt.getTime() < 30_000
@@ -273,7 +276,10 @@ const config = {
await prisma.activeSession.deleteMany({
where: { id: { in: toDelete.map((s) => s.id) } },
});
logger.info({ userId: user.id, kicked: toDelete.length, maxSessions }, "Kicked oldest sessions");
logger.info(
{ userId: user.id, kicked: toDelete.length, maxSessions },
"Kicked oldest sessions",
);
}
} catch (err) {
// Non-blocking: don't prevent login if session tracking fails
@@ -293,7 +299,9 @@ const config = {
// Remove from active session registry
if (jti) {
void prisma.activeSession.delete({ where: { jti } }).catch(() => { /* already gone */ });
void prisma.activeSession.delete({ where: { jti } }).catch(() => {
/* already gone */
});
}
void createAuditEntry({