From 534945f6e3625acea709cb2e04800aa09fb35739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Fri, 17 Apr 2026 08:13:25 +0200 Subject: [PATCH] security: bound password inputs, configure pino redact, patch deps (#36 #46 #58) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #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 --- apps/web/package.json | 2 +- apps/web/src/server/auth.ts | 22 +- package.json | 4 +- packages/api/src/lib/logger.ts | 38 +++ packages/api/src/router/auth.ts | 2 +- packages/api/src/router/invite.ts | 19 +- .../api/src/router/user-procedure-support.ts | 34 +- pnpm-lock.yaml | 293 +----------------- 8 files changed, 110 insertions(+), 304 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index c263aa0..b98fa23 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -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", diff --git a/apps/web/src/server/auth.ts b/apps/web/src/server/auth.ts index 7a0ebd7..5f84124 100644 --- a/apps/web/src/server/auth.ts +++ b/apps/web/src/server/auth.ts @@ -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({ diff --git a/package.json b/package.json index e47c08d..36b550f 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/packages/api/src/lib/logger.ts b/packages/api/src/lib/logger.ts index aaa084a..2bd360f 100644 --- a/packages/api/src/lib/logger.ts +++ b/packages/api/src/lib/logger.ts @@ -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 }; diff --git a/packages/api/src/router/auth.ts b/packages/api/src/router/auth.ts index 32104f4..caf7376 100644 --- a/packages/api/src/router/auth.ts +++ b/packages/api/src/router/auth.ts @@ -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 }) => { diff --git a/packages/api/src/router/invite.ts b/packages/api/src/router/invite.ts index 55c4b5a..cb79559 100644 --- a/packages/api/src/router/invite.ts +++ b/packages/api/src/router/invite.ts @@ -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"); diff --git a/packages/api/src/router/user-procedure-support.ts b/packages/api/src/router/user-procedure-support.ts index f0a5954..e0521aa 100644 --- a/packages/api/src/router/user-procedure-support.ts +++ b/packages/api/src/router/user-procedure-support.ts @@ -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, + before: { permissionOverrides: before.permissionOverrides } as unknown as Record< + string, + unknown + >, after: { permissionOverrides: input.overrides } as unknown as Record, 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, + before: { permissionOverrides: before.permissionOverrides } as unknown as Record< + string, + unknown + >, after: { permissionOverrides: null } as unknown as Record, 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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5dc3b7..19ea7b0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: