From 3cae48329e22fc32f06e4fe3ccd7718af9e19ad3 Mon Sep 17 00:00:00 2001 From: Shun Sakai Date: Wed, 23 Oct 2024 20:47:45 +0900 Subject: [PATCH] enhance(backend): Replace bcrypt with Argon2 Squashed commit of the following: commit 4862fd8467d529c54d0aa3e6abff15a574459a8b Author: Shun Sakai Date: Wed Oct 23 20:44:30 2024 +0900 chore(backend): Update `argon2` package commit a52eff5deaee39c0a70c83da49ca58d0eab8d513 Author: NoriDev Date: Mon Oct 7 18:11:34 2024 +0900 Revert "tweak 0dc322b6 (1673beta/cherrypick#88)" This reverts commit ab6a5d0c3dbe7146de19d72d08658b1c011fe30a. commit be51daec8a916a2668ea5794e067bde06499e1a4 Author: Mar0xy Date: Wed Sep 27 21:46:56 2023 +0200 upd: rehash misskey passwords with argon2 on login commit 67b124b7e6e8f1b1d1738ea9a123ab0500876d58 Author: Mar0xy Date: Fri Sep 22 00:21:57 2023 +0200 upd: swap bcrypt to argon2 --- packages/backend/package.json | 1 + .../src/core/CreateSystemUserService.ts | 5 +- packages/backend/src/core/SignupService.ts | 5 +- .../src/server/api/SigninApiService.ts | 19 +++- .../src/server/api/SignupApiService.ts | 5 +- .../api/endpoints/admin/reset-password.ts | 4 +- .../server/api/endpoints/i/2fa/key-done.ts | 4 +- .../api/endpoints/i/2fa/register-key.ts | 4 +- .../server/api/endpoints/i/2fa/register.ts | 4 +- .../server/api/endpoints/i/2fa/remove-key.ts | 4 +- .../server/api/endpoints/i/2fa/unregister.ts | 4 +- .../server/api/endpoints/i/2fa/update-key.ts | 1 - .../server/api/endpoints/i/change-password.ts | 7 +- .../server/api/endpoints/i/delete-account.ts | 4 +- .../api/endpoints/i/regenerate-token.ts | 4 +- .../server/api/endpoints/i/update-email.ts | 4 +- .../server/api/endpoints/reset-password.ts | 5 +- packages/backend/test/e2e/timelines.ts | 10 +- pnpm-lock.yaml | 104 +++++++++--------- 19 files changed, 106 insertions(+), 92 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index f56a737eea..322745a427 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -99,6 +99,7 @@ "accepts": "1.3.8", "ajv": "8.17.1", "archiver": "7.0.1", + "argon2": "^0.41.1", "async-mutex": "0.5.0", "bcryptjs": "2.4.3", "blurhash": "2.0.5", diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts index 6c5b0f6a36..8036fc11c7 100644 --- a/packages/backend/src/core/CreateSystemUserService.ts +++ b/packages/backend/src/core/CreateSystemUserService.ts @@ -5,7 +5,7 @@ import { randomUUID } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { IsNull, DataSource } from 'typeorm'; import { genRsaKeyPair } from '@/misc/gen-key-pair.js'; import { MiUser } from '@/models/User.js'; @@ -32,8 +32,7 @@ export class CreateSystemUserService { const password = randomUUID(); // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(password, salt); + const hash = await argon2.hash(password); // Generate secret const secret = generateNativeUserToken(); diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index 3865392b7f..171b1877ab 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -5,7 +5,7 @@ import { generateKeyPair } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { DataSource, IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { MiMeta, UsedUsernamesRepository, UsersRepository } from '@/models/_.js'; @@ -69,8 +69,7 @@ export class SignupService { } // Generate hash of password - const salt = await bcrypt.genSalt(8); - hash = await bcrypt.hash(password, salt); + hash = await argon2.hash(password); } // Generate secret diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 1d983ca4bc..d8655ca420 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -5,6 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { IsNull } from 'typeorm'; import * as Misskey from 'misskey-js'; import { DI } from '@/di-symbols.js'; @@ -155,7 +156,7 @@ export class SigninApiService { } // Compare password - const same = await bcrypt.compare(password, profile.password!); + const same = await argon2.verify(profile.password!, password) || bcrypt.compareSync(password, profile.password!); const fail = async (status?: number, failure?: { id: string; }) => { // Append signin history @@ -204,6 +205,14 @@ export class SigninApiService { } if (same) { + // Check if the password is still hashed using bcrypt + if (profile.password!.startsWith('$2')) { + // Rehash the password using Argon2 + const newHash = await argon2.hash(password); + this.userProfilesRepository.update(user.id, { + password: newHash, + }); + } return this.signinService.signin(request, reply, user); } else { return await fail(403, { @@ -220,6 +229,14 @@ export class SigninApiService { } try { + // Check if the password is still hashed using bcrypt + if (profile.password!.startsWith('$2')) { + // Rehash the password using Argon2 + const newHash = await argon2.hash(password); + this.userProfilesRepository.update(user.id, { + password: newHash, + }); + } await this.userAuthService.twoFactorAuthenticate(profile, token); } catch (e) { return await fail(403, { diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 3ec5e5d3e6..0939f64aa8 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -4,7 +4,7 @@ */ import { Inject, Injectable } from '@nestjs/common'; -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { RegistrationTicketsRepository, UsedUsernamesRepository, UserPendingsRepository, UserProfilesRepository, UsersRepository, MiRegistrationTicket, MiMeta } from '@/models/_.js'; @@ -186,8 +186,7 @@ export class SignupApiService { const code = secureRndstr(16, { chars: L_CHARS }); // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(password, salt); + const hash = await argon2.hash(password); const pendingUser = await this.userPendingsRepository.insertOne({ id: this.idService.gen(), diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts index 53db096c1d..831ea09ed6 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -4,7 +4,7 @@ */ import { Inject, Injectable } from '@nestjs/common'; -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UsersRepository, UserProfilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; @@ -65,7 +65,7 @@ export default class extends Endpoint { // eslint- const passwd = secureRndstr(8); // Generate hash of password - const hash = bcrypt.hashSync(passwd); + const hash = await argon2.hash(passwd); await this.userProfilesRepository.update({ userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 65eece5b97..e74a98a007 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -86,7 +86,7 @@ export default class extends Endpoint { } } - const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + const passwordMatched = await argon2.verify(profile.password ?? '', ps.password); if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index 9391aee5e0..ec6bdb9989 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UserProfilesRepository } from '@/models/_.js'; @@ -217,7 +217,7 @@ export default class extends Endpoint { } } - const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + const passwordMatched = await argon2.verify(profile.password ?? '', ps.password); if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index a54c598213..3fad552c11 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import * as OTPAuth from 'otpauth'; import * as QRCode from 'qrcode'; import { Inject, Injectable } from '@nestjs/common'; @@ -77,7 +77,7 @@ export default class extends Endpoint { // eslint- } } - const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + const passwordMatched = await argon2.verify(profile.password ?? '', ps.password); if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index c350136eae..571b1d1547 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/_.js'; @@ -66,7 +66,7 @@ export default class extends Endpoint { // eslint- } } - const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + const passwordMatched = await argon2.verify(profile.password ?? '', ps.password); if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts index b5a53cc889..6ade05e4d4 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -62,7 +62,7 @@ export default class extends Endpoint { // eslint- } } - const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + const passwordMatched = await argon2.verify(profile.password ?? '', ps.password); if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts index cfa07cc8d7..e1b9fb700a 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UserSecurityKeysRepository } from '@/models/_.js'; diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts index bb78d47149..577e7d1dfd 100644 --- a/packages/backend/src/server/api/endpoints/i/change-password.ts +++ b/packages/backend/src/server/api/endpoints/i/change-password.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UserProfilesRepository } from '@/models/_.js'; @@ -50,15 +50,14 @@ export default class extends Endpoint { // eslint- } } - const passwordMatched = await bcrypt.compare(ps.currentPassword, profile.password!); + const passwordMatched = await argon2.verify(profile.password!, ps.currentPassword); if (!passwordMatched) { throw new Error('incorrect password'); } // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(ps.newPassword, salt); + const hash = await argon2.hash(ps.newPassword); await this.userProfilesRepository.update(me.id, { password: hash, diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index bfa0b4605d..2279e06400 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import type { UsersRepository, UserProfilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -59,7 +59,7 @@ export default class extends Endpoint { // eslint- return; } - const passwordMatched = await bcrypt.compare(ps.password, profile.password!); + const passwordMatched = await argon2.verify(profile.password!, ps.password); if (!passwordMatched) { throw new Error('incorrect password'); } diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index 78f3cce9ad..d931eead84 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UsersRepository, UserProfilesRepository } from '@/models/_.js'; @@ -43,7 +43,7 @@ export default class extends Endpoint { // eslint- const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + const same = await argon2.verify(profile.password!, ps.password); if (!same) { throw new Error('incorrect password'); diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index da1faee30d..dc07556760 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -5,7 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { MiMeta, UserProfilesRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -96,7 +96,7 @@ export default class extends Endpoint { // eslint- } } - const passwordMatched = await bcrypt.compare(ps.password, profile.password!); + const passwordMatched = await argon2.verify(profile.password!, ps.password); if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index 9693892637..45ceae43f1 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import bcrypt from 'bcryptjs'; +import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import type { UserProfilesRepository, PasswordResetRequestsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -53,8 +53,7 @@ export default class extends Endpoint { // eslint- } // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(ps.password, salt); + const hash = await argon2.hash(ps.password); await this.userProfilesRepository.update(req.userId, { password: hash, diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index d12be2a9ac..062918f685 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -397,7 +397,7 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); assert.strictEqual(res.body.some(note => note.id === carolNote1.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote2.id), false); - }, 1000 * 10); + }, 1000 * 100); test.concurrent('フォローしているユーザーのチャンネル投稿が含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -744,7 +744,7 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); - }, 1000 * 10); + }, 1000 * 100); }); describe('Social TL', () => { @@ -955,7 +955,7 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); - }, 1000 * 10); + }, 1000 * 100); }); describe('User List TL', () => { @@ -1168,7 +1168,7 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); - }, 1000 * 10); + }, 1000 * 100); test.concurrent('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -1327,7 +1327,7 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); - }, 1000 * 10); + }, 1000 * 100); test.concurrent('[withChannelNotes: true] チャンネル投稿が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0ec950181..1c528dbf6f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -142,7 +142,7 @@ importers: version: 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/testing': specifier: 10.4.7 - version: 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7)) + version: 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7)(@nestjs/platform-express@10.4.7) '@peertube/http-signature': specifier: 1.7.0 version: 1.7.0 @@ -179,6 +179,9 @@ importers: archiver: specifier: 7.0.1 version: 7.0.1 + argon2: + specifier: ^0.41.1 + version: 0.41.1 async-mutex: specifier: 0.5.0 version: 0.5.0 @@ -1163,7 +1166,7 @@ importers: version: 7.17.0(eslint@9.14.0)(typescript@5.6.3) '@vitest/coverage-v8': specifier: 1.6.0 - version: 1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0)) + version: 1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0)) '@vue/runtime-core': specifier: 3.5.12 version: 3.5.12 @@ -3297,6 +3300,10 @@ packages: resolution: {integrity: sha512-aGQIwo6/sWtyyqhVK4e1MtxYz4N1X8CNt6SOtCc+Wnczs5S5ONaLHDDR8LYaGn0MgOwvGgXyuZ5sJIfd7iyoUw==} engines: {node: '>=0.10'} + '@phc/format@1.0.0': + resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==} + engines: {node: '>=10'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -5039,6 +5046,10 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + argon2@0.41.1: + resolution: {integrity: sha512-dqCW8kJXke8Ik+McUcMDltrbuAWETPyU6iq+4AhxqKphWi7pChB/Zgd/Tp/o8xRLbg8ksMj46F/vph9wnxpTzQ==} + engines: {node: '>=16.17.0'} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -8482,6 +8493,10 @@ packages: node-addon-api@3.2.1: resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} + node-addon-api@8.2.2: + resolution: {integrity: sha512-9emqXAKhVoNrQ792nLI/wpzPpJ/bj/YXxW0CvAau1+RdGBcCRF1Dmz7719zgVsQNrzHl9Tzn3ImZ4qWFarWL0A==} + engines: {node: ^18 || ^20 || >= 21} + node-bitmap@0.0.1: resolution: {integrity: sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==} engines: {node: '>=v0.6.5'} @@ -8520,6 +8535,10 @@ packages: resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} hasBin: true + node-gyp-build@4.8.3: + resolution: {integrity: sha512-EMS95CMJzdoSKoIiXo8pxKoL8DYxwIZXYlLmgPb8KUv794abpnLK6ynsCAWNliOjREKruYKdzbh76HHYUHX7nw==} + hasBin: true + node-gyp@10.2.0: resolution: {integrity: sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==} engines: {node: ^16.14.0 || >=18.0.0} @@ -11780,7 +11799,7 @@ snapshots: '@babel/traverse': 7.23.5 '@babel/types': 7.24.7 convert-source-map: 2.0.0 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -11800,7 +11819,7 @@ snapshots: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 convert-source-map: 2.0.0 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -12059,7 +12078,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.25.6 '@babel/types': 7.24.7 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -12074,7 +12093,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.25.6 '@babel/types': 7.25.6 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -12465,7 +12484,7 @@ snapshots: '@eslint/config-array@0.18.0': dependencies: '@eslint/object-schema': 2.1.4 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -12475,7 +12494,7 @@ snapshots: '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) espree: 10.3.0 globals: 14.0.0 ignore: 5.3.1 @@ -13180,7 +13199,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/testing@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7))': + '@nestjs/testing@10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.7)(@nestjs/platform-express@10.4.7)': dependencies: '@nestjs/common': 10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.7(@nestjs/common@10.4.7(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.7)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -13588,6 +13607,8 @@ snapshots: jsprim: 1.4.2 sshpk: 1.17.0 + '@phc/format@1.0.0': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -15384,7 +15405,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0))': + '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0))': dependencies: '@ampproject/remapping': 2.2.1 '@bcoe/v8-coverage': 0.2.3 @@ -15399,7 +15420,7 @@ snapshots: std-env: 3.7.0 strip-literal: 2.1.0 test-exclude: 6.0.0 - vitest: 1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0) + vitest: 1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0) transitivePeerDependencies: - supports-color @@ -15637,7 +15658,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color optional: true @@ -15775,6 +15796,12 @@ snapshots: arg@5.0.2: {} + argon2@0.41.1: + dependencies: + '@phc/format': 1.0.0 + node-addon-api: 8.2.2 + node-gyp-build: 4.8.3 + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -17248,7 +17275,7 @@ snapshots: esbuild-register@3.5.0(esbuild@0.24.0): dependencies: - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) esbuild: 0.24.0 transitivePeerDependencies: - supports-color @@ -17490,7 +17517,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) escape-string-regexp: 4.0.0 eslint-scope: 8.2.0 eslint-visitor-keys: 4.2.0 @@ -17935,7 +17962,7 @@ snapshots: follow-redirects@1.15.9(debug@4.3.7): optionalDependencies: - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) for-each@0.3.3: dependencies: @@ -18805,7 +18832,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -19215,35 +19242,6 @@ snapshots: jsdoc-type-pratt-parser@4.1.0: {} - jsdom@24.1.1: - dependencies: - cssstyle: 4.0.1 - data-urls: 5.0.0 - decimal.js: 10.4.3 - form-data: 4.0.1 - html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.12 - parse5: 7.2.1 - rrweb-cssom: 0.7.1 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 4.1.4 - w3c-xmlserializer: 5.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 3.1.1 - whatwg-mimetype: 4.0.0 - whatwg-url: 14.0.0 - ws: 8.18.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) - xml-name-validator: 5.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - optional: true - jsdom@24.1.1(bufferutil@4.0.7)(utf-8-validate@6.0.3): dependencies: cssstyle: 4.0.1 @@ -19936,7 +19934,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.0 @@ -20239,6 +20237,8 @@ snapshots: node-addon-api@3.2.1: optional: true + node-addon-api@8.2.2: {} + node-bitmap@0.0.1: {} node-domexception@1.0.0: {} @@ -20267,6 +20267,8 @@ snapshots: node-gyp-build@4.6.0: optional: true + node-gyp-build@4.8.3: {} + node-gyp@10.2.0: dependencies: env-paths: 2.2.1 @@ -21396,7 +21398,7 @@ snapshots: require-in-the-middle@7.3.0: dependencies: - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: @@ -21821,7 +21823,7 @@ snapshots: socks-proxy-agent@8.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) socks: 2.7.1 transitivePeerDependencies: - supports-color @@ -21930,7 +21932,7 @@ snapshots: arg: 5.0.2 bluebird: 3.7.2 check-more-types: 2.24.0 - debug: 4.3.7(supports-color@5.5.0) + debug: 4.3.7(supports-color@8.1.1) execa: 5.1.1 lazy-ass: 1.6.0 ps-tree: 1.2.0 @@ -22777,7 +22779,7 @@ snapshots: - supports-color - terser - vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1)(sass@1.79.4)(terser@5.36.0): + vitest@1.6.0(@types/node@22.9.0)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.79.4)(terser@5.36.0): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -22802,7 +22804,7 @@ snapshots: optionalDependencies: '@types/node': 22.9.0 happy-dom: 10.0.3 - jsdom: 24.1.1 + jsdom: 24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - less - lightningcss