#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:
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user