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
+1 -1
View File
@@ -31,7 +31,7 @@
"@trpc/server": "^11.0.0",
"@types/qrcode": "^1.5.6",
"clsx": "^2.1.1",
"dompurify": "^3.3.3",
"dompurify": "^3.4.0",
"exceljs": "^4.4.0",
"framer-motion": "^12.38.0",
"next": "^15.5.15",
+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({
+3 -1
View File
@@ -55,7 +55,9 @@
"overrides": {
"flatted": "^3.4.2",
"picomatch": "^4.0.4",
"lodash-es": "^4.18.0"
"lodash-es": "^4.18.0",
"brace-expansion": "^5.0.5",
"esbuild@<0.25.0": ">=0.25.0"
}
},
"packageManager": "pnpm@9.14.2",
+38
View File
@@ -5,15 +5,53 @@ const isProduction = process.env["NODE_ENV"] === "production";
const LOG_LEVEL = process.env["LOG_LEVEL"] ?? "info";
const devDestination = pino.destination({ dest: 1, sync: true });
const REDACT_PATHS = [
"password",
"*.password",
"*.*.password",
"newPassword",
"*.newPassword",
"currentPassword",
"*.currentPassword",
"passwordHash",
"*.passwordHash",
"token",
"*.token",
"*.*.token",
"accessToken",
"*.accessToken",
"refreshToken",
"*.refreshToken",
"apiKey",
"*.apiKey",
"authorization",
"*.authorization",
"cookie",
"*.cookie",
"totp",
"*.totp",
"totpSecret",
"*.totpSecret",
"secret",
"*.secret",
"req.headers.authorization",
"req.headers.cookie",
'res.headers["set-cookie"]',
];
const redactConfig = { paths: REDACT_PATHS, censor: "[REDACTED]" };
export const logger = isProduction
? pino({
level: LOG_LEVEL,
base: { service: "capakraken-api" },
redact: redactConfig,
})
: pino(
{
level: LOG_LEVEL,
base: { service: "capakraken-api" },
redact: redactConfig,
formatters: {
level(label: string) {
return { level: label };
+1 -1
View File
@@ -74,7 +74,7 @@ export const authRouter = createTRPCRouter({
.input(
z.object({
token: z.string().min(1),
password: z.string().min(12, "Password must be at least 12 characters."),
password: z.string().min(12, "Password must be at least 12 characters.").max(128),
}),
)
.mutation(async ({ ctx, input }) => {
+13 -6
View File
@@ -99,8 +99,10 @@ export const inviteRouter = createTRPCRouter({
select: { email: true, role: true, expiresAt: true, usedAt: true },
});
if (!invite) throw new TRPCError({ code: "NOT_FOUND", message: "Invite not found." });
if (invite.usedAt) throw new TRPCError({ code: "BAD_REQUEST", message: "This invite has already been used." });
if (invite.expiresAt < new Date()) throw new TRPCError({ code: "BAD_REQUEST", message: "This invite has expired." });
if (invite.usedAt)
throw new TRPCError({ code: "BAD_REQUEST", message: "This invite has already been used." });
if (invite.expiresAt < new Date())
throw new TRPCError({ code: "BAD_REQUEST", message: "This invite has expired." });
return { email: invite.email, role: invite.role };
}),
@@ -109,7 +111,7 @@ export const inviteRouter = createTRPCRouter({
.input(
z.object({
token: z.string(),
password: z.string().min(12, "Password must be at least 12 characters."),
password: z.string().min(12, "Password must be at least 12 characters.").max(128),
}),
)
.mutation(async ({ ctx, input }) => {
@@ -125,13 +127,18 @@ export const inviteRouter = createTRPCRouter({
where: { token: input.token },
});
if (!invite) throw new TRPCError({ code: "NOT_FOUND", message: "Invite not found." });
if (invite.usedAt) throw new TRPCError({ code: "BAD_REQUEST", message: "This invite has already been used." });
if (invite.expiresAt < new Date()) throw new TRPCError({ code: "BAD_REQUEST", message: "This invite has expired." });
if (invite.usedAt)
throw new TRPCError({ code: "BAD_REQUEST", message: "This invite has already been used." });
if (invite.expiresAt < new Date())
throw new TRPCError({ code: "BAD_REQUEST", message: "This invite has expired." });
// Check if user already exists
const existing = await ctx.db.user.findUnique({ where: { email: invite.email } });
if (existing) {
throw new TRPCError({ code: "CONFLICT", message: "An account with this email already exists." });
throw new TRPCError({
code: "CONFLICT",
message: "An account with this email already exists.",
});
}
const { hash } = await import("@node-rs/argon2");
@@ -10,12 +10,12 @@ export const CreateUserInputSchema = z.object({
email: z.string().email(),
name: z.string().min(1),
systemRole: z.nativeEnum(SystemRole).default(SystemRole.USER),
password: z.string().min(12),
password: z.string().min(12).max(128),
});
export const SetUserPasswordInputSchema = z.object({
userId: z.string(),
password: z.string().min(12, "Password must be at least 12 characters"),
password: z.string().min(12, "Password must be at least 12 characters").max(128),
});
export const UpdateUserRoleInputSchema = z.object({
@@ -289,10 +289,7 @@ export async function linkUserResource(
const linkResult = await ctx.db.resource.updateMany({
where: {
id: input.resourceId,
OR: [
{ userId: null },
{ userId: input.userId },
],
OR: [{ userId: null }, { userId: input.userId }],
},
data: { userId: input.userId },
});
@@ -393,7 +390,10 @@ export async function setUserPermissions(
entityId: input.userId,
entityName: `${before.name} (${before.email})`,
action: "UPDATE",
before: { permissionOverrides: before.permissionOverrides } as unknown as Record<string, unknown>,
before: { permissionOverrides: before.permissionOverrides } as unknown as Record<
string,
unknown
>,
after: { permissionOverrides: input.overrides } as unknown as Record<string, unknown>,
summary: input.overrides
? `Set permission overrides (granted: ${input.overrides.granted?.length ?? 0}, denied: ${input.overrides.denied?.length ?? 0})`
@@ -427,7 +427,10 @@ export async function resetUserPermissions(
entityId: input.userId,
entityName: `${before.name} (${before.email})`,
action: "UPDATE",
before: { permissionOverrides: before.permissionOverrides } as unknown as Record<string, unknown>,
before: { permissionOverrides: before.permissionOverrides } as unknown as Record<
string,
unknown
>,
after: { permissionOverrides: null } as unknown as Record<string, unknown>,
summary: "Reset permission overrides to role defaults",
});
@@ -464,7 +467,10 @@ export async function deactivateUser(
) {
const audit = makeAuditLogger(ctx.db, ctx.dbUser?.id);
if (ctx.dbUser!.id === input.userId) {
throw new TRPCError({ code: "BAD_REQUEST", message: "You cannot deactivate your own account." });
throw new TRPCError({
code: "BAD_REQUEST",
message: "You cannot deactivate your own account.",
});
}
const user = await findUniqueOrThrow(
@@ -479,7 +485,10 @@ export async function deactivateUser(
throw new TRPCError({ code: "BAD_REQUEST", message: "User is already inactive." });
}
await ctx.db.user.update({ where: { id: input.userId }, data: { isActive: false, deletedAt: new Date() } });
await ctx.db.user.update({
where: { id: input.userId },
data: { isActive: false, deletedAt: new Date() },
});
// Invalidate all existing sessions so the user is logged out immediately
await ctx.db.activeSession.deleteMany({ where: { userId: input.userId } });
@@ -512,7 +521,10 @@ export async function reactivateUser(
throw new TRPCError({ code: "BAD_REQUEST", message: "User is already active." });
}
await ctx.db.user.update({ where: { id: input.userId }, data: { isActive: true, deletedAt: null } });
await ctx.db.user.update({
where: { id: input.userId },
data: { isActive: true, deletedAt: null },
});
audit({
entityType: "User",
+16 -277
View File
@@ -8,6 +8,8 @@ overrides:
flatted: ^3.4.2
picomatch: ^4.0.4
lodash-es: ^4.18.0
brace-expansion: ^5.0.5
esbuild@<0.25.0: '>=0.25.0'
importers:
@@ -99,8 +101,8 @@ importers:
specifier: ^2.1.1
version: 2.1.1
dompurify:
specifier: ^3.3.3
version: 3.3.3
specifier: ^3.4.0
version: 3.4.0
exceljs:
specifier: ^4.4.0
version: 4.4.0
@@ -639,204 +641,102 @@ packages:
'@emnapi/wasi-threads@1.1.0':
resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==}
'@esbuild/aix-ppc64@0.21.5':
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [aix]
'@esbuild/aix-ppc64@0.27.3':
resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.21.5':
resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm64@0.27.3':
resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.21.5':
resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
'@esbuild/android-arm@0.27.3':
resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.21.5':
resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
'@esbuild/android-x64@0.27.3':
resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.21.5':
resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-arm64@0.27.3':
resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.21.5':
resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
'@esbuild/darwin-x64@0.27.3':
resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.21.5':
resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-arm64@0.27.3':
resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.21.5':
resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
'@esbuild/freebsd-x64@0.27.3':
resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.21.5':
resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm64@0.27.3':
resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.21.5':
resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
'@esbuild/linux-arm@0.27.3':
resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.21.5':
resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-ia32@0.27.3':
resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.21.5':
resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-loong64@0.27.3':
resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.21.5':
resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-mips64el@0.27.3':
resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.21.5':
resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-ppc64@0.27.3':
resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.21.5':
resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-riscv64@0.27.3':
resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.21.5':
resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-s390x@0.27.3':
resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.21.5':
resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
'@esbuild/linux-x64@0.27.3':
resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==}
engines: {node: '>=18'}
@@ -849,12 +749,6 @@ packages:
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.21.5':
resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
'@esbuild/netbsd-x64@0.27.3':
resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==}
engines: {node: '>=18'}
@@ -867,12 +761,6 @@ packages:
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.21.5':
resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
'@esbuild/openbsd-x64@0.27.3':
resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==}
engines: {node: '>=18'}
@@ -885,48 +773,24 @@ packages:
cpu: [arm64]
os: [openharmony]
'@esbuild/sunos-x64@0.21.5':
resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
'@esbuild/sunos-x64@0.27.3':
resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.21.5':
resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-arm64@0.27.3':
resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.21.5':
resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-ia32@0.27.3':
resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.21.5':
resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
'@esbuild/win32-x64@0.27.3':
resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==}
engines: {node: '>=18'}
@@ -2693,9 +2557,6 @@ packages:
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
engines: {node: '>= 0.4'}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
balanced-match@4.0.4:
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
engines: {node: 18 || 20 || >=22}
@@ -2732,14 +2593,8 @@ packages:
bluebird@3.4.7:
resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==}
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
brace-expansion@2.0.2:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
brace-expansion@5.0.4:
resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==}
brace-expansion@5.0.5:
resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==}
engines: {node: 18 || 20 || >=22}
braces@3.0.3:
@@ -2892,9 +2747,6 @@ packages:
resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==}
engines: {node: '>= 10'}
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
@@ -3107,8 +2959,8 @@ packages:
dom-accessibility-api@0.6.3:
resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
dompurify@3.3.3:
resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==}
dompurify@3.4.0:
resolution: {integrity: sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==}
dotenv@16.6.1:
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
@@ -3194,11 +3046,6 @@ packages:
es-toolkit@1.45.0:
resolution: {integrity: sha512-RArCX+Zea16+R1jg4mH223Z8p/ivbJjIkU3oC6ld2bdUfmDxiCkFYSi9zLOR2anucWJUeH4Djnzgd0im0nD3dw==}
esbuild@0.21.5:
resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
engines: {node: '>=12'}
hasBin: true
esbuild@0.27.3:
resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==}
engines: {node: '>=18'}
@@ -5726,150 +5573,81 @@ snapshots:
tslib: 2.8.1
optional: true
'@esbuild/aix-ppc64@0.21.5':
optional: true
'@esbuild/aix-ppc64@0.27.3':
optional: true
'@esbuild/android-arm64@0.21.5':
optional: true
'@esbuild/android-arm64@0.27.3':
optional: true
'@esbuild/android-arm@0.21.5':
optional: true
'@esbuild/android-arm@0.27.3':
optional: true
'@esbuild/android-x64@0.21.5':
optional: true
'@esbuild/android-x64@0.27.3':
optional: true
'@esbuild/darwin-arm64@0.21.5':
optional: true
'@esbuild/darwin-arm64@0.27.3':
optional: true
'@esbuild/darwin-x64@0.21.5':
optional: true
'@esbuild/darwin-x64@0.27.3':
optional: true
'@esbuild/freebsd-arm64@0.21.5':
optional: true
'@esbuild/freebsd-arm64@0.27.3':
optional: true
'@esbuild/freebsd-x64@0.21.5':
optional: true
'@esbuild/freebsd-x64@0.27.3':
optional: true
'@esbuild/linux-arm64@0.21.5':
optional: true
'@esbuild/linux-arm64@0.27.3':
optional: true
'@esbuild/linux-arm@0.21.5':
optional: true
'@esbuild/linux-arm@0.27.3':
optional: true
'@esbuild/linux-ia32@0.21.5':
optional: true
'@esbuild/linux-ia32@0.27.3':
optional: true
'@esbuild/linux-loong64@0.21.5':
optional: true
'@esbuild/linux-loong64@0.27.3':
optional: true
'@esbuild/linux-mips64el@0.21.5':
optional: true
'@esbuild/linux-mips64el@0.27.3':
optional: true
'@esbuild/linux-ppc64@0.21.5':
optional: true
'@esbuild/linux-ppc64@0.27.3':
optional: true
'@esbuild/linux-riscv64@0.21.5':
optional: true
'@esbuild/linux-riscv64@0.27.3':
optional: true
'@esbuild/linux-s390x@0.21.5':
optional: true
'@esbuild/linux-s390x@0.27.3':
optional: true
'@esbuild/linux-x64@0.21.5':
optional: true
'@esbuild/linux-x64@0.27.3':
optional: true
'@esbuild/netbsd-arm64@0.27.3':
optional: true
'@esbuild/netbsd-x64@0.21.5':
optional: true
'@esbuild/netbsd-x64@0.27.3':
optional: true
'@esbuild/openbsd-arm64@0.27.3':
optional: true
'@esbuild/openbsd-x64@0.21.5':
optional: true
'@esbuild/openbsd-x64@0.27.3':
optional: true
'@esbuild/openharmony-arm64@0.27.3':
optional: true
'@esbuild/sunos-x64@0.21.5':
optional: true
'@esbuild/sunos-x64@0.27.3':
optional: true
'@esbuild/win32-arm64@0.21.5':
optional: true
'@esbuild/win32-arm64@0.27.3':
optional: true
'@esbuild/win32-ia32@0.21.5':
optional: true
'@esbuild/win32-ia32@0.27.3':
optional: true
'@esbuild/win32-x64@0.21.5':
optional: true
'@esbuild/win32-x64@0.27.3':
optional: true
@@ -7208,7 +6986,7 @@ snapshots:
'@types/dompurify@3.2.0':
dependencies:
dompurify: 3.3.3
dompurify: 3.4.0
'@types/eslint-scope@3.7.7':
dependencies:
@@ -7722,8 +7500,6 @@ snapshots:
axobject-query@4.1.0: {}
balanced-match@1.0.2: {}
balanced-match@4.0.4: {}
base64-js@0.0.8: {}
@@ -7753,16 +7529,7 @@ snapshots:
bluebird@3.4.7: {}
brace-expansion@1.1.12:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
brace-expansion@2.0.2:
dependencies:
balanced-match: 1.0.2
brace-expansion@5.0.4:
brace-expansion@5.0.5:
dependencies:
balanced-match: 4.0.4
@@ -7914,8 +7681,6 @@ snapshots:
normalize-path: 3.0.0
readable-stream: 3.6.2
concat-map@0.0.1: {}
convert-source-map@2.0.0: {}
core-util-is@1.0.3: {}
@@ -8095,7 +7860,7 @@ snapshots:
dom-accessibility-api@0.6.3: {}
dompurify@3.3.3:
dompurify@3.4.0:
optionalDependencies:
'@types/trusted-types': 2.0.7
@@ -8226,32 +7991,6 @@ snapshots:
es-toolkit@1.45.0: {}
esbuild@0.21.5:
optionalDependencies:
'@esbuild/aix-ppc64': 0.21.5
'@esbuild/android-arm': 0.21.5
'@esbuild/android-arm64': 0.21.5
'@esbuild/android-x64': 0.21.5
'@esbuild/darwin-arm64': 0.21.5
'@esbuild/darwin-x64': 0.21.5
'@esbuild/freebsd-arm64': 0.21.5
'@esbuild/freebsd-x64': 0.21.5
'@esbuild/linux-arm': 0.21.5
'@esbuild/linux-arm64': 0.21.5
'@esbuild/linux-ia32': 0.21.5
'@esbuild/linux-loong64': 0.21.5
'@esbuild/linux-mips64el': 0.21.5
'@esbuild/linux-ppc64': 0.21.5
'@esbuild/linux-riscv64': 0.21.5
'@esbuild/linux-s390x': 0.21.5
'@esbuild/linux-x64': 0.21.5
'@esbuild/netbsd-x64': 0.21.5
'@esbuild/openbsd-x64': 0.21.5
'@esbuild/sunos-x64': 0.21.5
'@esbuild/win32-arm64': 0.21.5
'@esbuild/win32-ia32': 0.21.5
'@esbuild/win32-x64': 0.21.5
esbuild@0.27.3:
optionalDependencies:
'@esbuild/aix-ppc64': 0.27.3
@@ -9290,19 +9029,19 @@ snapshots:
minimatch@10.2.4:
dependencies:
brace-expansion: 5.0.4
brace-expansion: 5.0.5
minimatch@3.1.5:
dependencies:
brace-expansion: 1.1.12
brace-expansion: 5.0.5
minimatch@5.1.9:
dependencies:
brace-expansion: 2.0.2
brace-expansion: 5.0.5
minimatch@9.0.9:
dependencies:
brace-expansion: 2.0.2
brace-expansion: 5.0.5
minimist@1.2.8: {}
@@ -10606,7 +10345,7 @@ snapshots:
vite@5.4.21(@types/node@22.19.13)(terser@5.46.1):
dependencies:
esbuild: 0.21.5
esbuild: 0.27.3
postcss: 8.5.8
rollup: 4.59.0
optionalDependencies: