diff --git a/packages/server/package.json b/packages/server/package.json index c67877fd2..07ccb69d2 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -86,6 +86,7 @@ "fp-ts": "^2.16.9", "ioredis": "^5.6.0", "is-my-json-valid": "^2.20.5", + "jose": "^5.9.6", "js-money": "^0.6.3", "knex": "^3.1.0", "lamda": "^0.4.1", diff --git a/packages/server/src/modules/BankingPlaid/BankingPlaid.module.ts b/packages/server/src/modules/BankingPlaid/BankingPlaid.module.ts index 2f0a7be5a..862f0c28e 100644 --- a/packages/server/src/modules/BankingPlaid/BankingPlaid.module.ts +++ b/packages/server/src/modules/BankingPlaid/BankingPlaid.module.ts @@ -24,6 +24,7 @@ import { BankingPlaidWebhooksController } from './BankingPlaidWebhooks.controlle import { SetupPlaidItemTenantService } from './command/SetupPlaidItemTenant.service'; import { UpdateBankingPlaidTransitionsQueueJob } from './types/BankingPlaid.types'; import { PlaidFetchTransactionsProcessor } from './jobs/PlaidFetchTransactionsJob'; +import { PlaidWebhookVerificationService } from './PlaidWebhookVerification.service'; const models = [RegisterTenancyModel(PlaidItem)]; @@ -50,6 +51,7 @@ const models = [RegisterTenancyModel(PlaidItem)]; PlaidLinkTokenService, PlaidApplication, SetupPlaidItemTenantService, + PlaidWebhookVerificationService, TenancyContext, PlaidFetchTransactionsProcessor, PlaidUpdateTransactionsOnItemCreatedSubscriber, diff --git a/packages/server/src/modules/BankingPlaid/BankingPlaidWebhooks.controller.ts b/packages/server/src/modules/BankingPlaid/BankingPlaidWebhooks.controller.ts index 42a79f769..b479c9a9a 100644 --- a/packages/server/src/modules/BankingPlaid/BankingPlaidWebhooks.controller.ts +++ b/packages/server/src/modules/BankingPlaid/BankingPlaidWebhooks.controller.ts @@ -1,31 +1,50 @@ -import { Body, Controller, Post } from '@nestjs/common'; -import { PlaidWebhookDto } from './dtos/PlaidItem.dto'; +import { + BadRequestException, + Body, + Controller, + Headers, + HttpCode, + Logger, + Post, + RawBodyRequest, + Req, +} from '@nestjs/common'; +import { Request } from 'express'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { PlaidWebhookDto } from './dtos/PlaidItem.dto'; import { PlaidApplication } from './PlaidApplication'; import { PublicRoute } from '../Auth/guards/jwt.guard'; import { SetupPlaidItemTenantService } from './command/SetupPlaidItemTenant.service'; +import { PlaidWebhookVerificationService } from './PlaidWebhookVerification.service'; @Controller('banking/plaid') @ApiTags('banking-plaid') @PublicRoute() export class BankingPlaidWebhooksController { + private readonly logger = new Logger(BankingPlaidWebhooksController.name); + constructor( private readonly plaidApplication: PlaidApplication, private readonly setupPlaidItemTenantService: SetupPlaidItemTenantService, + private readonly verificationService: PlaidWebhookVerificationService, ) {} @Post('webhooks') + @HttpCode(200) @ApiOperation({ summary: 'Listen to Plaid webhooks' }) - webhooks(@Body() { itemId, webhookType, webhookCode }: PlaidWebhookDto) { - return this.setupPlaidItemTenantService.setupPlaidTenant( - itemId, - () => { - return this.plaidApplication.webhooks( - itemId, - webhookType, - webhookCode, - ); - }, + async webhooks( + @Req() req: RawBodyRequest, + @Headers('plaid-verification') verification: string, + @Body() { itemId, webhookType, webhookCode }: PlaidWebhookDto, + ) { + try { + await this.verificationService.verifyWebhook(req.rawBody, verification); + } catch (err) { + this.logger.warn(`Plaid webhook verification failed: ${err.message}`); + throw new BadRequestException('Invalid Plaid webhook signature'); + } + return this.setupPlaidItemTenantService.setupPlaidTenant(itemId, () => + this.plaidApplication.webhooks(itemId, webhookType, webhookCode), ); } } diff --git a/packages/server/src/modules/BankingPlaid/PlaidWebhookVerification.service.ts b/packages/server/src/modules/BankingPlaid/PlaidWebhookVerification.service.ts new file mode 100644 index 000000000..98acb134d --- /dev/null +++ b/packages/server/src/modules/BankingPlaid/PlaidWebhookVerification.service.ts @@ -0,0 +1,101 @@ +import { createHash, timingSafeEqual } from 'crypto'; +import { Inject, Injectable } from '@nestjs/common'; +import { + decodeProtectedHeader, + importJWK, + jwtVerify, + type JWK, + type KeyLike, +} from 'jose'; +import * as LRUCache from 'lru-cache'; +import type { PlaidApi } from 'plaid'; +import { PLAID_CLIENT } from '../Plaid/Plaid.module'; + +const ALLOWED_ALG = 'ES256'; +const MAX_TOKEN_AGE = '5m'; +const KEY_CACHE_MAX = 100; +const KEY_CACHE_TTL_MS = 24 * 60 * 60 * 1000; + +type ImportedKey = KeyLike | Uint8Array; + +@Injectable() +export class PlaidWebhookVerificationService { + private readonly keyCache: LRUCache = new LRUCache({ + max: KEY_CACHE_MAX, + maxAge: KEY_CACHE_TTL_MS, + }); + + constructor(@Inject(PLAID_CLIENT) private readonly plaidClient: PlaidApi) {} + + public async verifyWebhook( + rawBody: Buffer | undefined, + verificationHeader: string | undefined, + ): Promise { + if (!rawBody || rawBody.length === 0) { + throw new Error('Plaid webhook raw body missing'); + } + if (!verificationHeader) { + throw new Error('Plaid-Verification header missing'); + } + + const header = decodeProtectedHeader(verificationHeader); + if (header.alg !== ALLOWED_ALG) { + throw new Error(`Unexpected webhook JWT alg: ${header.alg}`); + } + if (!header.kid) { + throw new Error('Webhook JWT missing kid header'); + } + + const key = await this.getVerificationKey(header.kid); + + const { payload } = await jwtVerify(verificationHeader, key, { + algorithms: [ALLOWED_ALG], + maxTokenAge: MAX_TOKEN_AGE, + }); + + const expectedHash = payload['request_body_sha256']; + if (typeof expectedHash !== 'string') { + throw new Error('Webhook JWT missing request_body_sha256 claim'); + } + const actualHash = createHash('sha256').update(rawBody).digest('hex'); + if (!this.constantTimeEquals(actualHash, expectedHash)) { + throw new Error('Webhook body hash mismatch'); + } + } + + private async getVerificationKey(kid: string): Promise { + const cached = this.keyCache.get(kid); + if (cached) return cached; + + const response = await this.plaidClient.webhookVerificationKeyGet({ + key_id: kid, + }); + const plaidKey = response.data.key; + + if ( + plaidKey.expired_at !== null && + plaidKey.expired_at !== undefined && + plaidKey.expired_at * 1000 < Date.now() + ) { + throw new Error(`Plaid verification key ${kid} is expired`); + } + + const jwk: JWK = { + kty: plaidKey.kty, + crv: plaidKey.crv, + x: plaidKey.x, + y: plaidKey.y, + alg: plaidKey.alg, + use: plaidKey.use, + kid: plaidKey.kid, + }; + const imported = await importJWK(jwk, ALLOWED_ALG); + this.keyCache.set(kid, imported); + return imported; + } + + private constantTimeEquals(a: string, b: string): boolean { + if (a.length !== b.length) return false; + return timingSafeEqual(Buffer.from(a, 'utf8'), Buffer.from(b, 'utf8')); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3ac42bdc8..3648c0451 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -201,6 +201,9 @@ importers: is-my-json-valid: specifier: ^2.20.5 version: 2.20.6 + jose: + specifier: ^5.9.6 + version: 5.10.0 js-money: specifier: ^0.6.3 version: 0.6.3 @@ -9337,6 +9340,9 @@ packages: jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + jose@5.10.0: + resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -14052,11 +14058,11 @@ snapshots: '@babel/helpers': 7.28.4 '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -14152,7 +14158,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.26.0) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -14165,7 +14171,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -14189,7 +14195,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -14200,7 +14206,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) lodash.debounce: 4.0.8 resolve: 1.22.11 transitivePeerDependencies: @@ -14211,7 +14217,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) lodash.debounce: 4.0.8 resolve: 1.22.11 transitivePeerDependencies: @@ -14236,7 +14242,7 @@ snapshots: '@babel/helper-member-expression-to-functions@7.28.5': dependencies: - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color @@ -14245,13 +14251,6 @@ snapshots: dependencies: '@babel/types': 7.24.5 - '@babel/helper-module-imports@7.25.9': - dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-imports@7.25.9(supports-color@5.5.0)': dependencies: '@babel/traverse': 7.28.5(supports-color@5.5.0) @@ -14261,7 +14260,7 @@ snapshots: '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color @@ -14269,9 +14268,9 @@ snapshots: '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-module-imports': 7.25.9 + '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14280,7 +14279,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14289,7 +14288,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14308,7 +14307,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.28.3 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14317,7 +14316,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.28.3 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14340,7 +14339,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14349,7 +14348,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14359,7 +14358,7 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color @@ -14381,7 +14380,7 @@ snapshots: '@babel/helper-wrap-function@7.28.3': dependencies: '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color @@ -14412,7 +14411,7 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14420,7 +14419,7 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14466,7 +14465,7 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14474,7 +14473,7 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14718,7 +14717,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.26.0) - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14727,7 +14726,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14809,7 +14808,7 @@ snapshots: '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.26.0) - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14821,7 +14820,7 @@ snapshots: '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14841,7 +14840,7 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14849,7 +14848,7 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14966,7 +14965,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -14975,7 +14974,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -15057,7 +15056,7 @@ snapshots: '@babel/helper-module-transforms': 7.28.3(@babel/core@7.26.0) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -15067,7 +15066,7 @@ snapshots: '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -15136,7 +15135,7 @@ snapshots: '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.26.0) '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.26.0) - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -15147,7 +15146,7 @@ snapshots: '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -15357,7 +15356,7 @@ snapshots: '@babel/plugin-transform-runtime@7.24.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-module-imports': 7.25.9 + '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) '@babel/helper-plugin-utils': 7.27.1 babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.26.0) babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.26.0) @@ -15748,23 +15747,11 @@ snapshots: '@babel/parser': 7.28.5 '@babel/template': 7.25.9 '@babel/types': 7.28.5 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/traverse@7.28.5': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 - debug: 4.4.3(supports-color@10.2.2) - transitivePeerDependencies: - - supports-color - '@babel/traverse@7.28.5(supports-color@5.5.0)': dependencies: '@babel/code-frame': 7.27.1 @@ -16126,7 +16113,7 @@ snapshots: '@emotion/babel-plugin@11.12.0': dependencies: - '@babel/helper-module-imports': 7.25.9 + '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) '@babel/runtime': 7.24.5 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 @@ -16509,7 +16496,7 @@ snapshots: '@eslint/config-array@0.18.0': dependencies: '@eslint/object-schema': 2.1.4 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -16533,7 +16520,7 @@ snapshots: '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) espree: 10.2.0 globals: 14.0.0 ignore: 5.3.1 @@ -19348,7 +19335,7 @@ snapshots: dependencies: '@babel/generator': 7.28.5 '@babel/parser': 7.28.5 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) '@babel/types': 7.28.5 '@storybook/csf': 0.1.11 '@storybook/types': 7.2.2 @@ -20508,7 +20495,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@4.9.5) - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) eslint: 8.57.0 tsutils: 3.21.0(typescript@4.9.5) optionalDependencies: @@ -20520,7 +20507,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3) '@typescript-eslint/utils': 8.11.0(eslint@9.13.0(jiti@1.21.0))(typescript@5.6.3) - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) ts-api-utils: 1.3.0(typescript@5.6.3) optionalDependencies: typescript: 5.6.3 @@ -20550,7 +20537,7 @@ snapshots: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.3 @@ -20564,7 +20551,7 @@ snapshots: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.3 @@ -20578,7 +20565,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.11.0 '@typescript-eslint/visitor-keys': 8.11.0 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) fast-glob: 3.3.2 is-glob: 4.0.3 minimatch: 9.0.4 @@ -20999,13 +20986,13 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color agent-base@7.1.1: dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -21330,7 +21317,7 @@ snapshots: babel-plugin-emotion@10.2.2: dependencies: - '@babel/helper-module-imports': 7.25.9 + '@babel/helper-module-imports': 7.25.9(supports-color@5.5.0) '@emotion/hash': 0.8.0 '@emotion/memoize': 0.7.4 '@emotion/serialize': 0.11.16 @@ -22592,7 +22579,7 @@ snapshots: detect-port@1.6.1: dependencies: address: 1.2.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -22950,7 +22937,7 @@ snapshots: esbuild-register@3.6.0(esbuild@0.18.20): dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) esbuild: 0.18.20 transitivePeerDependencies: - supports-color @@ -23381,7 +23368,7 @@ snapshots: estree-to-babel@3.2.1: dependencies: - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@5.5.0) '@babel/types': 7.28.5 c8: 7.14.0 transitivePeerDependencies: @@ -24195,35 +24182,35 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color https-proxy-agent@4.0.0: dependencies: agent-base: 5.1.1 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color https-proxy-agent@7.0.4: dependencies: agent-base: 7.1.1 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -24410,7 +24397,7 @@ snapshots: dependencies: '@ioredis/commands': 1.2.0 cluster-key-slot: 1.1.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) denque: 1.5.1 lodash.defaults: 4.2.0 lodash.flatten: 4.4.0 @@ -24675,7 +24662,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -25066,6 +25053,8 @@ snapshots: jju@1.4.0: {} + jose@5.10.0: {} + joycon@3.1.1: {} js-beautify@1.15.1: @@ -27159,7 +27148,7 @@ snapshots: puppeteer-core@2.1.1: dependencies: '@types/mime-types': 2.1.4 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) extract-zip: 1.7.0 https-proxy-agent: 4.0.0 mime: 2.6.0 @@ -28311,7 +28300,7 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -28319,7 +28308,7 @@ snapshots: socks-proxy-agent@8.0.3: dependencies: agent-base: 7.1.1 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -28637,7 +28626,7 @@ snapshots: dependencies: component-emitter: 1.3.1 cookiejar: 2.1.4 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) fast-safe-stringify: 2.1.1 form-data: 4.0.0 formidable: 3.5.2 @@ -29103,7 +29092,7 @@ snapshots: tuf-js@1.1.7: dependencies: '@tufjs/models': 1.0.4 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color @@ -29111,7 +29100,7 @@ snapshots: tuf-js@2.2.1: dependencies: '@tufjs/models': 2.0.1 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) make-fetch-happen: 13.0.1 transitivePeerDependencies: - supports-color @@ -29401,7 +29390,7 @@ snapshots: vite-node@2.1.3(@types/node@20.19.25)(less@4.2.0)(sass@1.77.2)(terser@5.31.0): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@5.5.0) pathe: 1.1.2 vite: 5.4.10(@types/node@20.19.25)(less@4.2.0)(sass@1.77.2)(terser@5.31.0) transitivePeerDependencies: