diff --git a/.github/workflows/policy-engine.yml b/.github/workflows/policy-engine.yml index bc625ad85..9b65386f5 100644 --- a/.github/workflows/policy-engine.yml +++ b/.github/workflows/policy-engine.yml @@ -17,6 +17,21 @@ jobs: name: Build and test runs-on: ubuntu-latest + + services: + postgres: + image: postgres:14 + ports: + - '5432:5432' + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: - name: Checkout uses: actions/checkout@master @@ -40,6 +55,8 @@ jobs: shell: bash run: | make policy-engine/copy-default-env + make policy-engine/test/db/setup + make policy-engine/db/generate-types - name: Test types shell: bash diff --git a/apps/armory/.env.test.default b/apps/armory/.env.test.default index 2d141cf03..7a83b73c8 100644 --- a/apps/armory/.env.test.default +++ b/apps/armory/.env.test.default @@ -1,6 +1,11 @@ -# IMPORTANT: The variables defined here will override other variables. -# See `./apps/armory/jest.setup.ts`. - NODE_ENV=test +PORT=3005 + ARMORY_DATABASE_URL="postgresql://postgres:postgres@localhost:5432/armory_test?schema=public" + +REDIS_HOST=localhost +REDIS_PORT=6379 + +PRICE_FEED_PRIVATE_KEY="0xc7a1b8ba040a238e36058fc5693f801d129aca9f10ed30d0133878f1b9147c01" +HISTORICAL_TRANSFER_FEED_PRIVATE_KEY="0xf5c8f17cc09215c5038f6b8d5e557c0d98d341236307fe831efdcdd7faeef134" diff --git a/apps/armory/jest.setup.ts b/apps/armory/jest.setup.ts index 09d6219e1..72a94468a 100644 --- a/apps/armory/jest.setup.ts +++ b/apps/armory/jest.setup.ts @@ -3,26 +3,28 @@ import fs from 'fs' import nock from 'nock' const testEnvFile = `${__dirname}/.env.test` -const envFile = `${__dirname}/.env` // Ensure a test environment variable file exists because of the override config // loading mechanics below. if (!fs.existsSync(testEnvFile)) { - throw new Error('No .env.test file found. Please create one by running "make armory/copy-default-env".') + throw new Error('No .env.test file found. Please create one by running "make policy-engine/copy-default-env".') +} + +// By default, dotenv always loads .env and then you can override with .env.test +// But this is confusing, because then you have to look in multiple files to know which envs are loaded +// So we will clear all envs and then load .env.test +// NOTE: This will also override any CLI-declared envs (e.g. `MY_ENV=test jest`) +for (const prop in process.env) { + if (Object.prototype.hasOwnProperty.call(process.env, prop)) { + delete process.env[prop] + } } -// We don't want to have two dotenv files that are exactly the same, so we -// override the default with .env.test. -// -// If a .env.test file is not found, the DATABASE_URL will fallback to the -// default. Consequently, you'll lose your development database during the -// integration tests teardown. Hence, the check above. -dotenv.config({ path: envFile }) dotenv.config({ path: testEnvFile, override: true }) // Disable outgoing HTTP requests to avoid flaky tests. nock.disableNetConnect() -// Enable local outgoing HTTP request to allow E2E tests with supertestwith -// supertest to work. -nock.enableNetConnect(/127.0.0.1|localhost/) +// Enable outgoing HTTP requests to 127.0.0.1 to allow E2E tests with +// supertestwith supertest to work. +nock.enableNetConnect('127.0.0.1') diff --git a/apps/policy-engine/.env.default b/apps/policy-engine/.env.default index b57d6e3e8..802cc00ae 100644 --- a/apps/policy-engine/.env.default +++ b/apps/policy-engine/.env.default @@ -2,4 +2,12 @@ NODE_ENV=development PORT=3010 -ENGINE_DATABASE_URL="file:./engine-core.sqlite" +POLICY_ENGINE_DATABASE_URL="postgresql://postgres:postgres@localhost:5432/engine?schema=public" + +ENGINE_UID="local-dev-engine-instance-1" + +MASTER_PASSWORD="unsafe-local-dev-master-password" + +KEYRING_TYPE="raw" + +# MASTER_AWS_KMS_ARN="arn:aws:kms:us-east-2:728783560968:key/f6aa3ddb-47c3-4f31-977d-b93205bb23d1" diff --git a/apps/policy-engine/.env.test.default b/apps/policy-engine/.env.test.default index ac0145141..fde033c19 100644 --- a/apps/policy-engine/.env.test.default +++ b/apps/policy-engine/.env.test.default @@ -1,3 +1,13 @@ -# IMPORTANT: The variables defined here will override other variables. -# See `./apps/policy-engine/jest.setup.ts`. NODE_ENV=test + +PORT=3010 + +POLICY_ENGINE_DATABASE_URL="postgresql://postgres:postgres@localhost:5432/engine-test?schema=public" + +ENGINE_UID="local-dev-engine-instance-1" + +MASTER_PASSWORD="unsafe-local-test-master-password" + +KEYRING_TYPE="raw" + +# MASTER_AWS_KMS_ARN="arn:aws:kms:us-east-2:728783560968:key/f6aa3ddb-47c3-4f31-977d-b93205bb23d1" diff --git a/apps/policy-engine/Makefile b/apps/policy-engine/Makefile index 7ebd14877..ed775df42 100644 --- a/apps/policy-engine/Makefile +++ b/apps/policy-engine/Makefile @@ -1,5 +1,6 @@ POLICY_ENGINE_PROJECT_NAME := policy-engine POLICY_ENGINE_PROJECT_DIR := ./apps/policy-engine +POLICY_ENGINE_DATABASE_SCHEMA := ${POLICY_ENGINE_PROJECT_DIR}/src/shared/module/persistence/schema/schema.prisma # === Start === @@ -11,11 +12,19 @@ policy-engine/start/dev: policy-engine/setup: make policy-engine/copy-default-env make policy-engine/rego/build + make policy-engine/db/setup + make policy-engine/test/db/setup policy-engine/copy-default-env: cp ${POLICY_ENGINE_PROJECT_DIR}/.env.default ${POLICY_ENGINE_PROJECT_DIR}/.env cp ${POLICY_ENGINE_PROJECT_DIR}/.env.test.default ${POLICY_ENGINE_PROJECT_DIR}/.env.test +# === Build === + +policy-engine/build/script: + npx tsc --project ${POLICY_ENGINE_PROJECT_DIR}/tsconfig.app.json + npx tsc-alias --project ${POLICY_ENGINE_PROJECT_DIR}/tsconfig.app.json + # == Code format == policy-engine/format: @@ -30,9 +39,59 @@ policy-engine/format/check: policy-engine/lint/check: npx nx lint ${POLICY_ENGINE_PROJECT_NAME} +# === Database === + +policy-engine/db/generate-types: + npx prisma generate \ + --schema ${POLICY_ENGINE_DATABASE_SCHEMA} + +policy-engine/db/migrate: + npx dotenv -e ${POLICY_ENGINE_PROJECT_DIR}/.env -- \ + prisma migrate dev \ + --schema ${POLICY_ENGINE_DATABASE_SCHEMA} + +policy-engine/db/setup: + @echo "" + @echo "${TERM_GREEN}🛠️ Setting up Engine development database${TERM_NO_COLOR}" + @echo "" + npx dotenv -e ${POLICY_ENGINE_PROJECT_DIR}/.env -- \ + prisma migrate reset \ + --schema ${POLICY_ENGINE_DATABASE_SCHEMA} \ + --force + make policy-engine/db/seed + + @echo "" + @echo "${TERM_GREEN}🛠️ Setting up Engine test database${TERM_NO_COLOR}" + @echo "" + make policy-engine/test/db/setup + +policy-engine/db/create-migration: + npx dotenv -e ${POLICY_ENGINE_PROJECT_DIR}/.env -- \ + prisma migrate dev \ + --schema ${POLICY_ENGINE_DATABASE_SCHEMA} \ + --name ${NAME} + +# To maintain seed data within their respective modules and then import them +# into the main seed.ts file for execution, it's necessary to compile the +# project and resolve its path aliases before running the vanilla JavaScript +# seed entry point. +policy-engine/db/seed: + npx dotenv -e ${POLICY_ENGINE_PROJECT_DIR}/.env -- \ + ts-node -r tsconfig-paths/register --project ${POLICY_ENGINE_PROJECT_DIR}/tsconfig.app.json ${POLICY_ENGINE_PROJECT_DIR}/src/shared/module/persistence/seed.ts + + # === Testing === +policy-engine/test/db/setup: + npx dotenv -e ${POLICY_ENGINE_PROJECT_DIR}/.env.test --override -- \ + prisma migrate reset \ + --schema ${POLICY_ENGINE_DATABASE_SCHEMA} \ + --skip-seed \ + --force + + policy-engine/test/type: + make policy-engine/db/generate-types npx tsc \ --project ${POLICY_ENGINE_PROJECT_DIR}/tsconfig.app.json \ --noEmit diff --git a/apps/policy-engine/jest.setup.ts b/apps/policy-engine/jest.setup.ts index 1cdcc9d88..72a94468a 100644 --- a/apps/policy-engine/jest.setup.ts +++ b/apps/policy-engine/jest.setup.ts @@ -3,21 +3,23 @@ import fs from 'fs' import nock from 'nock' const testEnvFile = `${__dirname}/.env.test` -const envFile = `${__dirname}/.env` // Ensure a test environment variable file exists because of the override config // loading mechanics below. if (!fs.existsSync(testEnvFile)) { - throw new Error('No .env.test file found. Please create one by running "make authz/copy-default-env".') + throw new Error('No .env.test file found. Please create one by running "make policy-engine/copy-default-env".') +} + +// By default, dotenv always loads .env and then you can override with .env.test +// But this is confusing, because then you have to look in multiple files to know which envs are loaded +// So we will clear all envs and then load .env.test +// NOTE: This will also override any CLI-declared envs (e.g. `MY_ENV=test jest`) +for (const prop in process.env) { + if (Object.prototype.hasOwnProperty.call(process.env, prop)) { + delete process.env[prop] + } } -// We don't want to have two dotenv files that are exactly the same, so we -// override the default with .env.test. -// -// If a .env.test file is not found, the DATABASE_URL will fallback to the -// default. Consequently, you'll lose your development database during the -// integration tests teardown. Hence, the check above. -dotenv.config({ path: envFile }) dotenv.config({ path: testEnvFile, override: true }) // Disable outgoing HTTP requests to avoid flaky tests. diff --git a/apps/policy-engine/src/app/__test__/e2e/admin.spec.ts b/apps/policy-engine/src/app/__test__/e2e/admin.spec.ts index fd26b2695..7fd13680b 100644 --- a/apps/policy-engine/src/app/__test__/e2e/admin.spec.ts +++ b/apps/policy-engine/src/app/__test__/e2e/admin.spec.ts @@ -8,15 +8,19 @@ import { readFileSync, unlinkSync } from 'fs' import { mock } from 'jest-mock-extended' import request from 'supertest' import { AppModule } from '../../../app/app.module' +import { EncryptionService } from '../../../encryption/core/encryption.service' +import { load } from '../../../policy-engine.config' +import { PersistenceModule } from '../../../shared/module/persistence/persistence.module' +import { TestPrismaService } from '../../../shared/module/persistence/service/test-prisma.service' import { Organization } from '../../../shared/types/entities.types' import { Criterion, Then, TimeWindow } from '../../../shared/types/policy.type' -import { load } from '../../app.config' import { EntityRepository } from '../../persistence/repository/entity.repository' const REQUEST_HEADER_ORG_ID = 'x-org-id' describe('Admin Endpoints', () => { let app: INestApplication let module: TestingModule + let testPrismaService: TestPrismaService // TODO: Real sigs; these will NOT match the test data. const authentication: Signature = { @@ -45,6 +49,7 @@ describe('Admin Endpoints', () => { beforeAll(async () => { const entityRepositoryMock = mock() entityRepositoryMock.fetch.mockResolvedValue(FIXTURE.ENTITIES) + const encryptionMock = mock() module = await Test.createTestingModule({ imports: [ @@ -52,23 +57,32 @@ describe('Admin Endpoints', () => { load: [load], isGlobal: true }), - AppModule + AppModule, + PersistenceModule ] }) .overrideProvider(EntityRepository) .useValue(entityRepositoryMock) + .overrideProvider(EncryptionService) + .useValue(encryptionMock) .compile() + testPrismaService = module.get(TestPrismaService) app = module.createNestApplication() await app.init() }) afterAll(async () => { + await testPrismaService.truncateAll() await module.close() await app.close() }) + afterEach(async () => { + await testPrismaService.truncateAll() + }) + describe('POST /policies', () => { it('sets the organization policies', async () => { const payload = { diff --git a/apps/policy-engine/src/app/app.module.ts b/apps/policy-engine/src/app/app.module.ts index bef8fbb62..bcdd97b95 100644 --- a/apps/policy-engine/src/app/app.module.ts +++ b/apps/policy-engine/src/app/app.module.ts @@ -2,7 +2,8 @@ import { HttpModule } from '@nestjs/axios' import { Module, ValidationPipe } from '@nestjs/common' import { ConfigModule } from '@nestjs/config' import { APP_PIPE } from '@nestjs/core' -import { load } from './app.config' +import { EncryptionModule } from '../encryption/encryption.module' +import { load } from '../policy-engine.config' import { AppController } from './app.controller' import { AppService } from './app.service' import { AdminService } from './core/admin.service' @@ -16,7 +17,8 @@ import { EntityRepository } from './persistence/repository/entity.repository' load: [load], isGlobal: true }), - HttpModule + HttpModule, + EncryptionModule ], controllers: [AppController, AdminController], providers: [ diff --git a/apps/policy-engine/src/app/persistence/repository/__test__/unit/tenant.repository.spec.ts b/apps/policy-engine/src/app/persistence/repository/__test__/unit/tenant.repository.spec.ts new file mode 100644 index 000000000..425ff03bb --- /dev/null +++ b/apps/policy-engine/src/app/persistence/repository/__test__/unit/tenant.repository.spec.ts @@ -0,0 +1,60 @@ +import { DataStoreConfiguration } from '@narval/policy-engine-shared' +import { Test } from '@nestjs/testing' +import { KeyValueRepository } from '../../../../../shared/module/key-value/core/repository/key-value.repository' +import { KeyValueService } from '../../../../../shared/module/key-value/core/service/key-value.service' +import { InMemoryKeyValueRepository } from '../../../../../shared/module/key-value/persistence/repository/in-memory-key-value.repository' +import { Tenant } from '../../../../../shared/types/domain.type' +import { TenantRepository } from '../../../repository/tenant.repository' + +describe(TenantRepository.name, () => { + let repository: TenantRepository + let inMemoryKeyValueRepository: InMemoryKeyValueRepository + + beforeEach(async () => { + inMemoryKeyValueRepository = new InMemoryKeyValueRepository() + + const module = await Test.createTestingModule({ + providers: [ + KeyValueService, + TenantRepository, + { + provide: KeyValueRepository, + useValue: inMemoryKeyValueRepository + } + ] + }).compile() + + repository = module.get(TenantRepository) + }) + + describe('create', () => { + const now = new Date() + + const dataStoreConfiguration: DataStoreConfiguration = { + dataUrl: 'a-url-that-doesnt-need-to-exist-for-the-purpose-of-this-test', + signatureUrl: 'a-url-that-doesnt-need-to-exist-for-the-purpose-of-this-test', + keys: [] + } + + const tenant: Tenant = { + clientId: 'test-client-id', + clientSecret: 'test-client-secret', + dataStore: { + entity: dataStoreConfiguration, + policy: dataStoreConfiguration + }, + createdAt: now, + updatedAt: now + } + + it('creates a new tenant', async () => { + await repository.create(tenant) + + const value = await inMemoryKeyValueRepository.get(repository.getKey(tenant.clientId)) + const actualTenant = await repository.findByClientId(tenant.clientId) + + expect(value).not.toEqual(null) + expect(tenant).toEqual(actualTenant) + }) + }) +}) diff --git a/apps/policy-engine/src/app/persistence/repository/tenant.repository.ts b/apps/policy-engine/src/app/persistence/repository/tenant.repository.ts new file mode 100644 index 000000000..1ede4c633 --- /dev/null +++ b/apps/policy-engine/src/app/persistence/repository/tenant.repository.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@nestjs/common' +import { KeyValueService } from '../../../shared/module/key-value/core/service/key-value.service' +import { tenantSchema } from '../../../shared/schema/tenant.schema' +import { Tenant } from '../../../shared/types/domain.type' + +@Injectable() +export class TenantRepository { + constructor(private keyValueService: KeyValueService) {} + + async findByClientId(clientId: string): Promise { + const value = await this.keyValueService.get(this.getKey(clientId)) + + if (value) { + return this.decode(value) + } + + return null + } + + async create(tenant: Tenant): Promise { + await this.keyValueService.set(this.getKey(tenant.clientId), this.encode(tenant)) + + return tenant + } + + getKey(clientId: string): string { + return `${clientId}:tenant` + } + + private encode(tenant: Tenant): string { + return JSON.stringify(tenant) + } + + private decode(value: string): Tenant { + return tenantSchema.parse(JSON.parse(value)) + } +} diff --git a/apps/policy-engine/src/encryption/core/__test__/unit/encryption.service.spec.ts b/apps/policy-engine/src/encryption/core/__test__/unit/encryption.service.spec.ts new file mode 100644 index 000000000..019aa94ac --- /dev/null +++ b/apps/policy-engine/src/encryption/core/__test__/unit/encryption.service.spec.ts @@ -0,0 +1,82 @@ +import { ConfigModule, ConfigService } from '@nestjs/config' +import { Test } from '@nestjs/testing' +import { mock } from 'jest-mock-extended' +import nock from 'nock' +import { load } from '../../../../policy-engine.config' +import { EncryptionRepository } from '../../../persistence/repository/encryption.repository' +import { EncryptionService } from '../../encryption.service' + +describe('EncryptionService', () => { + let service: EncryptionService + + nock.enableNetConnect('kms.us-east-2.amazonaws.com:443') + + beforeEach(async () => { + // These mocked config values matter; they're specifically tied to the mocked masterKey below + // If you change these, the decryption won't work & tests will fail + const configServiceMock = mock({ + get: jest.fn().mockImplementation((key: string) => { + if (key === 'keyring') { + return { + type: 'raw', + masterPassword: 'unsafe-local-dev-master-password' + } + } + if (key === 'engine.id') { + return 'local-dev-engine-instance-1' + } + }) + }) + + const encryptionRepositoryMock = { + getEngine: jest.fn().mockImplementation(() => + Promise.resolve({ + // unencryptedMasterKey: dfd9cc70f1ad02d19e0efa020d82f557022f59ca6bedbec1df38e8fd37ae3bb9 + masterKey: + '0205785d67737fa3bae8eb249cf8d3baed5942f1677d8c98b4cdeef55560a3bcf510bd008d00030003617070000d61726d6f72792d656e67696e6500156177732d63727970746f2d7075626c69632d6b657900444177336764324b6e58646f512f2b76745347367031444442384d65766d61434b324c7861426e65476a315531537777526b376b4d366868752f707a446f48724c77773d3d0007707572706f7365000f646174612d656e6372797074696f6e000100146e617276616c2e61726d6f72792e656e67696e65002561726d6f72792e656e67696e652e6b656b000000800000000c8a92a7c9deb43316f6c29e8d0030132d63c7337c9888a06b638966e83056a0575958b42588b7aed999b9659e6d4bc5bed4664d91fae0b14d48917e00cdbb02000010000749ed0ed3616b7990f9e73f5a42eb46dc182002612e33dcb8e3c7d4759184c46ce3f0893a87ac15257d53097ac5d74affffffff00000001000000000000000000000001000000205d7209b51db8cf8264b9065add71a8514dc26baa6987d8a0a3acb1c4a2503b0f3b7c974a35ed234c1b94668736cd8bfa00673065023100a5d8d192e9802649dab86af6e00ab6d7472533e85dfe1006cb8bd9ef2472d15096fa42e742d18cb92530c762c3bd44d40230350299b42feaa1149c6ad1b25add24c30b3bf1c08263b96df0d43e2ad3e19802872e792040f1faf3d0a73bca6fb067ca', + id: 'test-engine-id' + }) + ) + } + const moduleRef = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + load: [load], + isGlobal: true + }) + ], + providers: [ + EncryptionService, + { + provide: EncryptionRepository, + useValue: encryptionRepositoryMock + }, + { + provide: ConfigService, + useValue: configServiceMock // use the mock ConfigService + } + ] + }).compile() + + service = moduleRef.get(EncryptionService) + if (service.onApplicationBootstrap) { + await service.onApplicationBootstrap() + } + }) + + it('should encrypt then decrypt successfully, with a string', async () => { + const data = 'Hello World' + const encrypted = await service.encrypt(data) + const decrypted = await service.decrypt(encrypted) + + expect(decrypted.toString('utf-8')).toBe(data) + }) + + it('should encrypt then decrypt successfully, with a buffer from a hexstring', async () => { + const data = 'dfd9cc70f1ad02d19e0efa020d82f557022f59ca6bedbec1df38e8fd37ae3bb9' + const encrypted = await service.encrypt(Buffer.from(data, 'hex')) + const decrypted = await service.decrypt(encrypted) + + expect(decrypted.toString('hex')).toBe(data) + }) +}) diff --git a/apps/policy-engine/src/encryption/core/encryption.service.ts b/apps/policy-engine/src/encryption/core/encryption.service.ts new file mode 100644 index 000000000..149ab1e06 --- /dev/null +++ b/apps/policy-engine/src/encryption/core/encryption.service.ts @@ -0,0 +1,175 @@ +import { + CommitmentPolicy, + KmsKeyringNode, + RawAesKeyringNode, + RawAesWrappingSuiteIdentifier, + buildClient +} from '@aws-crypto/client-node' +import { Inject, Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common' +import { ConfigService } from '@nestjs/config' +import crypto from 'crypto' +import { Config } from '../../policy-engine.config' +import { EncryptionRepository } from '../persistence/repository/encryption.repository' + +const keyNamespace = 'narval.armory.engine' +const commitmentPolicy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT +const wrappingSuite = RawAesWrappingSuiteIdentifier.AES256_GCM_IV12_TAG16_NO_PADDING +const defaultEncryptionContext = { + purpose: 'data-encryption', + app: 'armory-engine' +} + +const { encrypt, decrypt } = buildClient(commitmentPolicy) + +@Injectable() +export class EncryptionService implements OnApplicationBootstrap { + private logger = new Logger(EncryptionService.name) + + private configService: ConfigService + + private engineId: string + + private masterKey: Buffer | undefined + + private keyring: RawAesKeyringNode | KmsKeyringNode | undefined + + constructor( + private encryptionRepository: EncryptionRepository, + @Inject(ConfigService) configService: ConfigService + ) { + this.configService = configService + this.engineId = configService.get('engine.id', { infer: true }) + } + + async onApplicationBootstrap(): Promise { + this.logger.log('Keyring Service boot') + const keyringConfig = this.configService.get('keyring', { infer: true }) + + // We have a Raw Keyring, so we are using a MasterPassword/KEK+MasterKey for encryption + if (keyringConfig.masterPassword && !keyringConfig.masterAwsKmsArn) { + const engine = await this.encryptionRepository.getEngine(this.engineId) + let encryptedMasterKey = engine?.masterKey + + // Derive the Key Encryption Key (KEK) from the master password using PBKDF2 + const masterPassword = keyringConfig.masterPassword + const kek = this.deriveKeyEncryptionKey(masterPassword) + + if (!encryptedMasterKey) { + // No MK yet, so create it & encrypt w/ the KEK + encryptedMasterKey = await this.generateMasterKey(kek) + } + + const decryptedMasterKey = await this.decryptMasterKey(kek, Buffer.from(encryptedMasterKey, 'hex')) + const isolatedMasterKey = Buffer.alloc(decryptedMasterKey.length) + decryptedMasterKey.copy(isolatedMasterKey, 0, 0, decryptedMasterKey.length) + + /* Configure the Raw AES keyring. */ + const keyring = new RawAesKeyringNode({ + keyName: 'armory.engine.wrapping-key', + keyNamespace, + unencryptedMasterKey: isolatedMasterKey, + wrappingSuite + }) + + this.keyring = keyring + } + // We have AWS KMS config so we'll use that instead as the MasterKey, which means we don't need a KEK separately + else if (keyringConfig.masterAwsKmsArn && !keyringConfig.masterPassword) { + const keyring = new KmsKeyringNode({ generatorKeyId: keyringConfig.masterAwsKmsArn }) + this.keyring = keyring + } else { + throw new Error('Invalid Keyring Configuration found') + } + } + + private getKeyEncryptionKeyring(kek: Buffer) { + const keyring = new RawAesKeyringNode({ + keyName: 'armory.engine.kek', + keyNamespace, + unencryptedMasterKey: kek, + wrappingSuite + }) + + return keyring + } + + private deriveKeyEncryptionKey(password: string): Buffer { + // Derive the Key Encryption Key (KEK) from the master password using PBKDF2 + const kek = crypto.pbkdf2Sync(password.normalize(), this.engineId.normalize(), 1000000, 32, 'sha256') + return kek + } + + private async encryptMaterKey(kek: Buffer, cleartext: Buffer): Promise { + // Encrypt the Master Key (MK) with the Key Encryption Key (KEK) + const keyring = this.getKeyEncryptionKeyring(kek) + const { result } = await encrypt(keyring, cleartext, { + encryptionContext: defaultEncryptionContext + }) + + return result + } + + private async decryptMasterKey(kek: Buffer, ciphertext: Buffer): Promise { + const keyring = this.getKeyEncryptionKeyring(kek) + const { plaintext, messageHeader } = await decrypt(keyring, ciphertext) + + // Verify the context wasn't changed + const { encryptionContext } = messageHeader + + Object.entries(defaultEncryptionContext).forEach(([key, value]) => { + if (encryptionContext[key] !== value) throw new Error('Encryption Context does not match expected values') + }) + + return plaintext + } + + async encrypt(cleartext: string | Buffer): Promise { + const keyring = this.keyring + if (!keyring) throw new Error('Keyring not set') + + const { result } = await encrypt(keyring, cleartext, { + encryptionContext: defaultEncryptionContext + }) + + return result + } + + async decrypt(ciphertext: Buffer): Promise { + const keyring = this.keyring + if (!keyring) throw new Error('Keyring not set') + + const { plaintext, messageHeader } = await decrypt(keyring, ciphertext) + + // Verify the context wasn't changed + const { encryptionContext } = messageHeader + + Object.entries(defaultEncryptionContext).forEach(([key, value]) => { + if (encryptionContext[key] !== value) throw new Error('Encryption Context does not match expected values') + }) + + return plaintext + } + + private async generateMasterKey(kek: Buffer): Promise { + // Generate a new Master Key (MK) with AES256 + const mk = crypto.generateKeySync('aes', { length: 256 }) + const mkBuffer = mk.export() + + // Encrypt it with the Key Encryption Key (KEK) that was derived from the MP + const encryptedMk = await this.encryptMaterKey(kek, mkBuffer) + const encryptedMkString = encryptedMk.toString('hex') + + // Save the Result. + const existingEngine = await this.encryptionRepository.getEngine(this.engineId) + const engine = existingEngine + ? await this.encryptionRepository.saveMasterKey(this.engineId, encryptedMkString) + : await this.encryptionRepository.createEngine(this.engineId, encryptedMkString) + + if (!engine?.masterKey) { + throw new Error('Master Key was not saved') + } + + this.logger.log('Engine Master Key Setup Complete') + return encryptedMkString + } +} diff --git a/apps/policy-engine/src/encryption/encryption.module.ts b/apps/policy-engine/src/encryption/encryption.module.ts new file mode 100644 index 000000000..568f96dcc --- /dev/null +++ b/apps/policy-engine/src/encryption/encryption.module.ts @@ -0,0 +1,21 @@ +import { HttpModule } from '@nestjs/axios' +import { Module, ValidationPipe } from '@nestjs/common' +import { ConfigModule } from '@nestjs/config' +import { APP_PIPE } from '@nestjs/core' +import { PersistenceModule } from '../shared/module/persistence/persistence.module' +import { EncryptionService } from './core/encryption.service' +import { EncryptionRepository } from './persistence/repository/encryption.repository' + +@Module({ + imports: [ConfigModule.forRoot(), HttpModule, PersistenceModule], + controllers: [], + providers: [ + EncryptionService, + EncryptionRepository, + { + provide: APP_PIPE, + useClass: ValidationPipe + } + ] +}) +export class EncryptionModule {} diff --git a/apps/policy-engine/src/encryption/persistence/repository/encryption.repository.ts b/apps/policy-engine/src/encryption/persistence/repository/encryption.repository.ts new file mode 100644 index 000000000..5d8c0bdf6 --- /dev/null +++ b/apps/policy-engine/src/encryption/persistence/repository/encryption.repository.ts @@ -0,0 +1,39 @@ +import { Injectable, Logger } from '@nestjs/common' +import { PrismaService } from '../../../shared/module/persistence/service/prisma.service' + +@Injectable() +export class EncryptionRepository { + private logger = new Logger(EncryptionRepository.name) + + constructor(private prismaService: PrismaService) {} + + async getEngine(engineId: string) { + return this.prismaService.engine.findUnique({ + where: { + id: engineId + } + }) + } + + async createEngine(engineId: string, masterKey: string, adminApiKey?: string) { + return this.prismaService.engine.create({ + data: { + id: engineId, + masterKey, + adminApiKey + } + }) + } + + async saveMasterKey(engineId: string, masterKey: string) { + return this.prismaService.engine.update({ + where: { + id: engineId, + masterKey: null // ONLY allow updating it if already null. We don't want to accidentally overwrite it! + }, + data: { + masterKey + } + }) + } +} diff --git a/apps/policy-engine/src/main.ts b/apps/policy-engine/src/main.ts index 9c69e1132..b17a3c481 100644 --- a/apps/policy-engine/src/main.ts +++ b/apps/policy-engine/src/main.ts @@ -55,7 +55,7 @@ async function bootstrap() { ) ) - logger.log(`AuthZ is running on port ${port}`) + logger.log(`Policy Engine is running on port ${port}`) } bootstrap() diff --git a/apps/policy-engine/src/app/app.config.ts b/apps/policy-engine/src/policy-engine.config.ts similarity index 50% rename from apps/policy-engine/src/app/app.config.ts rename to apps/policy-engine/src/policy-engine.config.ts index a72ffc9b3..3f6e8c03e 100644 --- a/apps/policy-engine/src/app/app.config.ts +++ b/apps/policy-engine/src/policy-engine.config.ts @@ -10,7 +10,15 @@ const ConfigSchema = z.object({ env: z.nativeEnum(Env), port: z.coerce.number(), database: z.object({ - url: z.string().startsWith('file:') + url: z.string().startsWith('postgresql:') + }), + engine: z.object({ + id: z.string() + }), + keyring: z.object({ + type: z.enum(['awskms', 'raw']).default('raw'), + masterAwsKmsArn: z.string().optional(), // only if type = awskms + masterPassword: z.string().optional() // only if type = raw }) }) @@ -21,7 +29,15 @@ export const load = (): Config => { env: process.env.NODE_ENV, port: process.env.PORT, database: { - url: process.env.ENGINE_DATABASE_URL + url: process.env.POLICY_ENGINE_DATABASE_URL + }, + engine: { + id: process.env.ENGINE_UID + }, + keyring: { + type: process.env.KEYRING_TYPE, + masterAwsKmsArn: process.env.MASTER_AWS_KMS_ARN, + masterPassword: process.env.MASTER_PASSWORD } }) diff --git a/apps/policy-engine/src/shared/module/key-value/core/repository/key-value.repository.ts b/apps/policy-engine/src/shared/module/key-value/core/repository/key-value.repository.ts new file mode 100644 index 000000000..b70505d50 --- /dev/null +++ b/apps/policy-engine/src/shared/module/key-value/core/repository/key-value.repository.ts @@ -0,0 +1,7 @@ +export const KeyValueRepository = Symbol('KeyValueRepository') + +export interface KeyValueRepository { + get(key: string): Promise + set(key: string, value: string): Promise + delete(key: string): Promise +} diff --git a/apps/policy-engine/src/shared/module/key-value/core/service/__test__/unit/key-value.service.spec.ts b/apps/policy-engine/src/shared/module/key-value/core/service/__test__/unit/key-value.service.spec.ts new file mode 100644 index 000000000..b5b4e6a98 --- /dev/null +++ b/apps/policy-engine/src/shared/module/key-value/core/service/__test__/unit/key-value.service.spec.ts @@ -0,0 +1,44 @@ +import { ConfigModule } from '@nestjs/config' +import { Test } from '@nestjs/testing' +import { load } from '../../../../../../../policy-engine.config' +import { InMemoryKeyValueRepository } from '../../../../persistence/repository/in-memory-key-value.repository' +import { KeyValueRepository } from '../../../repository/key-value.repository' +import { KeyValueService } from '../../key-value.service' + +describe('foo', () => { + let service: KeyValueService + let inMemoryKeyValueRepository: InMemoryKeyValueRepository + + beforeEach(async () => { + inMemoryKeyValueRepository = new InMemoryKeyValueRepository() + + const module = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + load: [load], + isGlobal: true + }) + ], + providers: [ + KeyValueService, + { + provide: KeyValueRepository, + useValue: inMemoryKeyValueRepository + } + ] + }).compile() + + service = module.get(KeyValueService) + }) + + describe('set', () => { + it('sets encrypted value in the key-value storage', async () => { + const key = 'test-key' + const value = 'not encrypted value' + + await service.set(key, value) + + expect(await service.get(key)).toEqual(value) + }) + }) +}) diff --git a/apps/policy-engine/src/shared/module/key-value/core/service/key-value.service.ts b/apps/policy-engine/src/shared/module/key-value/core/service/key-value.service.ts new file mode 100644 index 000000000..88023934c --- /dev/null +++ b/apps/policy-engine/src/shared/module/key-value/core/service/key-value.service.ts @@ -0,0 +1,30 @@ +import { Inject, Injectable } from '@nestjs/common' +import { KeyValueRepository } from '../repository/key-value.repository' + +/** + * The key-value service is the main interface to interact with any storage + * back-end. Since the storage backend lives outside the engine, it's considered + * untrusted so the engine will encrypt the data before it sends them to the + * storage. + * + * It's because of that the key-value service assumes data is always encrypted. + * If you need non-encrypted data, you can use the key-value repository. + */ +@Injectable() +export class KeyValueService { + constructor(@Inject(KeyValueRepository) private keyValueRepository: KeyValueRepository) {} + + async get(key: string): Promise { + // TODO (@wcalderipe, 01/03/2024): Add decryption step. + return this.keyValueRepository.get(key) + } + + async set(key: string, value: string): Promise { + // TODO (@wcalderipe, 01/03/2024): Add encryption step. + return this.keyValueRepository.set(key, value) + } + + async delete(key: string): Promise { + return this.keyValueRepository.delete(key) + } +} diff --git a/apps/policy-engine/src/shared/module/key-value/key-value.module.ts b/apps/policy-engine/src/shared/module/key-value/key-value.module.ts new file mode 100644 index 000000000..4323036c6 --- /dev/null +++ b/apps/policy-engine/src/shared/module/key-value/key-value.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common' +import { KeyValueRepository } from './core/repository/key-value.repository' +import { KeyValueService } from './core/service/key-value.service' +import { InMemoryKeyValueRepository } from './persistence/repository/in-memory-key-value.repository' + +@Module({ + providers: [ + KeyValueService, + { + provide: KeyValueRepository, + useClass: InMemoryKeyValueRepository + } + ], + exports: [KeyValueService, KeyValueRepository] +}) +export class KeyValueModule {} diff --git a/apps/policy-engine/src/shared/module/key-value/persistence/repository/in-memory-key-value.repository.ts b/apps/policy-engine/src/shared/module/key-value/persistence/repository/in-memory-key-value.repository.ts new file mode 100644 index 000000000..df88feab2 --- /dev/null +++ b/apps/policy-engine/src/shared/module/key-value/persistence/repository/in-memory-key-value.repository.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@nestjs/common' +import { KeyValueRepository } from '../../core/repository/key-value.repository' + +@Injectable() +export class InMemoryKeyValueRepository implements KeyValueRepository { + private store = new Map() + + async get(key: string): Promise { + return this.store.get(key) || null + } + + async set(key: string, value: string): Promise { + this.store.set(key, value) + + return true + } + + async delete(key: string): Promise { + this.store.delete(key) + + return true + } +} diff --git a/apps/policy-engine/src/shared/module/persistence/persistence.module.ts b/apps/policy-engine/src/shared/module/persistence/persistence.module.ts new file mode 100644 index 000000000..95d5dcd4c --- /dev/null +++ b/apps/policy-engine/src/shared/module/persistence/persistence.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common' +import { PrismaService } from './service/prisma.service' +import { TestPrismaService } from './service/test-prisma.service' + +@Module({ + exports: [PrismaService, TestPrismaService], + providers: [PrismaService, TestPrismaService] +}) +export class PersistenceModule {} diff --git a/apps/policy-engine/src/shared/module/persistence/schema/migrations/20240301204146_init/migration.sql b/apps/policy-engine/src/shared/module/persistence/schema/migrations/20240301204146_init/migration.sql new file mode 100644 index 000000000..a57f19a4e --- /dev/null +++ b/apps/policy-engine/src/shared/module/persistence/schema/migrations/20240301204146_init/migration.sql @@ -0,0 +1,8 @@ +-- CreateTable +CREATE TABLE "engine" ( + "id" TEXT NOT NULL, + "master_key" TEXT, + "admin_api_key" TEXT, + + CONSTRAINT "engine_pkey" PRIMARY KEY ("id") +); diff --git a/apps/policy-engine/src/shared/module/persistence/schema/migrations/migration_lock.toml b/apps/policy-engine/src/shared/module/persistence/schema/migrations/migration_lock.toml new file mode 100644 index 000000000..fbffa92c2 --- /dev/null +++ b/apps/policy-engine/src/shared/module/persistence/schema/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/apps/policy-engine/src/shared/module/persistence/schema/schema.prisma b/apps/policy-engine/src/shared/module/persistence/schema/schema.prisma new file mode 100644 index 000000000..9ea1412f7 --- /dev/null +++ b/apps/policy-engine/src/shared/module/persistence/schema/schema.prisma @@ -0,0 +1,21 @@ +generator client { + provider = "prisma-client-js" + // Output into a separate subdirectory so multiple schemas can be used in a + // monorepo. + // + // Reference: https://github.com/nrwl/nx-recipes/tree/main/nestjs-prisma + output = "../../../../../../../node_modules/@prisma/client/policy-engine" +} + +datasource db { + provider = "postgresql" + url = env("POLICY_ENGINE_DATABASE_URL") +} + +model Engine { + id String @id + masterKey String? @map("master_key") + adminApiKey String? @map("admin_api_key") + + @@map("engine") +} diff --git a/apps/policy-engine/src/shared/module/persistence/seed.ts b/apps/policy-engine/src/shared/module/persistence/seed.ts new file mode 100644 index 000000000..5f42eb050 --- /dev/null +++ b/apps/policy-engine/src/shared/module/persistence/seed.ts @@ -0,0 +1,32 @@ +/* eslint-disable */ +import { Logger } from '@nestjs/common' +import { Engine, PrismaClient } from '@prisma/client/policy-engine' + +const prisma = new PrismaClient() + +const engine: Engine = { + id: '7d704a62-d15e-4382-a826-1eb41563043b', + adminApiKey: 'admin-api-key-xxx', + masterKey: 'master-key-xxx' +} + +async function main() { + const logger = new Logger('EngineSeed') + + logger.log('Seeding Engine database') + await prisma.$transaction(async (txn) => { + // await txn.engine.create({ data: engine }) + }) + + logger.log('Engine database germinated 🌱') +} + +main() + .then(async () => { + await prisma.$disconnect() + }) + .catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) + }) diff --git a/apps/policy-engine/src/shared/module/persistence/service/__test__/unit/prisma.service.spec.ts b/apps/policy-engine/src/shared/module/persistence/service/__test__/unit/prisma.service.spec.ts new file mode 100644 index 000000000..df6330a77 --- /dev/null +++ b/apps/policy-engine/src/shared/module/persistence/service/__test__/unit/prisma.service.spec.ts @@ -0,0 +1,17 @@ +import { ConfigService } from '@nestjs/config' +import { mock } from 'jest-mock-extended' +import { PrismaService } from '../../prisma.service' + +describe(PrismaService.name, () => { + describe('constructor', () => { + it('does not throw when POLICY_ENGINE_DATABASE_URL is present', () => { + const configServiceMock = mock({ + get: jest.fn().mockReturnValue('postgresql://test:test@localhost:5432/test?schema=public') + }) + + expect(() => { + new PrismaService(configServiceMock) + }).not.toThrow() + }) + }) +}) diff --git a/apps/policy-engine/src/shared/module/persistence/service/prisma.service.ts b/apps/policy-engine/src/shared/module/persistence/service/prisma.service.ts new file mode 100644 index 000000000..4034b6c1a --- /dev/null +++ b/apps/policy-engine/src/shared/module/persistence/service/prisma.service.ts @@ -0,0 +1,54 @@ +import { Inject, Injectable, Logger, OnApplicationShutdown, OnModuleDestroy, OnModuleInit } from '@nestjs/common' +import { ConfigService } from '@nestjs/config' +import { PrismaClient } from '@prisma/client/policy-engine' +import { Config } from '../../../../policy-engine.config' + +@Injectable() +export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy, OnApplicationShutdown { + private logger = new Logger(PrismaService.name) + + constructor(@Inject(ConfigService) configService: ConfigService) { + const url = configService.get('database.url', { infer: true }) + + super({ + datasources: { + db: { url } + } + }) + } + + async onModuleInit() { + this.logger.log({ + message: 'Connecting to Prisma on database module initialization' + }) + + await this.$connect() + } + + async onModuleDestroy() { + this.logger.log({ + message: 'Disconnecting from Prisma on module destroy' + }) + + await this.$disconnect() + } + + // In Prisma v5, the `beforeExit` is no longer available. Instead, we use + // NestJS' application shutdown to disconnect from the database. The shutdown + // hooks are called when the process receives a termination event lig SIGhooks + // are called when the process receives a termination event lig SIGTERM. + // + // See also https://www.prisma.io/docs/guides/upgrade-guides/upgrading-versions/upgrading-to-prisma-5#removal-of-the-beforeexit-hook-from-the-library-engine + onApplicationShutdown(signal: string) { + this.logger.log({ + message: 'Disconnecting from Prisma on application shutdown', + signal + }) + + // The $disconnect method returns a promise, so idealy we should wait for it + // to finish. However, the onApplicationShutdown, returns `void` making it + // impossible to ensure the database will be properly disconnected before + // the shutdown. + this.$disconnect() + } +} diff --git a/apps/policy-engine/src/shared/module/persistence/service/test-prisma.service.ts b/apps/policy-engine/src/shared/module/persistence/service/test-prisma.service.ts new file mode 100644 index 000000000..b64e99ec1 --- /dev/null +++ b/apps/policy-engine/src/shared/module/persistence/service/test-prisma.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@nestjs/common' +import { PrismaClient } from '@prisma/client/policy-engine' +import { PrismaService } from './prisma.service' + +@Injectable() +export class TestPrismaService { + constructor(private prisma: PrismaService) {} + + getClient(): PrismaClient { + return this.prisma + } + + async truncateAll(): Promise { + const tablenames = await this.prisma.$queryRaw< + Array<{ tablename: string }> + >`SELECT tablename FROM pg_tables WHERE schemaname='public'` + + for (const { tablename } of tablenames) { + if (tablename !== '_prisma_migrations') { + try { + await this.prisma.$executeRawUnsafe(`TRUNCATE TABLE "public"."${tablename}" CASCADE;`) + } catch (error) { + // The logger may be intentionally silented during tests. Thus, we use + // console.log to ensure engineers will see the error in the stdout. + // + // eslint-disable-next-line no-console + console.error('TestPrismaService truncateAll error', error) + } + } + } + } +} diff --git a/apps/policy-engine/src/shared/schema/tenant.schema.ts b/apps/policy-engine/src/shared/schema/tenant.schema.ts new file mode 100644 index 000000000..64848aca8 --- /dev/null +++ b/apps/policy-engine/src/shared/schema/tenant.schema.ts @@ -0,0 +1,13 @@ +import { dataStoreConfigurationSchema } from 'packages/policy-engine-shared/src/lib/schema/data-store.schema' +import { z } from 'zod' + +export const tenantSchema = z.object({ + clientId: z.string(), + clientSecret: z.string(), + dataStore: z.object({ + entity: dataStoreConfigurationSchema, + policy: dataStoreConfigurationSchema + }), + createdAt: z.coerce.date(), + updatedAt: z.coerce.date() +}) diff --git a/apps/policy-engine/src/shared/types/domain.type.ts b/apps/policy-engine/src/shared/types/domain.type.ts index e0129bf4d..bb441d52a 100644 --- a/apps/policy-engine/src/shared/types/domain.type.ts +++ b/apps/policy-engine/src/shared/types/domain.type.ts @@ -6,13 +6,10 @@ import { TransactionRequest } from '@narval/policy-engine-shared' import { Intent } from '@narval/transaction-request-intent' +import { z } from 'zod' +import { tenantSchema } from '../schema/tenant.schema' -export enum UserRoles { - ROOT = 'root', - ADMIN = 'admin', - MEMBER = 'member', - MANAGER = 'manager' -} +export type Tenant = z.infer export type RegoInput = { action: Action diff --git a/doc/policy-engine-architecture.md b/doc/policy-engine-architecture.md new file mode 100644 index 000000000..f192a674b --- /dev/null +++ b/doc/policy-engine-architecture.md @@ -0,0 +1,44 @@ +# Policy Engine Architecture + +This document describes the high-level architecture of the policy engine. + +```mermaid +block-beta +columns 1 + HTTP + block:Engine + Core space OPA["Open Policy Agent"] + end + space + block:Storage + StorageBackend["Storage Backend"] + PolicyStore["Policy Store"] + EntityStore["Entity Store"] + end + Core -- "evaluates" --> OPA + Engine -- "reads/writes" --> StorageBackend + Engine -- "reads" --> PolicyStore + Engine -- "reads" --> EntityStore +``` + +The policy engine is built on the shoulders of the Open Policy Agent (OPA), a +battle-tested multi purpose engine trusted by many organizations. The OPA core +bunlded with the policy engine's artifact and it's considered a trusted +component. + +The policy engine layer, is responsible for encrypting and decrypting tenant +data it stores. When the engine server starts, it writes data to its storage +backend. Since the storage backend lives outside the engine, it's considered +untrusted so the engine will encrypt the data before it sends them to the +storage. This mechanism ensures that if a malicious attacker attempts to gain +access to the storage backend, the data cannot be compromised since it remains +encrypted, until the engine decrypts it. The storage backend must provide a +durable data persistence where data is secured and available across the engine's +instances. + +Similar to the storage backend, the policy store and the entity store also live +outside and are considered untrusted. The key difference is that the engine +never writes to these stores, it only reads from them. The data in these stores +are hashed and signed by the tenant's private key. The engine uses the tenant's +public key to verify the signature and hash. This mechanism ensures that the +data is authentic and has not been tampered with. diff --git a/package-lock.json b/package-lock.json index 465c56d9b..bf5aa4f0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "apps/documentation" ], "dependencies": { + "@aws-crypto/client-node": "^4.0.0", "@bull-board/api": "^5.13.0", "@bull-board/express": "^5.13.0", "@bull-board/nestjs": "^5.11.0", @@ -512,6 +513,774 @@ "integrity": "sha512-ue35e4im3n7l+Eqq3kCA2nNs8jzJbjYLni+vlPdgJ+9KfsEykJjA1OpnNN/PG1tDaM0iyR2p/uZg27MOC/qiTg==", "dev": true }, + "node_modules/@aws-crypto/cache-material": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/cache-material/-/cache-material-4.0.0.tgz", + "integrity": "sha512-14m9QPzgMJZ2QdbiM7LCMKgqmONx+/9+Zm5YlXJmhP6Ue+qgniCs5MBOT99WKF50sihcjlA8cVbOUBBJh9t1mg==", + "dependencies": { + "@aws-crypto/material-management": "^4.0.0", + "@aws-crypto/serialize": "^4.0.0", + "@types/lru-cache": "^5.1.0", + "lru-cache": "^6.0.0", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/cache-material/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@aws-crypto/cache-material/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/@aws-crypto/caching-materials-manager-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/caching-materials-manager-node/-/caching-materials-manager-node-4.0.0.tgz", + "integrity": "sha512-uPhbFMyiHImEsYIZRuzwMO/VS/tzdUMKCN+p9/Hg5I2r97riqPU5ukfjA8whAsVygGNWRuhlnrwBdjwRE+MZuw==", + "dependencies": { + "@aws-crypto/cache-material": "^4.0.0", + "@aws-crypto/material-management-node": "^4.0.0", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/client-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/client-node/-/client-node-4.0.0.tgz", + "integrity": "sha512-hsYDRSY2MSaAS5AcToR4oSS3sZINVXlOIutJzNkLSLEJuiYwiycJJrILRDCQJk+G6XhT7oJeqbmxFjRA7X83KA==", + "dependencies": { + "@aws-crypto/caching-materials-manager-node": "^4.0.0", + "@aws-crypto/decrypt-node": "^4.0.0", + "@aws-crypto/encrypt-node": "^4.0.0", + "@aws-crypto/kms-keyring-node": "^4.0.0", + "@aws-crypto/material-management-node": "^4.0.0", + "@aws-crypto/raw-aes-keyring-node": "^4.0.0", + "@aws-crypto/raw-rsa-keyring-node": "^4.0.0", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/decrypt-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/decrypt-node/-/decrypt-node-4.0.0.tgz", + "integrity": "sha512-8xJ0Bjr0l4sBKpNM+zxAqfgUlcZxb/Jqj8IOTzL5IXEO301KH/qNJ1saI37Epmb0v9iKfmCou1D8pu9Y9GnuMw==", + "dependencies": { + "@aws-crypto/material-management-node": "^4.0.0", + "@aws-crypto/serialize": "^4.0.0", + "@types/duplexify": "^3.6.0", + "duplexify": "^4.1.1", + "readable-stream": "^3.6.0", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/encrypt-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/encrypt-node/-/encrypt-node-4.0.0.tgz", + "integrity": "sha512-p/iSjYh3u4KFet9vmlnGn2YYf+j3aTQxh5SoxpkxnzSVvH3Sc/Ul5mPu13hIatIjrpnJGH5JNdT98igtsatWxA==", + "dependencies": { + "@aws-crypto/material-management-node": "^4.0.0", + "@aws-crypto/serialize": "^4.0.0", + "@types/duplexify": "^3.6.0", + "duplexify": "^4.1.1", + "readable-stream": "^3.6.0", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/hkdf-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/hkdf-node/-/hkdf-node-4.0.0.tgz", + "integrity": "sha512-FytH3TF9c0OP+vnicc4YJoxoFoLajdRzzuRchDHmh4yXk32lj/HzgXGPfj+kSyy0chkh4XVONh2/zMRmqsA/hQ==", + "dependencies": { + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/kms-keyring": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/kms-keyring/-/kms-keyring-4.0.0.tgz", + "integrity": "sha512-05jqVPbgzZA3R5ZBZznUtc3T9SNAdaLptRU4bnwHeB5kxrhTU8vT+Mabp9vvqhdRauPkZMZvWpvTSWIyDXiYdA==", + "dependencies": { + "@aws-crypto/material-management": "^4.0.0", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/kms-keyring-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/kms-keyring-node/-/kms-keyring-node-4.0.0.tgz", + "integrity": "sha512-O3zjC4njVEUrgRUOpFlr4vkbGX1D2XBS9tBMJeBh5VR2Rr/j0ogiEMed6iG1VaFx3ulZ/9Ozq7VxlZxyNCx0fg==", + "dependencies": { + "@aws-crypto/kms-keyring": "^4.0.0", + "@aws-crypto/material-management-node": "^4.0.0", + "@aws-sdk/client-kms": "^3.362.0", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/material-management": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/material-management/-/material-management-4.0.0.tgz", + "integrity": "sha512-1hVZVxIZBc47h599h6jiBkNJnPvckvk1CSDZ9Bi/aCsqVYDFza9frki7+dOsMJu5zYB0cL/H3u1MtuUZEDlsXw==", + "dependencies": { + "asn1.js": "^5.3.0", + "bn.js": "^5.1.1", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/material-management-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/material-management-node/-/material-management-node-4.0.0.tgz", + "integrity": "sha512-urGhjEibLj3atMeUl8RjqmADN8cvTFFhQixvjvoQItU90t4LTPCaHBm+f52QHNhAmGEzBcKFcNBeItNTsed/Cg==", + "dependencies": { + "@aws-crypto/hkdf-node": "^4.0.0", + "@aws-crypto/material-management": "^4.0.0", + "@aws-crypto/serialize": "^4.0.0", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/raw-aes-keyring-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/raw-aes-keyring-node/-/raw-aes-keyring-node-4.0.0.tgz", + "integrity": "sha512-ioXTDkEkVldm8Hmq8o1oWWdAlNz9OHiz7lMaWcAtDBJ+FDuf4pwmgX4sZyYyfs2JHhNDy9gq+L4xPp/oVIoNBw==", + "dependencies": { + "@aws-crypto/material-management-node": "^4.0.0", + "@aws-crypto/raw-keyring": "^4.0.0", + "@aws-crypto/serialize": "^4.0.0", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/raw-keyring": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/raw-keyring/-/raw-keyring-4.0.0.tgz", + "integrity": "sha512-Iw+WxKWM4YWAfL5xAB8wNXoCIRJr3ohH1OaGUNP5bKTR2IxDB9ALsRxdI9f61DIwWFsHAgsjIH2qecbW4RDC3Q==", + "dependencies": { + "@aws-crypto/material-management": "^4.0.0", + "@aws-crypto/serialize": "^4.0.0", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/raw-rsa-keyring-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/raw-rsa-keyring-node/-/raw-rsa-keyring-node-4.0.0.tgz", + "integrity": "sha512-o1wCF8gRStr3tIMYeu46u+gYPexvNQ+JDaLzGqe9nH0dRXADDG9w5NSdx0kVmFAMvLUgJJyULcwKU2e7o4Ucpg==", + "dependencies": { + "@aws-crypto/material-management-node": "^4.0.0", + "@aws-crypto/raw-keyring": "^4.0.0", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/serialize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/serialize/-/serialize-4.0.0.tgz", + "integrity": "sha512-bi3h2KA+vktnWDG2q/J7Pjgg0MsSgsytH4ZfDztj9KgKRIp9Jq0z8KcIpNK47osNG4MxOjjgqXCZsxp1bnIwjQ==", + "dependencies": { + "@aws-crypto/material-management": "^4.0.0", + "asn1.js": "^5.3.0", + "bn.js": "^5.1.1", + "tslib": "^2.2.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-sdk/client-kms": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-kms/-/client-kms-3.523.0.tgz", + "integrity": "sha512-g0EINy3KA82GgpiGdqKczukjaPqBUiX5KcUhg+6tUqtpFaDt1yvTyDqLevOIEx3dsTKXUAjNi9UswGb+RjpKmA==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.523.0", + "@aws-sdk/core": "3.523.0", + "@aws-sdk/credential-provider-node": "3.523.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.523.0", + "@aws-sdk/region-config-resolver": "3.523.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.523.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.523.0", + "@smithy/config-resolver": "^2.1.3", + "@smithy/core": "^1.3.4", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.3", + "@smithy/middleware-retry": "^2.1.3", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.3", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.1", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.3", + "@smithy/util-defaults-mode-node": "^2.2.2", + "@smithy/util-endpoints": "^1.1.3", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.523.0.tgz", + "integrity": "sha512-vob/Tk9bIr6VIyzScBWsKpP92ACI6/aOXBL2BITgvRWl5Umqi1jXFtfssj/N2UJHM4CBMRwxIJ33InfN0gPxZw==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.523.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.523.0", + "@aws-sdk/region-config-resolver": "3.523.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.523.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.523.0", + "@smithy/config-resolver": "^2.1.3", + "@smithy/core": "^1.3.4", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.3", + "@smithy/middleware-retry": "^2.1.3", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.3", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.1", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.3", + "@smithy/util-defaults-mode-node": "^2.2.2", + "@smithy/util-endpoints": "^1.1.3", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.523.0.tgz", + "integrity": "sha512-OktkdiuJ5DtYgNrJlo53Tf7pJ+UWfOt7V7or0ije6MysLP18GwlTkbg2UE4EUtfOxt/baXxHMlExB1vmRtlATw==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.523.0", + "@aws-sdk/core": "3.523.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.523.0", + "@aws-sdk/region-config-resolver": "3.523.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.523.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.523.0", + "@smithy/config-resolver": "^2.1.3", + "@smithy/core": "^1.3.4", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.3", + "@smithy/middleware-retry": "^2.1.3", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.3", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.1", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.3", + "@smithy/util-defaults-mode-node": "^2.2.2", + "@smithy/util-endpoints": "^1.1.3", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.523.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.523.0.tgz", + "integrity": "sha512-ggAkL8szaJkqD8oOsS68URJ9XMDbLA/INO/NPZJqv9BhmftecJvfy43uUVWGNs6n4YXNzfF0Y+zQ3DT0fZkv9g==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.523.0", + "@aws-sdk/middleware-host-header": "3.523.0", + "@aws-sdk/middleware-logger": "3.523.0", + "@aws-sdk/middleware-recursion-detection": "3.523.0", + "@aws-sdk/middleware-user-agent": "3.523.0", + "@aws-sdk/region-config-resolver": "3.523.0", + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.523.0", + "@aws-sdk/util-user-agent-browser": "3.523.0", + "@aws-sdk/util-user-agent-node": "3.523.0", + "@smithy/config-resolver": "^2.1.3", + "@smithy/core": "^1.3.4", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/hash-node": "^2.1.3", + "@smithy/invalid-dependency": "^2.1.3", + "@smithy/middleware-content-length": "^2.1.3", + "@smithy/middleware-endpoint": "^2.4.3", + "@smithy/middleware-retry": "^2.1.3", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/node-config-provider": "^2.2.3", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.1", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.3", + "@smithy/util-defaults-mode-node": "^2.2.2", + "@smithy/util-endpoints": "^1.1.3", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "@smithy/util-utf8": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.523.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.523.0.tgz", + "integrity": "sha512-JHa3ngEWkTzZ2YTn6EavcADC8gv6zZU4U9WBAleClh6ioXH0kGMBawZje3y0F0mKyLTfLhFqFUlCV5sngI/Qcw==", + "dependencies": { + "@smithy/core": "^1.3.4", + "@smithy/protocol-http": "^3.2.1", + "@smithy/signature-v4": "^2.1.3", + "@smithy/smithy-client": "^2.4.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.523.0.tgz", + "integrity": "sha512-Y6DWdH6/OuMDoNKVzZlNeBc6f1Yjk1lYMjANKpIhMbkRCvLJw/PYZKOZa8WpXbTYdgg9XLjKybnLIb3ww3uuzA==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.523.0.tgz", + "integrity": "sha512-6YUtePbn3UFpY9qfVwHFWIVnFvVS5vsbGxxkTO02swvZBvVG4sdG0Xj0AbotUNQNY9QTCN7WkhwIrd50rfDQ9Q==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/property-provider": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.1", + "@smithy/types": "^2.10.1", + "@smithy/util-stream": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.523.0.tgz", + "integrity": "sha512-dRch5Ts67FFRZY5r9DpiC3PM6BVHv1tRcy1b26hoqfFkxP9xYH3dsTSPBog1azIqaJa2GcXqEvKCqhghFTt4Xg==", + "dependencies": { + "@aws-sdk/client-sts": "3.523.0", + "@aws-sdk/credential-provider-env": "3.523.0", + "@aws-sdk/credential-provider-process": "3.523.0", + "@aws-sdk/credential-provider-sso": "3.523.0", + "@aws-sdk/credential-provider-web-identity": "3.523.0", + "@aws-sdk/types": "3.523.0", + "@smithy/credential-provider-imds": "^2.2.3", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.523.0.tgz", + "integrity": "sha512-0aW5ylA8pZmvv/8qA/+iel4acEyzSlHRiaHYL3L0qu9SSoe2a92+RHjrxKl6+Sb55eA2mRfQjaN8oOa5xiYyKA==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.523.0", + "@aws-sdk/credential-provider-http": "3.523.0", + "@aws-sdk/credential-provider-ini": "3.523.0", + "@aws-sdk/credential-provider-process": "3.523.0", + "@aws-sdk/credential-provider-sso": "3.523.0", + "@aws-sdk/credential-provider-web-identity": "3.523.0", + "@aws-sdk/types": "3.523.0", + "@smithy/credential-provider-imds": "^2.2.3", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.523.0.tgz", + "integrity": "sha512-f0LP9KlFmMvPWdKeUKYlZ6FkQAECUeZMmISsv6NKtvPCI9e4O4cLTeR09telwDK8P0HrgcRuZfXM7E30m8re0Q==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.523.0.tgz", + "integrity": "sha512-/VfOJuI8ImV//W4gr+yieF/4shzWAzWYeaaNu7hv161C5YW7/OoCygwRVHSnF4KKeUGQZomZWwml5zHZ57f8xQ==", + "dependencies": { + "@aws-sdk/client-sso": "3.523.0", + "@aws-sdk/token-providers": "3.523.0", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.523.0.tgz", + "integrity": "sha512-EyBwVoTNZrhLRIHly3JnLzy86deT2hHGoxSCrT3+cVcF1Pq3FPp6n9fUkHd6Yel+wFrjpXCRggLddPvajUoXtQ==", + "dependencies": { + "@aws-sdk/client-sts": "3.523.0", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.523.0.tgz", + "integrity": "sha512-4g3q7Ta9sdD9TMUuohBAkbx/e3I/juTqfKi7TPgP+8jxcYX72MOsgemAMHuP6CX27eyj4dpvjH+w4SIVDiDSmg==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.523.0.tgz", + "integrity": "sha512-PeDNJNhfiaZx54LBaLTXzUaJ9LXFwDFFIksipjqjvxMafnoVcQwKbkoPUWLe5ytT4nnL1LogD3s55mERFUsnwg==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.523.0.tgz", + "integrity": "sha512-nZ3Vt7ehfSDYnrcg/aAfjjvpdE+61B3Zk68i6/hSUIegT3IH9H1vSW67NDKVp+50hcEfzWwM2HMPXxlzuyFyrw==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.523.0.tgz", + "integrity": "sha512-5OoKkmAPNaxLgJuS65gByW1QknGvvXdqzrIMXLsm9LjbsphTOscyvT439qk3Jf08TL4Zlw2x+pZMG7dZYuMAhQ==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@aws-sdk/util-endpoints": "3.523.0", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.523.0.tgz", + "integrity": "sha512-IypIAecBc8b4jM0uVBEj90NYaIsc0vuLdSFyH4LPO7is4rQUet4CkkD+S036NvDdcdxBsQ4hJZBmWrqiizMHhQ==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/node-config-provider": "^2.2.3", + "@smithy/types": "^2.10.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.523.0.tgz", + "integrity": "sha512-m3sPEnLuGV3JY9A8ytcz90SogVtjxEyIxUDFeswxY4C5wP/36yOq3ivenRu07dH+QIJnBhsQdjnHwJfrIetG6g==", + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.523.0", + "@aws-sdk/types": "3.523.0", + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.523.0.tgz", + "integrity": "sha512-AqGIu4u+SxPiUuNBp2acCVcq80KDUFjxe6e3cMTvKWTzCbrVk1AXv0dAaJnCmdkWIha6zJDWxpIk/aL4EGhZ9A==", + "dependencies": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.523.0.tgz", + "integrity": "sha512-f4qe4AdafjAZoVGoVt69Jb2rXCgo306OOobSJ/f4bhQ0zgAjGELKJATNRRe0J7P28+ffmSxeuYwM3r4gDkD/QA==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", + "@smithy/util-endpoints": "^1.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.495.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.495.0.tgz", + "integrity": "sha512-MfaPXT0kLX2tQaR90saBT9fWQq2DHqSSJRzW+MZWsmF+y5LGCOhO22ac/2o6TKSQm7h0HRc2GaADqYYYor62yg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.523.0.tgz", + "integrity": "sha512-6ZRNdGHX6+HQFqTbIA5+i8RWzxFyxsZv8D3soRfpdyWIKkzhSz8IyRKXRciwKBJDaC7OX2jzGE90wxRQft27nA==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/types": "^2.10.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.523.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.523.0.tgz", + "integrity": "sha512-tW7vliJ77EsE8J1bzFpDYCiUyrw2NTcem+J5ddiWD4HA/xNQUyX0CMOXMBZCBA31xLTIchyz0LkZHlDsmB9LUw==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/node-config-provider": "^2.2.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "dependencies": { + "tslib": "^2.3.1" + } + }, "node_modules/@babel/code-frame": { "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz", @@ -13663,155 +14432,534 @@ "node": ">=14" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + "node_modules/@smithy/abort-controller": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.1.3.tgz", + "integrity": "sha512-c2aYH2Wu1RVE3rLlVgg2kQOBJGM0WbjReQi5DnPTm2Zb7F0gk7J2aeQeaX2u/lQZoHl6gv8Oac7mt9alU3+f4A==", + "dependencies": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@stablelib/aead": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/aead/-/aead-1.0.1.tgz", - "integrity": "sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==" + "node_modules/@smithy/config-resolver": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.1.4.tgz", + "integrity": "sha512-AW2WUZmBAzgO3V3ovKtsUbI3aBNMeQKFDumoqkNxaVDWF/xfnxAWqBKDr/NuG7c06N2Rm4xeZLPiJH/d+na0HA==", + "dependencies": { + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@stablelib/binary": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", - "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", + "node_modules/@smithy/core": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.5.tgz", + "integrity": "sha512-Rrc+e2Jj6Gu7Xbn0jvrzZlSiP2CZocIOfZ9aNUA82+1sa6GBnxqL9+iZ9EKHeD9aqD1nU8EK4+oN2EiFpSv7Yw==", "dependencies": { - "@stablelib/int": "^1.0.1" + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-retry": "^2.1.4", + "@smithy/middleware-serde": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@stablelib/bytes": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz", - "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==" + "node_modules/@smithy/credential-provider-imds": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.2.4.tgz", + "integrity": "sha512-DdatjmBZQnhGe1FhI8gO98f7NmvQFSDiZTwC3WMvLTCKQUY+Y1SVkhJqIuLu50Eb7pTheoXQmK+hKYUgpUWsNA==", + "dependencies": { + "@smithy/node-config-provider": "^2.2.4", + "@smithy/property-provider": "^2.1.3", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@stablelib/chacha": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/chacha/-/chacha-1.0.1.tgz", - "integrity": "sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg==", + "node_modules/@smithy/eventstream-codec": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.1.3.tgz", + "integrity": "sha512-rGlCVuwSDv6qfKH4/lRxFjcZQnIE0LZ3D4lkMHg7ZSltK9rA74r0VuGSvWVQ4N/d70VZPaniFhp4Z14QYZsa+A==", "dependencies": { - "@stablelib/binary": "^1.0.1", - "@stablelib/wipe": "^1.0.1" + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.10.1", + "@smithy/util-hex-encoding": "^2.1.1", + "tslib": "^2.5.0" } }, - "node_modules/@stablelib/chacha20poly1305": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/chacha20poly1305/-/chacha20poly1305-1.0.1.tgz", - "integrity": "sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA==", + "node_modules/@smithy/fetch-http-handler": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.4.3.tgz", + "integrity": "sha512-Fn/KYJFo6L5I4YPG8WQb2hOmExgRmNpVH5IK2zU3JKrY5FKW7y9ar5e0BexiIC9DhSKqKX+HeWq/Y18fq7Dkpw==", "dependencies": { - "@stablelib/aead": "^1.0.1", - "@stablelib/binary": "^1.0.1", - "@stablelib/chacha": "^1.0.1", - "@stablelib/constant-time": "^1.0.1", - "@stablelib/poly1305": "^1.0.1", - "@stablelib/wipe": "^1.0.1" + "@smithy/protocol-http": "^3.2.1", + "@smithy/querystring-builder": "^2.1.3", + "@smithy/types": "^2.10.1", + "@smithy/util-base64": "^2.1.1", + "tslib": "^2.5.0" } }, - "node_modules/@stablelib/constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/constant-time/-/constant-time-1.0.1.tgz", - "integrity": "sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==" + "node_modules/@smithy/hash-node": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.1.3.tgz", + "integrity": "sha512-FsAPCUj7VNJIdHbSxMd5uiZiF20G2zdSDgrgrDrHqIs/VMxK85Vqk5kMVNNDMCZmMezp6UKnac0B4nAyx7HJ9g==", + "dependencies": { + "@smithy/types": "^2.10.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@stablelib/ed25519": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz", - "integrity": "sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==", + "node_modules/@smithy/invalid-dependency": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.1.3.tgz", + "integrity": "sha512-wkra7d/G4CbngV4xsjYyAYOvdAhahQje/WymuQdVEnXFExJopEu7fbL5AEAlBPgWHXwu94VnCSG00gVzRfExyg==", "dependencies": { - "@stablelib/random": "^1.0.2", - "@stablelib/sha512": "^1.0.1", - "@stablelib/wipe": "^1.0.1" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" } }, - "node_modules/@stablelib/hash": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", - "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==" + "node_modules/@smithy/is-array-buffer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.1.1.tgz", + "integrity": "sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@stablelib/hkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/hkdf/-/hkdf-1.0.1.tgz", - "integrity": "sha512-SBEHYE16ZXlHuaW5RcGk533YlBj4grMeg5TooN80W3NpcHRtLZLLXvKyX0qcRFxf+BGDobJLnwkvgEwHIDBR6g==", + "node_modules/@smithy/middleware-content-length": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.1.3.tgz", + "integrity": "sha512-aJduhkC+dcXxdnv5ZpM3uMmtGmVFKx412R1gbeykS5HXDmRU6oSsyy2SoHENCkfOGKAQOjVE2WVqDJibC0d21g==", "dependencies": { - "@stablelib/hash": "^1.0.1", - "@stablelib/hmac": "^1.0.1", - "@stablelib/wipe": "^1.0.1" + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@stablelib/hmac": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/hmac/-/hmac-1.0.1.tgz", - "integrity": "sha512-V2APD9NSnhVpV/QMYgCVMIYKiYG6LSqw1S65wxVoirhU/51ACio6D4yDVSwMzuTJXWZoVHbDdINioBwKy5kVmA==", + "node_modules/@smithy/middleware-endpoint": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.4.4.tgz", + "integrity": "sha512-4yjHyHK2Jul4JUDBo2sTsWY9UshYUnXeb/TAK/MTaPEb8XQvDmpwSFnfIRDU45RY1a6iC9LCnmJNg/yHyfxqkw==", "dependencies": { - "@stablelib/constant-time": "^1.0.1", - "@stablelib/hash": "^1.0.1", - "@stablelib/wipe": "^1.0.1" + "@smithy/middleware-serde": "^2.1.3", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/shared-ini-file-loader": "^2.3.4", + "@smithy/types": "^2.10.1", + "@smithy/url-parser": "^2.1.3", + "@smithy/util-middleware": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@stablelib/int": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", - "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==" + "node_modules/@smithy/middleware-retry": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.1.4.tgz", + "integrity": "sha512-Cyolv9YckZTPli1EkkaS39UklonxMd08VskiuMhURDjC0HHa/AD6aK/YoD21CHv9s0QLg0WMLvk9YeLTKkXaFQ==", + "dependencies": { + "@smithy/node-config-provider": "^2.2.4", + "@smithy/protocol-http": "^3.2.1", + "@smithy/service-error-classification": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-retry": "^2.1.3", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@stablelib/keyagreement": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz", - "integrity": "sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==", + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.1.3.tgz", + "integrity": "sha512-s76LId+TwASrHhUa9QS4k/zeXDUAuNuddKklQzRgumbzge5BftVXHXIqL4wQxKGLocPwfgAOXWx+HdWhQk9hTg==", "dependencies": { - "@stablelib/bytes": "^1.0.1" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@stablelib/poly1305": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/poly1305/-/poly1305-1.0.1.tgz", - "integrity": "sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==", + "node_modules/@smithy/middleware-stack": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.1.3.tgz", + "integrity": "sha512-opMFufVQgvBSld/b7mD7OOEBxF6STyraVr1xel1j0abVILM8ALJvRoFbqSWHGmaDlRGIiV9Q5cGbWi0sdiEaLQ==", "dependencies": { - "@stablelib/constant-time": "^1.0.1", - "@stablelib/wipe": "^1.0.1" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@stablelib/random": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", - "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "node_modules/@smithy/node-config-provider": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.2.4.tgz", + "integrity": "sha512-nqazHCp8r4KHSFhRQ+T0VEkeqvA0U+RhehBSr1gunUuNW3X7j0uDrWBxB2gE9eutzy6kE3Y7L+Dov/UXT871vg==", "dependencies": { - "@stablelib/binary": "^1.0.1", - "@stablelib/wipe": "^1.0.1" + "@smithy/property-provider": "^2.1.3", + "@smithy/shared-ini-file-loader": "^2.3.4", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@stablelib/sha256": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/sha256/-/sha256-1.0.1.tgz", - "integrity": "sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ==", + "node_modules/@smithy/node-http-handler": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.4.1.tgz", + "integrity": "sha512-HCkb94soYhJMxPCa61wGKgmeKpJ3Gftx1XD6bcWEB2wMV1L9/SkQu/6/ysKBnbOzWRE01FGzwrTxucHypZ8rdg==", "dependencies": { - "@stablelib/binary": "^1.0.1", - "@stablelib/hash": "^1.0.1", - "@stablelib/wipe": "^1.0.1" + "@smithy/abort-controller": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/querystring-builder": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@stablelib/sha512": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz", - "integrity": "sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==", + "node_modules/@smithy/property-provider": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.1.3.tgz", + "integrity": "sha512-bMz3se+ySKWNrgm7eIiQMa2HO/0fl2D0HvLAdg9pTMcpgp4SqOAh6bz7Ik6y7uQqSrk4rLjIKgbQ6yzYgGehCQ==", "dependencies": { - "@stablelib/binary": "^1.0.1", - "@stablelib/hash": "^1.0.1", - "@stablelib/wipe": "^1.0.1" + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@stablelib/wipe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", - "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==" + "node_modules/@smithy/protocol-http": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.2.1.tgz", + "integrity": "sha512-KLrQkEw4yJCeAmAH7hctE8g9KwA7+H2nSJwxgwIxchbp/L0B5exTdOQi9D5HinPLlothoervGmhpYKelZ6AxIA==", + "dependencies": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@stablelib/x25519": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz", - "integrity": "sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==", + "node_modules/@smithy/querystring-builder": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.1.3.tgz", + "integrity": "sha512-kFD3PnNqKELe6m9GRHQw/ftFFSZpnSeQD4qvgDB6BQN6hREHELSosVFUMPN4M3MDKN2jAwk35vXHLoDrNfKu0A==", "dependencies": { - "@stablelib/keyagreement": "^1.0.1", - "@stablelib/random": "^1.0.2", - "@stablelib/wipe": "^1.0.1" + "@smithy/types": "^2.10.1", + "@smithy/util-uri-escape": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.1.3.tgz", + "integrity": "sha512-3+CWJoAqcBMR+yvz6D+Fc5VdoGFtfenW6wqSWATWajrRMGVwJGPT3Vy2eb2bnMktJc4HU4bpjeovFa566P3knQ==", + "dependencies": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.3.tgz", + "integrity": "sha512-iUrpSsem97bbXHHT/v3s7vaq8IIeMo6P6cXdeYHrx0wOJpMeBGQF7CB0mbJSiTm3//iq3L55JiEm8rA7CTVI8A==", + "dependencies": { + "@smithy/types": "^2.10.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.3.4.tgz", + "integrity": "sha512-CiZmPg9GeDKbKmJGEFvJBsJcFnh0AQRzOtQAzj1XEa8N/0/uSN/v1LYzgO7ry8hhO8+9KB7+DhSW0weqBra4Aw==", + "dependencies": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.1.3.tgz", + "integrity": "sha512-Jq4iPPdCmJojZTsPePn4r1ULShh6ONkokLuxp1Lnk4Sq7r7rJp4HlA1LbPBq4bD64TIzQezIpr1X+eh5NYkNxw==", + "dependencies": { + "@smithy/eventstream-codec": "^2.1.3", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/types": "^2.10.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-middleware": "^2.1.3", + "@smithy/util-uri-escape": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.4.2.tgz", + "integrity": "sha512-ntAFYN51zu3N3mCd95YFcFi/8rmvm//uX+HnK24CRbI6k5Rjackn0JhgKz5zOx/tbNvOpgQIwhSX+1EvEsBLbA==", + "dependencies": { + "@smithy/middleware-endpoint": "^2.4.4", + "@smithy/middleware-stack": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/types": "^2.10.1", + "@smithy/util-stream": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.10.1.tgz", + "integrity": "sha512-hjQO+4ru4cQ58FluQvKKiyMsFg0A6iRpGm2kqdH8fniyNd2WyanoOsYJfMX/IFLuLxEoW6gnRkNZy1y6fUUhtA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.1.3.tgz", + "integrity": "sha512-X1NRA4WzK/ihgyzTpeGvI9Wn45y8HmqF4AZ/FazwAv8V203Ex+4lXqcYI70naX9ETqbqKVzFk88W6WJJzCggTQ==", + "dependencies": { + "@smithy/querystring-parser": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.1.1.tgz", + "integrity": "sha512-UfHVpY7qfF/MrgndI5PexSKVTxSZIdz9InghTFa49QOvuu9I52zLPLUHXvHpNuMb1iD2vmc6R+zbv/bdMipR/g==", + "dependencies": { + "@smithy/util-buffer-from": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.1.1.tgz", + "integrity": "sha512-ekOGBLvs1VS2d1zM2ER4JEeBWAvIOUKeaFch29UjjJsxmZ/f0L3K3x0dEETgh3Q9bkZNHgT+rkdl/J/VUqSRag==", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.2.1.tgz", + "integrity": "sha512-/ggJG+ta3IDtpNVq4ktmEUtOkH1LW64RHB5B0hcr5ZaWBmo96UX2cIOVbjCqqDickTXqBWZ4ZO0APuaPrD7Abg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.1.1.tgz", + "integrity": "sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg==", + "dependencies": { + "@smithy/is-array-buffer": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.2.1.tgz", + "integrity": "sha512-50VL/tx9oYYcjJn/qKqNy7sCtpD0+s8XEBamIFo4mFFTclKMNp+rsnymD796uybjiIquB7VCB/DeafduL0y2kw==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.1.4.tgz", + "integrity": "sha512-J6XAVY+/g7jf03QMnvqPyU+8jqGrrtXoKWFVOS+n1sz0Lg8HjHJ1ANqaDN+KTTKZRZlvG8nU5ZrJOUL6VdwgcQ==", + "dependencies": { + "@smithy/property-provider": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.2.3.tgz", + "integrity": "sha512-ttUISrv1uVOjTlDa3nznX33f0pthoUlP+4grhTvOzcLhzArx8qHB94/untGACOG3nlf8vU20nI2iWImfzoLkYA==", + "dependencies": { + "@smithy/config-resolver": "^2.1.4", + "@smithy/credential-provider-imds": "^2.2.4", + "@smithy/node-config-provider": "^2.2.4", + "@smithy/property-provider": "^2.1.3", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.1.4.tgz", + "integrity": "sha512-/qAeHmK5l4yQ4/bCIJ9p49wDe9rwWtOzhPHblu386fwPNT3pxmodgcs9jDCV52yK9b4rB8o9Sj31P/7Vzka1cg==", + "dependencies": { + "@smithy/node-config-provider": "^2.2.4", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.1.1.tgz", + "integrity": "sha512-3UNdP2pkYUUBGEXzQI9ODTDK+Tcu1BlCyDBaRHwyxhA+8xLP8agEKQq4MGmpjqb4VQAjq9TwlCQX0kP6XDKYLg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.1.3.tgz", + "integrity": "sha512-/+2fm7AZ2ozl5h8wM++ZP0ovE9/tiUUAHIbCfGfb3Zd3+Dyk17WODPKXBeJ/TnK5U+x743QmA0xHzlSm8I/qhw==", + "dependencies": { + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.1.3.tgz", + "integrity": "sha512-Kbvd+GEMuozbNUU3B89mb99tbufwREcyx2BOX0X2+qHjq6Gvsah8xSDDgxISDwcOHoDqUWO425F0Uc/QIRhYkg==", + "dependencies": { + "@smithy/service-error-classification": "^2.1.3", + "@smithy/types": "^2.10.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.1.3.tgz", + "integrity": "sha512-HvpEQbP8raTy9n86ZfXiAkf3ezp1c3qeeO//zGqwZdrfaoOpGKQgF2Sv1IqZp7wjhna7pvczWaGUHjcOPuQwKw==", + "dependencies": { + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/types": "^2.10.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.1.1.tgz", + "integrity": "sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.1.1.tgz", + "integrity": "sha512-BqTpzYEcUMDwAKr7/mVRUtHDhs6ZoXDi9NypMvMfOr/+u1NW7JgqodPDECiiLboEm6bobcPcECxzjtQh865e9A==", + "dependencies": { + "@smithy/util-buffer-from": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { @@ -14743,10 +15891,13 @@ "@types/ms": "*" } }, - "node_modules/@types/dom-screen-wake-lock": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/dom-screen-wake-lock/-/dom-screen-wake-lock-1.0.3.tgz", - "integrity": "sha512-3Iten7X3Zgwvk6kh6/NRdwN7WbZ760YgFCsF5AxDifltUQzW1RaW+WRmcVtgwFzLjaNu64H+0MPJ13yRa8g3Dw==" + "node_modules/@types/duplexify": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/@types/duplexify/-/duplexify-3.6.4.tgz", + "integrity": "sha512-2eahVPsd+dy3CL6FugAzJcxoraWhUghZGEQJns1kTKfCXWKJ5iG/VkaB05wRVrDKHfOFKqb0X0kXh91eE99RZg==", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/eslint": { "version": "8.44.7", @@ -14940,6 +16091,11 @@ "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", "dev": true }, + "node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==" + }, "node_modules/@types/mdast": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", @@ -17017,20 +18173,25 @@ "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true }, - "node_modules/ast-types": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz", - "integrity": "sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==", - "peer": true, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" } }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -22907,20 +24068,19 @@ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" }, "node_modules/fast-xml-parser": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.5.tgz", - "integrity": "sha512-sWvP1Pl8H03B8oFJpFR3HE31HUfwtX7Rlf9BNsvdpujD4n7WMhfmu8h9wOV2u+c1k0ZilTADhPqypzx2J690ZQ==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - }, { "type": "paypal", "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "peer": true, "dependencies": { "strnum": "^1.0.5" }, @@ -36331,8 +37491,7 @@ "node_modules/strnum": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "peer": true + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, "node_modules/strong-log-transformer": { "version": "2.1.0", diff --git a/package.json b/package.json index 8499606f1..0e1e5a4fb 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "zod-fixture": "^2.5.1" }, "dependencies": { + "@aws-crypto/client-node": "4.0.0", "@bull-board/api": "^5.13.0", "@bull-board/express": "^5.13.0", "@bull-board/nestjs": "^5.11.0", diff --git a/packages/policy-engine-shared/src/index.ts b/packages/policy-engine-shared/src/index.ts index ca5017b1c..7ed926e47 100644 --- a/packages/policy-engine-shared/src/index.ts +++ b/packages/policy-engine-shared/src/index.ts @@ -8,6 +8,7 @@ export * from './lib/dto' export * from './lib/schema/address.schema' export * from './lib/schema/hex.schema' export * from './lib/type/action.type' +export * from './lib/type/data-store.type' export * from './lib/type/domain.type' export * from './lib/type/entity.type' export * from './lib/util/caip.util' diff --git a/packages/policy-engine-shared/src/lib/schema/data-store.schema.ts b/packages/policy-engine-shared/src/lib/schema/data-store.schema.ts new file mode 100644 index 000000000..76e96e62e --- /dev/null +++ b/packages/policy-engine-shared/src/lib/schema/data-store.schema.ts @@ -0,0 +1,37 @@ +import { z } from 'zod' +import { entitiesSchema } from './entity.schema' + +export const jsonWebKeySetSchema = z.object({ + kty: z.string().describe('Key Type (e.g. RSA or EC'), + use: z.string(), + kid: z.string().describe('Arbitrary key ID'), + alg: z.string().describe('Algorithm'), + n: z.string().describe('Key modulus'), + e: z.string().describe('Key exponent') +}) + +export const dataStoreProtocolSchema = z.enum(['file']) + +export const dataStoreConfigurationSchema = z.object({ + dataUrl: z.string(), + signatureUrl: z.string(), + keys: z.array(jsonWebKeySetSchema) +}) + +export const entityDataSchema = z.object({ + entity: z.object({ + data: entitiesSchema + }) +}) + +export const entitySignatureSchema = z.object({ + entity: z.object({ + signature: z.string() + }) +}) + +export const entityJsonWebKeySetSchema = z.object({ + entity: z.object({ + keys: z.array(jsonWebKeySetSchema) + }) +}) diff --git a/packages/policy-engine-shared/src/lib/schema/entity.schema.ts b/packages/policy-engine-shared/src/lib/schema/entity.schema.ts new file mode 100644 index 000000000..af83f0293 --- /dev/null +++ b/packages/policy-engine-shared/src/lib/schema/entity.schema.ts @@ -0,0 +1,83 @@ +import { Alg } from '@narval/signature' +import { z } from 'zod' +import { addressSchema } from './address.schema' + +export const userRoleSchema = z.enum(['ROOT', 'ADMIN', 'MEMBER', 'MANAGER']) + +export const accountTypeSchema = z.enum(['EOA', '4337']) + +export const accountClassificationSchema = z.enum(['EXTERNAL', 'COUNTERPARTY', 'INTERNAL', 'WALLET']) + +export const credentialEntitySchema = z.object({ + id: z.string(), + pubKey: z.string(), + alg: z.nativeEnum(Alg), + userId: z.string() +}) + +export const organizationEntitySchema = z.object({ + id: z.string() +}) + +export const userEntitySchema = z.object({ + id: z.string(), + role: userRoleSchema +}) + +export const userGroupEntitySchema = z.object({ + id: z.string() +}) + +export const userWalletEntitySchema = z.object({ + userId: z.string(), + walletId: z.string() +}) + +export const userGroupMemberEntitySchema = z.object({ + userId: z.string(), + groupId: z.string() +}) + +export const walletEntitySchema = z.object({ + id: z.string(), + address: addressSchema, + accountType: accountTypeSchema, + chainId: z.number().optional() +}) + +export const walletGroupEntitySchema = z.object({ + id: z.string() +}) + +export const walletGroupMemberEntitySchema = z.object({ + walletId: z.string(), + groupId: z.string() +}) + +export const addressBookAccountEntitySchema = z.object({ + id: z.string(), + address: addressSchema, + chainId: z.number(), + classification: accountClassificationSchema +}) + +export const tokenEntitySchema = z.object({ + id: z.string(), + address: addressSchema, + symbol: z.string(), + chainId: z.number(), + decimals: z.number() +}) + +export const entitiesSchema = z.object({ + addressBook: z.array(addressBookAccountEntitySchema), + credentials: z.array(credentialEntitySchema), + tokens: z.array(tokenEntitySchema), + userGroupMembers: z.array(userGroupMemberEntitySchema), + userGroups: z.array(userGroupEntitySchema), + userWallets: z.array(userWalletEntitySchema), + users: z.array(userEntitySchema), + walletGroupMembers: z.array(walletGroupMemberEntitySchema), + walletGroups: z.array(walletGroupEntitySchema), + wallets: z.array(walletEntitySchema) +}) diff --git a/packages/policy-engine-shared/src/lib/type/data-store.type.ts b/packages/policy-engine-shared/src/lib/type/data-store.type.ts new file mode 100644 index 000000000..0b97a6d9e --- /dev/null +++ b/packages/policy-engine-shared/src/lib/type/data-store.type.ts @@ -0,0 +1,22 @@ +import { z } from 'zod' +import { + dataStoreConfigurationSchema, + dataStoreProtocolSchema, + entityDataSchema, + entityJsonWebKeySetSchema, + entitySignatureSchema, + jsonWebKeySetSchema +} from '../schema/data-store.schema' + +export type JsonWebKeySet = z.infer + +export type DataStoreProtocol = z.infer +export const DataStoreProtocol = dataStoreProtocolSchema.Enum + +export type DataStoreConfiguration = z.infer + +export type EntityData = z.infer + +export type EntitySignature = z.infer + +export type EntityJsonWebKeySet = z.infer