From bcbdd9e1af1d87a97d596f73044f0a0dd1e598d8 Mon Sep 17 00:00:00 2001 From: Rhys Cox Date: Fri, 13 Mar 2026 09:18:33 +0000 Subject: [PATCH 01/17] CCM-15215 - Sign callback payloads --- .../terraform/components/callbacks/README.md | 1 + .../terraform/components/callbacks/locals.tf | 2 + .../module_transform_filter_lambda.tf | 14 + .../ssm_parameter_applications_map.tf | 11 + .../components/callbacks/variables.tf | 5 + .../cloudwatch_event_rule_main.tf | 6 + .../package.json | 3 + .../src/__tests__/index.component.test.ts | 50 +- .../src/__tests__/index.test.ts | 28 +- .../__tests__/services/payload-signer.test.ts | 50 + .../services/ssm-applications-map.test.ts | 101 ++ .../src/handler.ts | 77 +- .../src/index.ts | 31 + .../src/services/payload-signer.ts | 13 + .../src/services/ssm-applications-map.ts | 57 + package-lock.json | 998 +++++++++--------- 16 files changed, 920 insertions(+), 527 deletions(-) create mode 100644 infrastructure/terraform/components/callbacks/ssm_parameter_applications_map.tf create mode 100644 lambdas/client-transform-filter-lambda/src/__tests__/services/payload-signer.test.ts create mode 100644 lambdas/client-transform-filter-lambda/src/__tests__/services/ssm-applications-map.test.ts create mode 100644 lambdas/client-transform-filter-lambda/src/services/payload-signer.ts create mode 100644 lambdas/client-transform-filter-lambda/src/services/ssm-applications-map.ts diff --git a/infrastructure/terraform/components/callbacks/README.md b/infrastructure/terraform/components/callbacks/README.md index f9ef0bec..88cbd6d3 100644 --- a/infrastructure/terraform/components/callbacks/README.md +++ b/infrastructure/terraform/components/callbacks/README.md @@ -13,6 +13,7 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [applications\_map\_parameter\_name](#input\_applications\_map\_parameter\_name) | SSM Parameter Store path for the clientId-to-applicationId map used for payload signing | `string` | n/a | yes | | [aws\_account\_id](#input\_aws\_account\_id) | The AWS Account ID (numeric) | `string` | n/a | yes | | [clients](#input\_clients) | n/a |
list(object({
connection_name = string
destination_name = string
invocation_endpoint = string
invocation_rate_limit_per_second = optional(number, 10)
http_method = optional(string, "POST")
header_name = optional(string, "x-api-key")
header_value = string
client_detail = list(string)
}))
| `[]` | no | | [component](#input\_component) | The variable encapsulating the name of this component | `string` | `"callbacks"` | no | diff --git a/infrastructure/terraform/components/callbacks/locals.tf b/infrastructure/terraform/components/callbacks/locals.tf index 4ac741f1..b9f7d4d8 100644 --- a/infrastructure/terraform/components/callbacks/locals.tf +++ b/infrastructure/terraform/components/callbacks/locals.tf @@ -27,4 +27,6 @@ locals { } : {} all_clients = merge(local.clients_by_name, local.mock_client) + + applications_map_parameter_name = coalesce(var.applications_map_parameter_name, "/${var.project}/${var.environment}/${var.component}/applications-map") } diff --git a/infrastructure/terraform/components/callbacks/module_transform_filter_lambda.tf b/infrastructure/terraform/components/callbacks/module_transform_filter_lambda.tf index a9064bdc..22155559 100644 --- a/infrastructure/terraform/components/callbacks/module_transform_filter_lambda.tf +++ b/infrastructure/terraform/components/callbacks/module_transform_filter_lambda.tf @@ -42,6 +42,7 @@ module "client_transform_filter_lambda" { CLIENT_SUBSCRIPTION_CONFIG_PREFIX = "client_subscriptions/" CLIENT_SUBSCRIPTION_CACHE_TTL_SECONDS = "60" MESSAGE_ROOT_URI = var.message_root_uri + APPLICATIONS_MAP_PARAMETER = local.applications_map_parameter_name } } @@ -86,6 +87,19 @@ data "aws_iam_policy_document" "client_transform_filter_lambda" { ] } + statement { + sid = "SSMApplicationsMapRead" + effect = "Allow" + + actions = [ + "ssm:GetParameter", + ] + + resources = [ + "arn:aws:ssm:${var.region}:${var.aws_account_id}:parameter${local.applications_map_parameter_name}", + ] + } + statement { sid = "CloudWatchMetrics" effect = "Allow" diff --git a/infrastructure/terraform/components/callbacks/ssm_parameter_applications_map.tf b/infrastructure/terraform/components/callbacks/ssm_parameter_applications_map.tf new file mode 100644 index 00000000..f4aa4c67 --- /dev/null +++ b/infrastructure/terraform/components/callbacks/ssm_parameter_applications_map.tf @@ -0,0 +1,11 @@ +resource "aws_ssm_parameter" "applications_map" { + count = var.deploy_mock_webhook ? 1 : 0 + + name = local.applications_map_parameter_name + type = "SecureString" + key_id = module.kms.key_arn + + value = jsonencode({ + "mock-client" = "mock-application-id" + }) +} diff --git a/infrastructure/terraform/components/callbacks/variables.tf b/infrastructure/terraform/components/callbacks/variables.tf index 42ee7f13..87aa0a42 100644 --- a/infrastructure/terraform/components/callbacks/variables.tf +++ b/infrastructure/terraform/components/callbacks/variables.tf @@ -179,3 +179,8 @@ variable "enable_debug_log_bucket" { description = "Enable the debug log S3 bucket used for integration testing" default = false } + +variable "applications_map_parameter_name" { + type = string + description = "SSM Parameter Store path for the clientId-to-applicationId map used for payload signing" +} diff --git a/infrastructure/terraform/modules/client-destination/cloudwatch_event_rule_main.tf b/infrastructure/terraform/modules/client-destination/cloudwatch_event_rule_main.tf index 0b2bf042..4bce1003 100644 --- a/infrastructure/terraform/modules/client-destination/cloudwatch_event_rule_main.tf +++ b/infrastructure/terraform/modules/client-destination/cloudwatch_event_rule_main.tf @@ -22,6 +22,12 @@ resource "aws_cloudwatch_event_target" "main" { arn = module.target_dlq.sqs_queue_arn } + http_target { + header_parameters = { + "x-hmac-sha256-signature" = "$.detail.headers.x-hmac-sha256-signature" + } + } + retry_policy { maximum_retry_attempts = 3 maximum_event_age_in_seconds = 3600 diff --git a/lambdas/client-transform-filter-lambda/package.json b/lambdas/client-transform-filter-lambda/package.json index 82cd8dc5..4a5a2221 100644 --- a/lambdas/client-transform-filter-lambda/package.json +++ b/lambdas/client-transform-filter-lambda/package.json @@ -1,10 +1,12 @@ { "dependencies": { "@aws-sdk/client-s3": "^3.821.0", + "@aws-sdk/client-ssm": "^3.821.0", "@nhs-notify-client-callbacks/logger": "*", "@nhs-notify-client-callbacks/models": "*", "aws-embedded-metrics": "^4.2.1", "cloudevents": "^8.0.2", + "crypto-js": "^4.2.0", "esbuild": "^0.25.0", "p-map": "^4.0.0", "zod": "^4.1.13" @@ -12,6 +14,7 @@ "devDependencies": { "@tsconfig/node22": "^22.0.2", "@types/aws-lambda": "^8.10.148", + "@types/crypto-js": "^4.2.2", "@types/jest": "^29.5.14", "jest": "^29.7.0", "jest-mock-extended": "^3.0.7", diff --git a/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts b/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts index b8851d59..1e962e72 100644 --- a/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts +++ b/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts @@ -15,6 +15,17 @@ jest.mock("@aws-sdk/client-s3", () => { }; }); +const mockSsmSend = jest.fn(); +jest.mock("@aws-sdk/client-ssm", () => { + const actual = jest.requireActual("@aws-sdk/client-ssm"); + return { + ...actual, + SSMClient: jest.fn().mockImplementation(() => ({ + send: mockSsmSend, + })), + }; +}); + jest.mock("aws-embedded-metrics", () => ({ createMetricsLogger: jest.fn(() => ({ setNamespace: jest.fn(), @@ -29,10 +40,11 @@ jest.mock("aws-embedded-metrics", () => ({ })); import { GetObjectCommand, NoSuchKey } from "@aws-sdk/client-s3"; +import { GetParameterCommand } from "@aws-sdk/client-ssm"; import type { SQSRecord } from "aws-lambda"; import { EventTypes } from "@nhs-notify-client-callbacks/models"; import { createS3Client } from "services/config-loader-service"; -import { configLoaderService, handler } from ".."; +import { applicationsMapService, configLoaderService, handler } from ".."; const makeSqsRecord = (body: object): SQSRecord => ({ messageId: "sqs-id", @@ -106,10 +118,21 @@ describe("Lambda handler with S3 subscription filtering", () => { process.env.CLIENT_SUBSCRIPTION_CACHE_TTL_SECONDS = "60"; process.env.METRICS_NAMESPACE = "test-namespace"; process.env.ENVIRONMENT = "test"; + process.env.APPLICATIONS_MAP_PARAMETER = "/test/applications-map"; + }); + + const applicationsMap = JSON.stringify({ + "client-1": "app-id-1", + "client-a": "app-id-a", + "client-b": "app-id-b", + "client-no-config": "app-id-no-config", }); beforeEach(() => { mockSend.mockClear(); + mockSsmSend.mockClear(); + applicationsMapService.reset(); + mockSsmSend.mockResolvedValue({ Parameter: { Value: applicationsMap } }); // Reset loader and clear cache for clean state between tests configLoaderService.reset( createS3Client({ AWS_ENDPOINT_URL: "http://localhost:4566" }), @@ -123,6 +146,7 @@ describe("Lambda handler with S3 subscription filtering", () => { delete process.env.CLIENT_SUBSCRIPTION_CACHE_TTL_SECONDS; delete process.env.METRICS_NAMESPACE; delete process.env.ENVIRONMENT; + delete process.env.APPLICATIONS_MAP_PARAMETER; }); it("passes event through when client config matches subscription", async () => { @@ -141,6 +165,9 @@ describe("Lambda handler with S3 subscription filtering", () => { expect(result).toHaveLength(1); expect(mockSend).toHaveBeenCalledTimes(1); expect(mockSend.mock.calls[0][0]).toBeInstanceOf(GetObjectCommand); + expect(mockSsmSend).toHaveBeenCalledTimes(1); + expect(mockSsmSend.mock.calls[0][0]).toBeInstanceOf(GetParameterCommand); + expect(result[0].headers["x-hmac-sha256-signature"]).toMatch(/^[0-9a-f]+$/); }); it("filters out event when status is not in subscription", async () => { @@ -236,4 +263,25 @@ describe("Lambda handler with S3 subscription filtering", () => { // S3 fetched once per distinct client (client-a and client-b), not once per event expect(mockSend).toHaveBeenCalledTimes(2); }); + + it("filters out event when no applicationId found in SSM map", async () => { + mockSend.mockResolvedValue({ + Body: { + transformToString: jest + .fn() + .mockResolvedValue( + JSON.stringify(createValidConfig("client-unknown")), + ), + }, + }); + mockSsmSend.mockResolvedValue({ + Parameter: { Value: JSON.stringify({}) }, + }); + + const result = await handler([ + makeSqsRecord(validMessageStatusEvent("client-unknown", "DELIVERED")), + ]); + + expect(result).toHaveLength(0); + }); }); diff --git a/lambdas/client-transform-filter-lambda/src/__tests__/index.test.ts b/lambdas/client-transform-filter-lambda/src/__tests__/index.test.ts index fc3317b6..b8d02995 100644 --- a/lambdas/client-transform-filter-lambda/src/__tests__/index.test.ts +++ b/lambdas/client-transform-filter-lambda/src/__tests__/index.test.ts @@ -10,12 +10,22 @@ import type { import type { Logger } from "services/logger"; import type { CallbackMetrics } from "services/metrics"; import type { ConfigLoader } from "services/config-loader"; +import type { ApplicationsMapService } from "services/ssm-applications-map"; import { ObservabilityService } from "services/observability"; import { ConfigLoaderService } from "services/config-loader-service"; import { createHandler } from ".."; jest.mock("aws-embedded-metrics"); +const stubTarget = { + Type: "API", + TargetId: "00000000-0000-4000-8000-000000000001", + InvocationEndpoint: "https://example.com/webhook", + InvocationMethod: "POST", + InvocationRateLimit: 10, + APIKey: { HeaderName: "x-api-key", HeaderValue: "test-api-key" }, +}; + const createPassthroughConfigLoader = (): ConfigLoader => ({ loadClientConfig: jest.fn().mockImplementation(async (clientId: string) => [ @@ -23,7 +33,7 @@ const createPassthroughConfigLoader = (): ConfigLoader => SubscriptionType: "MessageStatus", SubscriptionId: "00000000-0000-0000-0000-000000000001", ClientId: clientId, - Targets: [], + Targets: [stubTarget], MessageStatuses: [ "DELIVERED", "FAILED", @@ -37,7 +47,7 @@ const createPassthroughConfigLoader = (): ConfigLoader => SubscriptionType: "ChannelStatus", SubscriptionId: "00000000-0000-0000-0000-000000000002", ClientId: clientId, - Targets: [], + Targets: [stubTarget], ChannelType: "NHSAPP", ChannelStatuses: ["DELIVERED", "FAILED", "TECHNICAL_FAILURE"], SupplierStatuses: [ @@ -50,7 +60,7 @@ const createPassthroughConfigLoader = (): ConfigLoader => SubscriptionType: "ChannelStatus", SubscriptionId: "00000000-0000-0000-0000-000000000003", ClientId: clientId, - Targets: [], + Targets: [stubTarget], ChannelType: "SMS", ChannelStatuses: ["DELIVERED", "FAILED", "TECHNICAL_FAILURE"], SupplierStatuses: [ @@ -67,6 +77,15 @@ const makeStubConfigLoaderService = (): ConfigLoaderService => { return { getLoader: () => loader } as unknown as ConfigLoaderService; }; +const makeStubApplicationsMapService = (): ApplicationsMapService => + ({ + getApplicationId: jest + .fn() + .mockImplementation( + async (clientId: string) => `test-app-id-${clientId}`, + ), + }) as unknown as ApplicationsMapService; + describe("Lambda handler", () => { const mockLogger = { info: jest.fn(), @@ -96,6 +115,7 @@ describe("Lambda handler", () => { createObservabilityService: () => new ObservabilityService(mockLogger, mockMetrics, mockMetricsLogger), createConfigLoaderService: makeStubConfigLoaderService, + createApplicationsMapService: makeStubApplicationsMapService, }); beforeEach(() => { @@ -348,6 +368,7 @@ describe("Lambda handler", () => { const faultyHandler = createHandler({ createObservabilityService: () => faultyObservability, createConfigLoaderService: makeStubConfigLoaderService, + createApplicationsMapService: makeStubApplicationsMapService, }); const sqsMessage: SQSRecord = { @@ -527,6 +548,7 @@ describe("createHandler default wiring", () => { [], state.mockObservabilityInstance, expect.any(Object), + expect.any(Object), ); expect(result).toEqual(["ok"]); diff --git a/lambdas/client-transform-filter-lambda/src/__tests__/services/payload-signer.test.ts b/lambdas/client-transform-filter-lambda/src/__tests__/services/payload-signer.test.ts new file mode 100644 index 00000000..368f5695 --- /dev/null +++ b/lambdas/client-transform-filter-lambda/src/__tests__/services/payload-signer.test.ts @@ -0,0 +1,50 @@ +import hmacsha256 from "crypto-js/hmac-sha256"; +import type { ClientCallbackPayload } from "@nhs-notify-client-callbacks/models"; +import { signPayload } from "services/payload-signer"; + +const makePayload = (id = "msg-1") => + ({ data: [{ id }] }) as unknown as ClientCallbackPayload; + +describe("signPayload", () => { + it("produces the expected HMAC-SHA256 hex string", () => { + const payload = makePayload(); + const applicationId = "app-id-1"; + const apiKey = "api-key-1"; + + const expected = hmacsha256( + JSON.stringify(payload), + `${applicationId}.${apiKey}`, + ).toString(); + + expect(signPayload(payload, applicationId, apiKey)).toBe(expected); + }); + + it("returns a non-empty hex string", () => { + const result = signPayload(makePayload(), "app-id", "api-key"); + expect(result).toMatch(/^[0-9a-f]+$/); + }); + + it("produces different signatures for different payloads", () => { + const apiKey = "key"; + const appId = "app"; + expect(signPayload(makePayload("msg-1"), appId, apiKey)).not.toBe( + signPayload(makePayload("msg-2"), appId, apiKey), + ); + }); + + it("produces different signatures for different applicationIds", () => { + const payload = makePayload(); + const apiKey = "key"; + expect(signPayload(payload, "app-1", apiKey)).not.toBe( + signPayload(payload, "app-2", apiKey), + ); + }); + + it("produces different signatures for different apiKeys", () => { + const payload = makePayload(); + const appId = "app"; + expect(signPayload(payload, appId, "key-1")).not.toBe( + signPayload(payload, appId, "key-2"), + ); + }); +}); diff --git a/lambdas/client-transform-filter-lambda/src/__tests__/services/ssm-applications-map.test.ts b/lambdas/client-transform-filter-lambda/src/__tests__/services/ssm-applications-map.test.ts new file mode 100644 index 00000000..6ec07353 --- /dev/null +++ b/lambdas/client-transform-filter-lambda/src/__tests__/services/ssm-applications-map.test.ts @@ -0,0 +1,101 @@ +import { GetParameterCommand, SSMClient } from "@aws-sdk/client-ssm"; +import { ApplicationsMapService } from "services/ssm-applications-map"; + +const makeSsmClient = (value: string | undefined) => + ({ + send: jest + .fn() + .mockResolvedValue( + value === undefined ? {} : { Parameter: { Value: value } }, + ), + }) as unknown as SSMClient; + +describe("ApplicationsMapService", () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it("returns the applicationId for a known clientId", async () => { + const ssmClient = makeSsmClient( + JSON.stringify({ "client-1": "app-id-1", "client-2": "app-id-2" }), + ); + const service = new ApplicationsMapService(ssmClient, "/test/param"); + + expect(await service.getApplicationId("client-1")).toBe("app-id-1"); + }); + + it("returns undefined for an unknown clientId", async () => { + const ssmClient = makeSsmClient(JSON.stringify({ "client-1": "app-id-1" })); + const service = new ApplicationsMapService(ssmClient, "/test/param"); + + expect(await service.getApplicationId("unknown")).toBeUndefined(); + }); + + it("loads from SSM and sends GetParameterCommand with WithDecryption", async () => { + const ssmClient = makeSsmClient(JSON.stringify({ "client-1": "app-id-1" })); + const service = new ApplicationsMapService(ssmClient, "/test/param"); + + await service.getApplicationId("client-1"); + + expect(ssmClient.send).toHaveBeenCalledTimes(1); + expect((ssmClient.send as jest.Mock).mock.calls[0][0]).toBeInstanceOf( + GetParameterCommand, + ); + }); + + it("caches the map and does not call SSM again within TTL", async () => { + const ssmClient = makeSsmClient(JSON.stringify({ "client-1": "app-id-1" })); + const service = new ApplicationsMapService(ssmClient, "/test/param", 5000); + + await service.getApplicationId("client-1"); + await service.getApplicationId("client-1"); + + expect(ssmClient.send).toHaveBeenCalledTimes(1); + }); + + it("reloads from SSM after TTL expires", async () => { + const ssmClient = makeSsmClient(JSON.stringify({ "client-1": "app-id-1" })); + const service = new ApplicationsMapService(ssmClient, "/test/param", 5000); + + await service.getApplicationId("client-1"); + jest.advanceTimersByTime(6000); + await service.getApplicationId("client-1"); + + expect(ssmClient.send).toHaveBeenCalledTimes(2); + }); + + it("throws when SSM parameter is missing", async () => { + const ssmClient = makeSsmClient(undefined); + const service = new ApplicationsMapService(ssmClient, "/test/param"); + + await expect(service.getApplicationId("client-1")).rejects.toThrow( + "SSM parameter '/test/param' not found or has no value", + ); + }); + + it("throws when SSM parameter has empty value", async () => { + const ssmClient = { + send: jest.fn().mockResolvedValue({ Parameter: { Value: "" } }), + } as unknown as SSMClient; + const service = new ApplicationsMapService(ssmClient, "/test/param"); + + await expect(service.getApplicationId("client-1")).rejects.toThrow( + "SSM parameter '/test/param' not found or has no value", + ); + }); + + it("reset clears the cache and forces reload on next call", async () => { + const ssmClient = makeSsmClient(JSON.stringify({ "client-1": "app-id-1" })); + const service = new ApplicationsMapService(ssmClient, "/test/param", 5000); + + await service.getApplicationId("client-1"); + service.reset(); + await service.getApplicationId("client-1"); + + expect(ssmClient.send).toHaveBeenCalledTimes(2); + }); +}); diff --git a/lambdas/client-transform-filter-lambda/src/handler.ts b/lambdas/client-transform-filter-lambda/src/handler.ts index 09e0dfb5..3c8190aa 100644 --- a/lambdas/client-transform-filter-lambda/src/handler.ts +++ b/lambdas/client-transform-filter-lambda/src/handler.ts @@ -6,17 +6,24 @@ import type { } from "@nhs-notify-client-callbacks/models"; import { validateStatusPublishEvent } from "services/validators/event-validator"; import { transformEvent } from "services/transformers/event-transformer"; -import { extractCorrelationId } from "services/logger"; +import { extractCorrelationId, logger } from "services/logger"; import { ValidationError, getEventError } from "services/error-handler"; import type { ObservabilityService } from "services/observability"; import type { ConfigLoader } from "services/config-loader"; import { evaluateSubscriptionFilters } from "services/subscription-filter"; +import type { ApplicationsMapService } from "services/ssm-applications-map"; +import { signPayload } from "services/payload-signer"; const BATCH_CONCURRENCY = Number(process.env.BATCH_CONCURRENCY) || 10; const MESSAGE_ROOT_URI = process.env.MESSAGE_ROOT_URI ?? ""; +type UnsignedEvent = StatusPublishEvent & { + transformedPayload: ClientCallbackPayload; +}; + export interface TransformedEvent extends StatusPublishEvent { transformedPayload: ClientCallbackPayload; + headers: { "x-hmac-sha256-signature": string }; } class BatchStats { @@ -83,7 +90,7 @@ function parseSqsMessageBody( function processSingleEvent( event: StatusPublishEvent, observability: ObservabilityService, -): TransformedEvent { +): UnsignedEvent { const correlationId = extractCorrelationId(event); const eventType = event.type; const { clientId, messageId } = event.data; @@ -114,6 +121,52 @@ function processSingleEvent( }; } +async function signBatch( + filteredEvents: UnsignedEvent[], + applicationsMapService: ApplicationsMapService, + configLoader: ConfigLoader, + stats: BatchStats, +): Promise { + const results = await pMap( + filteredEvents, + async (event): Promise => { + const { clientId } = event.data; + const correlationId = extractCorrelationId(event); + + const applicationId = + await applicationsMapService.getApplicationId(clientId); + if (!applicationId) { + stats.recordFiltered(); + logger.warn( + "No applicationId found in SSM map - event will not be delivered", + { clientId, correlationId }, + ); + return undefined; + } + + const clientConfig = await configLoader.loadClientConfig(clientId); + const apiKey = clientConfig?.[0]?.Targets?.[0]?.APIKey?.HeaderValue; + if (!apiKey) { + stats.recordFiltered(); + logger.warn( + "No apiKey in client config - event will not be delivered", + { clientId, correlationId }, + ); + return undefined; + } + + const signature = signPayload( + event.transformedPayload, + applicationId, + apiKey, + ); + return { ...event, headers: { "x-hmac-sha256-signature": signature } }; + }, + { concurrency: BATCH_CONCURRENCY }, + ); + return results.filter((e): e is TransformedEvent => e !== undefined); +} + function recordDeliveryInitiated( transformedEvents: TransformedEvent[], observability: ObservabilityService, @@ -132,11 +185,11 @@ function recordDeliveryInitiated( } async function filterBatch( - transformedEvents: TransformedEvent[], + transformedEvents: UnsignedEvent[], configLoader: ConfigLoader, observability: ObservabilityService, stats: BatchStats, -): Promise { +): Promise { observability.recordFilteringStarted({ batchSize: transformedEvents.length }); const uniqueClientIds = new Set( @@ -154,7 +207,7 @@ async function filterBatch( const configByClientId = new Map(configEntries); - const filtered: TransformedEvent[] = []; + const filtered: UnsignedEvent[] = []; for (const event of transformedEvents) { const { clientId } = event.data; @@ -191,7 +244,7 @@ async function transformBatch( sqsRecords: SQSRecord[], observability: ObservabilityService, stats: BatchStats, -): Promise { +): Promise { return pMap( sqsRecords, (sqsRecord: SQSRecord) => { @@ -217,6 +270,7 @@ export async function processEvents( event: SQSRecord[], observability: ObservabilityService, configLoader: ConfigLoader, + applicationsMapService: ApplicationsMapService, ): Promise { const startTime = Date.now(); const stats = new BatchStats(); @@ -231,6 +285,13 @@ export async function processEvents( stats, ); + const signedEvents = await signBatch( + filteredEvents, + applicationsMapService, + configLoader, + stats, + ); + const processingTime = Date.now() - startTime; observability.logBatchProcessingCompleted({ ...stats.toObject(), @@ -238,10 +299,10 @@ export async function processEvents( processingTimeMs: processingTime, }); - recordDeliveryInitiated(filteredEvents, observability); + recordDeliveryInitiated(signedEvents, observability); await observability.flush(); - return filteredEvents; + return signedEvents; } catch (error) { stats.recordFailure(); diff --git a/lambdas/client-transform-filter-lambda/src/index.ts b/lambdas/client-transform-filter-lambda/src/index.ts index 87d4afdd..31fac0ec 100644 --- a/lambdas/client-transform-filter-lambda/src/index.ts +++ b/lambdas/client-transform-filter-lambda/src/index.ts @@ -1,15 +1,37 @@ import type { SQSRecord } from "aws-lambda"; +import { SSMClient } from "@aws-sdk/client-ssm"; import { Logger, flushLogs } from "services/logger"; import { CallbackMetrics, createMetricLogger } from "services/metrics"; import { ObservabilityService } from "services/observability"; import { ConfigLoaderService } from "services/config-loader-service"; +import { ApplicationsMapService } from "services/ssm-applications-map"; import { type TransformedEvent, processEvents } from "handler"; export const configLoaderService = new ConfigLoaderService(); +const DEFAULT_SSM_CACHE_TTL_SECONDS = 60; + +export const createSsmClient = ( + env: NodeJS.ProcessEnv = process.env, +): SSMClient => { + const endpoint = env.AWS_ENDPOINT_URL; + return new SSMClient({ endpoint }); +}; + +export const applicationsMapService = new ApplicationsMapService( + createSsmClient(), + process.env.APPLICATIONS_MAP_PARAMETER ?? "", + (Number.parseInt( + process.env.APPLICATIONS_MAP_CACHE_TTL_SECONDS ?? + `${DEFAULT_SSM_CACHE_TTL_SECONDS}`, + 10, + ) || DEFAULT_SSM_CACHE_TTL_SECONDS) * 1000, +); + export interface HandlerDependencies { createObservabilityService?: () => ObservabilityService; createConfigLoaderService?: () => ConfigLoaderService; + createApplicationsMapService?: () => ApplicationsMapService; } function createDefaultObservabilityService(): ObservabilityService { @@ -24,6 +46,10 @@ function createDefaultConfigLoaderService(): ConfigLoaderService { return configLoaderService; } +function createDefaultApplicationsMapService(): ApplicationsMapService { + return applicationsMapService; +} + export function createHandler( dependencies: Partial = {}, ): (event: SQSRecord[]) => Promise { @@ -33,6 +59,10 @@ export function createHandler( const configLoader = ( dependencies.createConfigLoaderService ?? createDefaultConfigLoaderService )(); + const applicationsMap = ( + dependencies.createApplicationsMapService ?? + createDefaultApplicationsMapService + )(); return async (event: SQSRecord[]): Promise => { const observability = createObservabilityService(); @@ -40,6 +70,7 @@ export function createHandler( event, observability, configLoader.getLoader(), + applicationsMap, ); await flushLogs(); return result; diff --git a/lambdas/client-transform-filter-lambda/src/services/payload-signer.ts b/lambdas/client-transform-filter-lambda/src/services/payload-signer.ts new file mode 100644 index 00000000..f533c02a --- /dev/null +++ b/lambdas/client-transform-filter-lambda/src/services/payload-signer.ts @@ -0,0 +1,13 @@ +import hmacsha256 from "crypto-js/hmac-sha256"; +import type { ClientCallbackPayload } from "@nhs-notify-client-callbacks/models"; + +export function signPayload( + payload: ClientCallbackPayload, + applicationId: string, + apiKey: string, +): string { + return hmacsha256( + JSON.stringify(payload), + `${applicationId}.${apiKey}`, + ).toString(); +} diff --git a/lambdas/client-transform-filter-lambda/src/services/ssm-applications-map.ts b/lambdas/client-transform-filter-lambda/src/services/ssm-applications-map.ts new file mode 100644 index 00000000..8125fbf2 --- /dev/null +++ b/lambdas/client-transform-filter-lambda/src/services/ssm-applications-map.ts @@ -0,0 +1,57 @@ +import { GetParameterCommand, SSMClient } from "@aws-sdk/client-ssm"; +import { logger } from "services/logger"; + +const DEFAULT_CACHE_TTL_MS = 60_000; + +export class ApplicationsMapService { + private cachedMap: Map | undefined; + + private cacheExpiresAt = 0; + + constructor( + private readonly ssmClient: SSMClient, + private readonly parameterName: string, + private readonly cacheTtlMs: number = DEFAULT_CACHE_TTL_MS, + ) {} + + async getApplicationId(clientId: string): Promise { + const map = await this.getMap(); + return map.get(clientId); + } + + private async getMap(): Promise> { + if (this.cachedMap && Date.now() < this.cacheExpiresAt) { + logger.debug("Applications map loaded from cache"); + return this.cachedMap; + } + + const response = await this.ssmClient.send( + new GetParameterCommand({ + Name: this.parameterName, + WithDecryption: true, + }), + ); + + if (!response.Parameter?.Value) { + throw new Error( + `SSM parameter '${this.parameterName}' not found or has no value`, + ); + } + + const parsed = JSON.parse(response.Parameter.Value) as Record< + string, + string + >; + this.cachedMap = new Map(Object.entries(parsed)); + this.cacheExpiresAt = Date.now() + this.cacheTtlMs; + logger.info("Applications map loaded from SSM", { + parameterName: this.parameterName, + }); + return this.cachedMap; + } + + reset(): void { + this.cachedMap = undefined; + this.cacheExpiresAt = 0; + } +} diff --git a/package-lock.json b/package-lock.json index e56e383f..871500e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,10 +51,12 @@ "version": "0.0.1", "dependencies": { "@aws-sdk/client-s3": "^3.821.0", + "@aws-sdk/client-ssm": "^3.821.0", "@nhs-notify-client-callbacks/logger": "*", "@nhs-notify-client-callbacks/models": "*", "aws-embedded-metrics": "^4.2.1", "cloudevents": "^8.0.2", + "crypto-js": "^4.2.0", "esbuild": "^0.25.0", "p-map": "^4.0.0", "zod": "^4.1.13" @@ -62,6 +64,7 @@ "devDependencies": { "@tsconfig/node22": "^22.0.2", "@types/aws-lambda": "^8.10.148", + "@types/crypto-js": "^4.2.2", "@types/jest": "^29.5.14", "jest": "^29.7.0", "jest-mock-extended": "^3.0.7", @@ -362,8 +365,6 @@ }, "node_modules/@aws-sdk/client-cognito-identity": { "version": "3.1005.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.1005.0.tgz", - "integrity": "sha512-vYZCaQPvawj2W76UtQCu2VCV0vEQnHxL1MOKri4jICLSRiFPlHTosUuwJDzJIIrvTuFgR2DolbcBLRSCLBo/oQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -412,8 +413,6 @@ }, "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-endpoints": { "version": "3.996.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.4.tgz", - "integrity": "sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.5", @@ -428,8 +427,6 @@ }, "node_modules/@aws-sdk/client-eventbridge": { "version": "3.1001.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-eventbridge/-/client-eventbridge-3.1001.0.tgz", - "integrity": "sha512-asySfaKnDTxhMtxCX1dvjDPfJwrQ5xy/tzdmFHmRyURNhIhXG3dwishJ6ROXzOrY7hFCiz+OTWWjZ+IJbrfzkA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -479,8 +476,6 @@ }, "node_modules/@aws-sdk/client-eventbridge/node_modules/@aws-sdk/util-endpoints": { "version": "3.996.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.3.tgz", - "integrity": "sha512-yWIQSNiCjykLL+ezN5A+DfBb1gfXTytBxm57e64lYmwxDHNmInYHRJYYRAGWG1o77vKEiWaw4ui28e3yb1k5aQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.4", @@ -637,10 +632,75 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/client-ssm": { + "version": "3.1011.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ssm/-/client-ssm-3.1011.0.tgz", + "integrity": "sha512-XFrixqhD4Lp9Bq/ZefbeYLp9oOL+4htqo7UxDCgrON0HBoj5u/3bDQPPse7CrfaYVw986mvoDJVRnIY2g+TDjA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/credential-provider-node": "^3.972.21", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.8", + "@aws-sdk/middleware-user-agent": "^3.972.21", + "@aws-sdk/region-config-resolver": "^3.972.8", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.7", + "@smithy/config-resolver": "^4.4.11", + "@smithy/core": "^3.23.11", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.25", + "@smithy/middleware-retry": "^4.4.42", + "@smithy/middleware-serde": "^4.2.14", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.4.16", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.5", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.41", + "@smithy/util-defaults-mode-node": "^4.2.44", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.12", + "@smithy/util-utf8": "^4.2.2", + "@smithy/util-waiter": "^4.2.13", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz", + "integrity": "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-endpoints": "^3.3.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/client-sts": { "version": "3.1001.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.1001.0.tgz", - "integrity": "sha512-1HVxJcad+BTMVQ4lN2jw4SzyVqnIRZ7mb8YjwqMQ6p1MjuklSriVUXKtYFyxLVJnqaw61nFv9F8oHMOK69p6BQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -689,8 +749,6 @@ }, "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-endpoints": { "version": "3.996.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.3.tgz", - "integrity": "sha512-yWIQSNiCjykLL+ezN5A+DfBb1gfXTytBxm57e64lYmwxDHNmInYHRJYYRAGWG1o77vKEiWaw4ui28e3yb1k5aQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.4", @@ -704,22 +762,22 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.973.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.19.tgz", - "integrity": "sha512-56KePyOcZnKTWCd89oJS1G6j3HZ9Kc+bh/8+EbvtaCCXdP6T7O7NzCiPuHRhFLWnzXIaXX3CxAz0nI5My9spHQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/xml-builder": "^3.972.10", - "@smithy/core": "^3.23.9", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/property-provider": "^4.2.11", - "@smithy/protocol-http": "^5.3.11", - "@smithy/signature-v4": "^5.3.11", - "@smithy/smithy-client": "^4.12.3", - "@smithy/types": "^4.13.0", + "version": "3.973.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.20.tgz", + "integrity": "sha512-i3GuX+lowD892F3IuJf8o6AbyDupMTdyTxQrCJGcn71ni5hTZ82L4nQhcdumxZ7XPJRJJVHS/CR3uYOIIs0PVA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/xml-builder": "^3.972.11", + "@smithy/core": "^3.23.11", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/smithy-client": "^4.12.5", + "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", - "@smithy/util-middleware": "^4.2.11", + "@smithy/util-middleware": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, @@ -740,8 +798,6 @@ }, "node_modules/@aws-sdk/credential-provider-cognito-identity": { "version": "3.972.11", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.972.11.tgz", - "integrity": "sha512-yHBjinYauxSvikf15EtgXyZ9TBIMVHUSWFPycQtPltTINpK+uv6K22zKkVsbxpB0gvsdRdIWP0UG5gejM+jPuQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/nested-clients": "^3.996.8", @@ -755,15 +811,15 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.17", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.17.tgz", - "integrity": "sha512-MBAMW6YELzE1SdkOniqr51mrjapQUv8JXSGxtwRjQV0mwVDutVsn22OPAUt4RcLRvdiHQmNBDEFP9iTeSVCOlA==", + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.18.tgz", + "integrity": "sha512-X0B8AlQY507i5DwjLByeU2Af4ARsl9Vr84koDcXCbAkplmU+1xBFWxEPrWRAoh56waBne/yJqEloSwvRf4x6XA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/types": "^3.973.5", - "@smithy/property-provider": "^4.2.11", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -771,20 +827,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.19.tgz", - "integrity": "sha512-9EJROO8LXll5a7eUFqu48k6BChrtokbmgeMWmsH7lBb6lVbtjslUYz/ShLi+SHkYzTomiGBhmzTW7y+H4BxsnA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/types": "^3.973.5", - "@smithy/fetch-http-handler": "^5.3.13", - "@smithy/node-http-handler": "^4.4.14", - "@smithy/property-provider": "^4.2.11", - "@smithy/protocol-http": "^5.3.11", - "@smithy/smithy-client": "^4.12.3", - "@smithy/types": "^4.13.0", - "@smithy/util-stream": "^4.5.17", + "version": "3.972.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.20.tgz", + "integrity": "sha512-ey9Lelj001+oOfrbKmS6R2CJAiXX7QKY4Vj9VJv6L2eE6/VjD8DocHIoYqztTm70xDLR4E1jYPTKfIui+eRNDA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/types": "^3.973.6", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/node-http-handler": "^4.4.16", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.5", + "@smithy/types": "^4.13.1", + "@smithy/util-stream": "^4.5.19", "tslib": "^2.6.2" }, "engines": { @@ -792,24 +848,24 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.18", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.18.tgz", - "integrity": "sha512-vthIAXJISZnj2576HeyLBj4WTeX+I7PwWeRkbOa0mVX39K13SCGxCgOFuKj2ytm9qTlLOmXe4cdEnroteFtJfw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/credential-provider-env": "^3.972.17", - "@aws-sdk/credential-provider-http": "^3.972.19", - "@aws-sdk/credential-provider-login": "^3.972.18", - "@aws-sdk/credential-provider-process": "^3.972.17", - "@aws-sdk/credential-provider-sso": "^3.972.18", - "@aws-sdk/credential-provider-web-identity": "^3.972.18", - "@aws-sdk/nested-clients": "^3.996.8", - "@aws-sdk/types": "^3.973.5", - "@smithy/credential-provider-imds": "^4.2.11", - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "version": "3.972.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.20.tgz", + "integrity": "sha512-5flXSnKHMloObNF+9N0cupKegnH1Z37cdVlpETVgx8/rAhCe+VNlkcZH3HDg2SDn9bI765S+rhNPXGDJJPfbtA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/credential-provider-env": "^3.972.18", + "@aws-sdk/credential-provider-http": "^3.972.20", + "@aws-sdk/credential-provider-login": "^3.972.20", + "@aws-sdk/credential-provider-process": "^3.972.18", + "@aws-sdk/credential-provider-sso": "^3.972.20", + "@aws-sdk/credential-provider-web-identity": "^3.972.20", + "@aws-sdk/nested-clients": "^3.996.10", + "@aws-sdk/types": "^3.973.6", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -817,18 +873,18 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.18", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.18.tgz", - "integrity": "sha512-kINzc5BBxdYBkPZ0/i1AMPMOk5b5QaFNbYMElVw5QTX13AKj6jcxnv/YNl9oW9mg+Y08ti19hh01HhyEAxsSJQ==", + "version": "3.972.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.20.tgz", + "integrity": "sha512-gEWo54nfqp2jABMu6HNsjVC4hDLpg9HC8IKSJnp0kqWtxIJYHTmiLSsIfI4ScQjxEwpB+jOOH8dOLax1+hy/Hw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/nested-clients": "^3.996.8", - "@aws-sdk/types": "^3.973.5", - "@smithy/property-provider": "^4.2.11", - "@smithy/protocol-http": "^5.3.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/nested-clients": "^3.996.10", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -836,22 +892,22 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.19.tgz", - "integrity": "sha512-yDWQ9dFTr+IMxwanFe7+tbN5++q8psZBjlUwOiCXn1EzANoBgtqBwcpYcHaMGtn0Wlfj4NuXdf2JaEx1lz5RaQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.17", - "@aws-sdk/credential-provider-http": "^3.972.19", - "@aws-sdk/credential-provider-ini": "^3.972.18", - "@aws-sdk/credential-provider-process": "^3.972.17", - "@aws-sdk/credential-provider-sso": "^3.972.18", - "@aws-sdk/credential-provider-web-identity": "^3.972.18", - "@aws-sdk/types": "^3.973.5", - "@smithy/credential-provider-imds": "^4.2.11", - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "version": "3.972.21", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.21.tgz", + "integrity": "sha512-hah8if3/B/Q+LBYN5FukyQ1Mym6PLPDsBOBsIgNEYD6wLyZg0UmUF/OKIVC3nX9XH8TfTPuITK+7N/jenVACWA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.18", + "@aws-sdk/credential-provider-http": "^3.972.20", + "@aws-sdk/credential-provider-ini": "^3.972.20", + "@aws-sdk/credential-provider-process": "^3.972.18", + "@aws-sdk/credential-provider-sso": "^3.972.20", + "@aws-sdk/credential-provider-web-identity": "^3.972.20", + "@aws-sdk/types": "^3.973.6", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -859,16 +915,16 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.17", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.17.tgz", - "integrity": "sha512-c8G8wT1axpJDgaP3xzcy+q8Y1fTi9A2eIQJvyhQ9xuXrUZhlCfXbC0vM9bM1CUXiZppFQ1p7g0tuUMvil/gCPg==", + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.18.tgz", + "integrity": "sha512-Tpl7SRaPoOLT32jbTWchPsn52hYYgJ0kpiFgnwk8pxTANQdUymVSZkzFvv1+oOgZm1CrbQUP9MBeoMZ9IzLZjA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/types": "^3.973.5", - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -876,18 +932,18 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.18", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.18.tgz", - "integrity": "sha512-YHYEfj5S2aqInRt5ub8nDOX8vAxgMvd84wm2Y3WVNfFa/53vOv9T7WOAqXI25qjj3uEcV46xxfqdDQk04h5XQA==", + "version": "3.972.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.20.tgz", + "integrity": "sha512-p+R+PYR5Z7Gjqf/6pvbCnzEHcqPCpLzR7Yf127HjJ6EAb4hUcD+qsNRnuww1sB/RmSeCLxyay8FMyqREw4p1RA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/nested-clients": "^3.996.8", - "@aws-sdk/token-providers": "3.1005.0", - "@aws-sdk/types": "^3.973.5", - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/nested-clients": "^3.996.10", + "@aws-sdk/token-providers": "3.1009.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -895,17 +951,17 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.18", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.18.tgz", - "integrity": "sha512-OqlEQpJ+J3T5B96qtC1zLLwkBloechP+fezKbCH0sbd2cCc0Ra55XpxWpk/hRj69xAOYtHvoC4orx6eTa4zU7g==", + "version": "3.972.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.20.tgz", + "integrity": "sha512-rWCmh8o7QY4CsUj63qopzMzkDq/yPpkrpb+CnjBEFSOg/02T/we7sSTVg4QsDiVS9uwZ8VyONhq98qt+pIh3KA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/nested-clients": "^3.996.8", - "@aws-sdk/types": "^3.973.5", - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/nested-clients": "^3.996.10", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -914,8 +970,6 @@ }, "node_modules/@aws-sdk/credential-providers": { "version": "3.1005.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.1005.0.tgz", - "integrity": "sha512-H6Prb37+8TdTkGsYH1WkWjupWdM4aHTmF6VLdyTwOWmg4WFkKAb1OJcMrxNCJ8Yy/UvxOca4jk6z2ej+T1pjtQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-cognito-identity": "3.1005.0", @@ -996,14 +1050,14 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.7.tgz", - "integrity": "sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ==", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.8.tgz", + "integrity": "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -1023,13 +1077,13 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.7.tgz", - "integrity": "sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w==", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.8.tgz", + "integrity": "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -1037,15 +1091,15 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.7.tgz", - "integrity": "sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ==", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.8.tgz", + "integrity": "sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", + "@aws-sdk/types": "^3.973.6", "@aws/lambda-invoke-store": "^0.2.2", - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -1054,8 +1108,6 @@ }, "node_modules/@aws-sdk/middleware-sdk-s3": { "version": "3.972.16", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.16.tgz", - "integrity": "sha512-U4K1rqyJYvT/zgTI3+rN+MToa51dFnnq1VSsVJuJWPNEKcEnuZVqf7yTpkJJMkYixVW5TTi1dgupd+nmJ0JyWw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/core": "^3.973.16", @@ -1106,18 +1158,18 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.972.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.20.tgz", - "integrity": "sha512-3kNTLtpUdeahxtnJRnj/oIdLAUdzTfr9N40KtxNhtdrq+Q1RPMdCJINRXq37m4t5+r3H70wgC3opW46OzFcZYA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/util-endpoints": "^3.996.4", - "@smithy/core": "^3.23.9", - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", - "@smithy/util-retry": "^4.2.11", + "version": "3.972.21", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.21.tgz", + "integrity": "sha512-62XRl1GDYPpkt7cx1AX1SPy9wgNE9Iw/NPuurJu4lmhCWS7sGKO+kS53TQ8eRmIxy3skmvNInnk0ZbWrU5Dpyg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@smithy/core": "^3.23.11", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-retry": "^4.2.12", "tslib": "^2.6.2" }, "engines": { @@ -1125,15 +1177,15 @@ } }, "node_modules/@aws-sdk/middleware-user-agent/node_modules/@aws-sdk/util-endpoints": { - "version": "3.996.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.4.tgz", - "integrity": "sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==", + "version": "3.996.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz", + "integrity": "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", - "@smithy/util-endpoints": "^3.3.2", + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-endpoints": "^3.3.3", "tslib": "^2.6.2" }, "engines": { @@ -1141,47 +1193,47 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.996.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.8.tgz", - "integrity": "sha512-6HlLm8ciMW8VzfB80kfIx16PBA9lOa9Dl+dmCBi78JDhvGlx3I7Rorwi5PpVRkL31RprXnYna3yBf6UKkD/PqA==", + "version": "3.996.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.10.tgz", + "integrity": "sha512-SlDol5Z+C7Ivnc2rKGqiqfSUmUZzY1qHfVs9myt/nxVwswgfpjdKahyTzLTx802Zfq0NFRs7AejwKzzzl5Co2w==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/middleware-host-header": "^3.972.7", - "@aws-sdk/middleware-logger": "^3.972.7", - "@aws-sdk/middleware-recursion-detection": "^3.972.7", - "@aws-sdk/middleware-user-agent": "^3.972.20", - "@aws-sdk/region-config-resolver": "^3.972.7", - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/util-endpoints": "^3.996.4", - "@aws-sdk/util-user-agent-browser": "^3.972.7", - "@aws-sdk/util-user-agent-node": "^3.973.5", - "@smithy/config-resolver": "^4.4.10", - "@smithy/core": "^3.23.9", - "@smithy/fetch-http-handler": "^5.3.13", - "@smithy/hash-node": "^4.2.11", - "@smithy/invalid-dependency": "^4.2.11", - "@smithy/middleware-content-length": "^4.2.11", - "@smithy/middleware-endpoint": "^4.4.23", - "@smithy/middleware-retry": "^4.4.40", - "@smithy/middleware-serde": "^4.2.12", - "@smithy/middleware-stack": "^4.2.11", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/node-http-handler": "^4.4.14", - "@smithy/protocol-http": "^5.3.11", - "@smithy/smithy-client": "^4.12.3", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.8", + "@aws-sdk/middleware-user-agent": "^3.972.21", + "@aws-sdk/region-config-resolver": "^3.972.8", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.7", + "@smithy/config-resolver": "^4.4.11", + "@smithy/core": "^3.23.11", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.25", + "@smithy/middleware-retry": "^4.4.42", + "@smithy/middleware-serde": "^4.2.14", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.4.16", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.5", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.39", - "@smithy/util-defaults-mode-node": "^4.2.42", - "@smithy/util-endpoints": "^3.3.2", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-retry": "^4.2.11", + "@smithy/util-defaults-mode-browser": "^4.3.41", + "@smithy/util-defaults-mode-node": "^4.2.44", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, @@ -1190,15 +1242,15 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-endpoints": { - "version": "3.996.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.4.tgz", - "integrity": "sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==", + "version": "3.996.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz", + "integrity": "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", - "@smithy/util-endpoints": "^3.3.2", + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-endpoints": "^3.3.3", "tslib": "^2.6.2" }, "engines": { @@ -1206,15 +1258,15 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.7.tgz", - "integrity": "sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA==", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.8.tgz", + "integrity": "sha512-1eD4uhTDeambO/PNIDVG19A6+v4NdD7xzwLHDutHsUqz0B+i661MwQB2eYO4/crcCvCiQG4SRm1k81k54FEIvw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/config-resolver": "^4.4.10", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/config-resolver": "^4.4.11", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -1223,8 +1275,6 @@ }, "node_modules/@aws-sdk/signature-v4-multi-region": { "version": "3.996.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.4.tgz", - "integrity": "sha512-MGa8ro0onekYIiesHX60LwKdkxK3Kd61p7TTbLwZemBqlnD9OLrk9sXZdFOIxXanJ+3AaJnV/jiX866eD/4PDg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/middleware-sdk-s3": "^3.972.16", @@ -1239,17 +1289,17 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.1005.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1005.0.tgz", - "integrity": "sha512-vMxd+ivKqSxU9bHx5vmAlFKDAkjGotFU56IOkDa5DaTu1WWwbcse0yFHEm9I537oVvodaiwMl3VBwgHfzQ2rvw==", + "version": "3.1009.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1009.0.tgz", + "integrity": "sha512-KCPLuTqN9u0Rr38Arln78fRG9KXpzsPWmof+PZzfAHMMQq2QED6YjQrkrfiH7PDefLWEposY1o4/eGwrmKA4JA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.19", - "@aws-sdk/nested-clients": "^3.996.8", - "@aws-sdk/types": "^3.973.5", - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/nested-clients": "^3.996.10", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -1257,12 +1307,12 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.973.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.5.tgz", - "integrity": "sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ==", + "version": "3.973.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.6.tgz", + "integrity": "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -1305,27 +1355,28 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.7.tgz", - "integrity": "sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw==", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.8.tgz", + "integrity": "sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.973.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.5.tgz", - "integrity": "sha512-Dyy38O4GeMk7UQ48RupfHif//gqnOPbq/zlvRssc11E2mClT+aUfc3VS2yD8oLtzqO3RsqQ9I3gOBB4/+HjPOw==", + "version": "3.973.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.7.tgz", + "integrity": "sha512-Hz6EZMUAEzqUd7e+vZ9LE7mn+5gMbxltXy18v+YSFY+9LBJz15wkNZvw5JqfX3z0FS9n3bgUtz3L5rAsfh4YlA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "^3.972.20", - "@aws-sdk/types": "^3.973.5", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/types": "^4.13.0", + "@aws-sdk/middleware-user-agent": "^3.972.21", + "@aws-sdk/types": "^3.973.6", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -1341,12 +1392,12 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.10", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.10.tgz", - "integrity": "sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==", + "version": "3.972.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.11.tgz", + "integrity": "sha512-iitV/gZKQMvY9d7ovmyFnFuTHbBAtrmLnvaSb/3X8vOKyevwtpmEtyc8AdhVWZe0pI/1GsHxlEvQeOePFzy7KQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "fast-xml-parser": "5.4.1", "tslib": "^2.6.2" }, @@ -1822,20 +1873,6 @@ "version": "4.3.7", "license": "MIT" }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.0", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "dev": true, @@ -2339,8 +2376,6 @@ }, "node_modules/@jest/reporters/node_modules/@bcoe/v8-coverage": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true, "license": "MIT" }, @@ -2604,12 +2639,12 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.11.tgz", - "integrity": "sha512-Hj4WoYWMJnSpM6/kchsm4bUNTL9XiSyhvoMb2KIq4VJzyDt7JpGHUZHkVNPZVC7YE1tf8tPeVauxpFBKGW4/KQ==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.12.tgz", + "integrity": "sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2638,16 +2673,16 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.10.tgz", - "integrity": "sha512-IRTkd6ps0ru+lTWnfnsbXzW80A8Od8p3pYiZnW98K2Hb20rqfsX7VTlfUwhrcOeSSy68Gn9WBofwPuw3e5CCsg==", + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.11.tgz", + "integrity": "sha512-YxFiiG4YDAtX7WMN7RuhHZLeTmRRAOyCbr+zB8e3AQzHPnUhS8zXjB1+cniPVQI3xbWsQPM0X2aaIkO/ME0ymw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.11", - "@smithy/types": "^4.13.0", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", - "@smithy/util-endpoints": "^3.3.2", - "@smithy/util-middleware": "^4.2.11", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", "tslib": "^2.6.2" }, "engines": { @@ -2655,18 +2690,18 @@ } }, "node_modules/@smithy/core": { - "version": "3.23.9", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.9.tgz", - "integrity": "sha512-1Vcut4LEL9HZsdpI0vFiRYIsaoPwZLjAxnVQDUMQK8beMS+EYPLDQCXtbzfxmM5GzSgjfe2Q9M7WaXwIMQllyQ==", + "version": "3.23.12", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.12.tgz", + "integrity": "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.2.12", - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-stream": "^4.5.17", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-stream": "^4.5.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" @@ -2676,15 +2711,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.11.tgz", - "integrity": "sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.12.tgz", + "integrity": "sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.11", - "@smithy/property-provider": "^4.2.11", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", "tslib": "^2.6.2" }, "engines": { @@ -2752,14 +2787,14 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.13", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.13.tgz", - "integrity": "sha512-U2Hcfl2s3XaYjikN9cT4mPu8ybDbImV3baXR0PkVlC0TTx808bRP3FaPGAzPtB8OByI+JqJ1kyS+7GEgae7+qQ==", + "version": "5.3.15", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.15.tgz", + "integrity": "sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.11", - "@smithy/querystring-builder": "^4.2.11", - "@smithy/types": "^4.13.0", + "@smithy/protocol-http": "^5.3.12", + "@smithy/querystring-builder": "^4.2.12", + "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" }, @@ -2781,12 +2816,12 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.11.tgz", - "integrity": "sha512-T+p1pNynRkydpdL015ruIoyPSRw9e/SQOWmSAMmmprfswMrd5Ow5igOWNVlvyVFZlxXqGmyH3NQwfwy8r5Jx0A==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.12.tgz", + "integrity": "sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" @@ -2808,12 +2843,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.11.tgz", - "integrity": "sha512-cGNMrgykRmddrNhYy1yBdrp5GwIgEkniS7k9O1VLB38yxQtlvrxpZtUVvo6T4cKpeZsriukBuuxfJcdZQc/f/g==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.12.tgz", + "integrity": "sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2822,8 +2857,6 @@ }, "node_modules/@smithy/is-array-buffer": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz", - "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -2845,13 +2878,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.11.tgz", - "integrity": "sha512-UvIfKYAKhCzr4p6jFevPlKhQwyQwlJ6IeKLDhmV1PlYfcW3RL4ROjNEDtSik4NYMi9kDkH7eSwyTP3vNJ/u/Dw==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.12.tgz", + "integrity": "sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2859,18 +2892,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.4.23", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.23.tgz", - "integrity": "sha512-UEFIejZy54T1EJn2aWJ45voB7RP2T+IRzUqocIdM6GFFa5ClZncakYJfcYnoXt3UsQrZZ9ZRauGm77l9UCbBLw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.23.9", - "@smithy/middleware-serde": "^4.2.12", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", - "@smithy/util-middleware": "^4.2.11", + "version": "4.4.26", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.26.tgz", + "integrity": "sha512-8Qfikvd2GVKSm8S6IbjfwFlRY9VlMrj0Dp4vTwAuhqbX7NhJKE5DQc2bnfJIcY0B+2YKMDBWfvexbSZeejDgeg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.12", + "@smithy/middleware-serde": "^4.2.15", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-middleware": "^4.2.12", "tslib": "^2.6.2" }, "engines": { @@ -2878,18 +2911,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.40", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.40.tgz", - "integrity": "sha512-YhEMakG1Ae57FajERdHNZ4ShOPIY7DsgV+ZoAxo/5BT0KIe+f6DDU2rtIymNNFIj22NJfeeI6LWIifrwM0f+rA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.11", - "@smithy/protocol-http": "^5.3.11", - "@smithy/service-error-classification": "^4.2.11", - "@smithy/smithy-client": "^4.12.3", - "@smithy/types": "^4.13.0", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-retry": "^4.2.11", + "version": "4.4.43", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.43.tgz", + "integrity": "sha512-ZwsifBdyuNHrFGmbc7bAfP2b54+kt9J2rhFd18ilQGAB+GDiP4SrawqyExbB7v455QVR7Psyhb2kjULvBPIhvA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/service-error-classification": "^4.2.12", + "@smithy/smithy-client": "^4.12.6", + "@smithy/types": "^4.13.1", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" }, @@ -2898,13 +2931,14 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.12.tgz", - "integrity": "sha512-W9g1bOLui7Xn5FABRVS0o3rXL0gfN37d/8I/W7i0N7oxjx9QecUmXEMSUMADTODwdtka9cN43t5BI2CodLJpng==", + "version": "4.2.15", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.15.tgz", + "integrity": "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", + "@smithy/core": "^3.23.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2912,12 +2946,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.11.tgz", - "integrity": "sha512-s+eenEPW6RgliDk2IhjD2hWOxIx1NKrOHxEwNUaUXxYBxIyCcDfNULZ2Mu15E3kwcJWBedTET/kEASPV1A1Akg==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.12.tgz", + "integrity": "sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2925,14 +2959,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.3.11", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.11.tgz", - "integrity": "sha512-xD17eE7kaLgBBGf5CZQ58hh2YmwK1Z0O8YhffwB/De2jsL0U3JklmhVYJ9Uf37OtUDLF2gsW40Xwwag9U869Gg==", + "version": "4.3.12", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.12.tgz", + "integrity": "sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.11", - "@smithy/shared-ini-file-loader": "^4.4.6", - "@smithy/types": "^4.13.0", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2940,15 +2974,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.4.14", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.14.tgz", - "integrity": "sha512-DamSqaU8nuk0xTJDrYnRzZndHwwRnyj/n/+RqGGCcBKB4qrQem0mSDiWdupaNWdwxzyMU91qxDmHOCazfhtO3A==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.5.0.tgz", + "integrity": "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.11", - "@smithy/protocol-http": "^5.3.11", - "@smithy/querystring-builder": "^4.2.11", - "@smithy/types": "^4.13.0", + "@smithy/abort-controller": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/querystring-builder": "^4.2.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2956,12 +2990,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.11.tgz", - "integrity": "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.12.tgz", + "integrity": "sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2969,12 +3003,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.3.11", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.11.tgz", - "integrity": "sha512-hI+barOVDJBkNt4y0L2mu3Ugc0w7+BpJ2CZuLwXtSltGAAwCb3IvnalGlbDV/UCS6a9ZuT3+exd1WxNdLb5IlQ==", + "version": "5.3.12", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.12.tgz", + "integrity": "sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2982,12 +3016,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.11.tgz", - "integrity": "sha512-7spdikrYiljpket6u0up2Ck2mxhy7dZ0+TDd+S53Dg2DHd6wg+YNJrTCHiLdgZmEXZKI7LJZcwL3721ZRDFiqA==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.12.tgz", + "integrity": "sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" }, @@ -2996,12 +3030,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.11.tgz", - "integrity": "sha512-nE3IRNjDltvGcoThD2abTozI1dkSy8aX+a2N1Rs55en5UsdyyIXgGEmevUL3okZFoJC77JgRGe99xYohhsjivQ==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.12.tgz", + "integrity": "sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -3009,24 +3043,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.11.tgz", - "integrity": "sha512-HkMFJZJUhzU3HvND1+Yw/kYWXp4RPDLBWLcK1n+Vqw8xn4y2YiBhdww8IxhkQjP/QlZun5bwm3vcHc8AqIU3zw==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.12.tgz", + "integrity": "sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0" + "@smithy/types": "^4.13.1" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.6.tgz", - "integrity": "sha512-IB/M5I8G0EeXZTHsAxpx51tMQ5R719F3aq+fjEB6VtNcCHDc0ajFDIGDZw+FW9GxtEkgTduiPpjveJdA/CX7sw==", + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.7.tgz", + "integrity": "sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -3034,16 +3068,16 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.3.11", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.11.tgz", - "integrity": "sha512-V1L6N9aKOBAN4wEHLyqjLBnAz13mtILU0SeDrjOaIZEeN6IFa6DxwRt1NNpOdmSpQUfkBj0qeD3m6P77uzMhgQ==", + "version": "5.3.12", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.12.tgz", + "integrity": "sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.2.2", - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", "@smithy/util-hex-encoding": "^4.2.2", - "@smithy/util-middleware": "^4.2.11", + "@smithy/util-middleware": "^4.2.12", "@smithy/util-uri-escape": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" @@ -3053,17 +3087,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.12.3", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.3.tgz", - "integrity": "sha512-7k4UxjSpHmPN2AxVhvIazRSzFQjWnud3sOsXcFStzagww17j1cFQYqTSiQ8xuYK3vKLR1Ni8FzuT3VlKr3xCNw==", + "version": "4.12.6", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.6.tgz", + "integrity": "sha512-aib3f0jiMsJ6+cvDnXipBsGDL7ztknYSVqJs1FdN9P+u9tr/VzOR7iygSh6EUOdaBeMCMSh3N0VdyYsG4o91DQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.9", - "@smithy/middleware-endpoint": "^4.4.23", - "@smithy/middleware-stack": "^4.2.11", - "@smithy/protocol-http": "^5.3.11", - "@smithy/types": "^4.13.0", - "@smithy/util-stream": "^4.5.17", + "@smithy/core": "^3.23.12", + "@smithy/middleware-endpoint": "^4.4.26", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" }, "engines": { @@ -3071,7 +3105,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.13.0", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.1.tgz", + "integrity": "sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3081,13 +3117,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.11.tgz", - "integrity": "sha512-oTAGGHo8ZYc5VZsBREzuf5lf2pAurJQsccMusVZ85wDkX66ojEc/XauiGjzCj50A61ObFTPe6d7Pyt6UBYaing==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.12.tgz", + "integrity": "sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.2.11", - "@smithy/types": "^4.13.0", + "@smithy/querystring-parser": "^4.2.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -3096,8 +3132,6 @@ }, "node_modules/@smithy/util-base64": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz", - "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^4.2.2", @@ -3110,8 +3144,6 @@ }, "node_modules/@smithy/util-body-length-browser": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz", - "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3122,8 +3154,6 @@ }, "node_modules/@smithy/util-body-length-node": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz", - "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3134,8 +3164,6 @@ }, "node_modules/@smithy/util-buffer-from": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz", - "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.2.2", @@ -3147,8 +3175,6 @@ }, "node_modules/@smithy/util-config-provider": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz", - "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3158,14 +3184,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.39", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.39.tgz", - "integrity": "sha512-ui7/Ho/+VHqS7Km2wBw4/Ab4RktoiSshgcgpJzC4keFPs6tLJS4IQwbeahxQS3E/w98uq6E1mirCH/id9xIXeQ==", + "version": "4.3.42", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.42.tgz", + "integrity": "sha512-0vjwmcvkWAUtikXnWIUOyV6IFHTEeQUYh3JUZcDgcszF+hD/StAsQ3rCZNZEPHgI9kVNcbnyc8P2CBHnwgmcwg==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.11", - "@smithy/smithy-client": "^4.12.3", - "@smithy/types": "^4.13.0", + "@smithy/property-provider": "^4.2.12", + "@smithy/smithy-client": "^4.12.6", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -3173,17 +3199,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.42", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.42.tgz", - "integrity": "sha512-QDA84CWNe8Akpj15ofLO+1N3Rfg8qa2K5uX0y6HnOp4AnRYRgWrKx/xzbYNbVF9ZsyJUYOfcoaN3y93wA/QJ2A==", + "version": "4.2.45", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.45.tgz", + "integrity": "sha512-q5dOqqfTgUcLe38TAGiFn9srToKj2YCHJ34QGOLzM+xYLLA+qRZv7N+33kl1MERVusue36ZHnlNaNEvY/PzSrw==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.4.10", - "@smithy/credential-provider-imds": "^4.2.11", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/property-provider": "^4.2.11", - "@smithy/smithy-client": "^4.12.3", - "@smithy/types": "^4.13.0", + "@smithy/config-resolver": "^4.4.11", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/smithy-client": "^4.12.6", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -3191,13 +3217,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.2.tgz", - "integrity": "sha512-+4HFLpE5u29AbFlTdlKIT7jfOzZ8PDYZKTb3e+AgLz986OYwqTourQ5H+jg79/66DB69Un1+qKecLnkZdAsYcA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.3.tgz", + "integrity": "sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.11", - "@smithy/types": "^4.13.0", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -3206,8 +3232,6 @@ }, "node_modules/@smithy/util-hex-encoding": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz", - "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3217,12 +3241,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.11.tgz", - "integrity": "sha512-r3dtF9F+TpSZUxpOVVtPfk09Rlo4lT6ORBqEvX3IBT6SkQAdDSVKR5GcfmZbtl7WKhKnmb3wbDTQ6ibR2XHClw==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.12.tgz", + "integrity": "sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -3230,13 +3254,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.11.tgz", - "integrity": "sha512-XSZULmL5x6aCTTii59wJqKsY1l3eMIAomRAccW7Tzh9r8s7T/7rdo03oektuH5jeYRlJMPcNP92EuRDvk9aXbw==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.12.tgz", + "integrity": "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.2.11", - "@smithy/types": "^4.13.0", + "@smithy/service-error-classification": "^4.2.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -3244,14 +3268,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.17", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.17.tgz", - "integrity": "sha512-793BYZ4h2JAQkNHcEnyFxDTcZbm9bVybD0UV/LEWmZ5bkTms7JqjfrLMi2Qy0E5WFcCzLwCAPgcvcvxoeALbAQ==", + "version": "4.5.20", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.20.tgz", + "integrity": "sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.13", - "@smithy/node-http-handler": "^4.4.14", - "@smithy/types": "^4.13.0", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/node-http-handler": "^4.5.0", + "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", @@ -3264,8 +3288,6 @@ }, "node_modules/@smithy/util-uri-escape": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", - "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3276,8 +3298,6 @@ }, "node_modules/@smithy/util-utf8": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", - "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^4.2.2", @@ -3288,11 +3308,13 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "4.2.9", + "version": "4.2.13", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.13.tgz", + "integrity": "sha512-2zdZ9DTHngRtcYxJK1GUDxruNr53kv5W2Lupe0LMU+Imr6ohQg8M2T14MNkj1Y0wS3FFwpgpGQyvuaMF7CiTmQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.9", - "@smithy/types": "^4.12.1", + "@smithy/abort-controller": "^4.2.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -3301,8 +3323,6 @@ }, "node_modules/@smithy/uuid": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz", - "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3426,6 +3446,13 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/crypto-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", + "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "dev": true, @@ -3733,18 +3760,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.9.2", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, "node_modules/abab": { "version": "2.0.6", "dev": true, @@ -4098,8 +4113,6 @@ }, "node_modules/astral-regex": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "license": "MIT", "engines": { "node": ">=8" @@ -4613,8 +4626,6 @@ }, "node_modules/collect-v8-coverage": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", - "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", "dev": true, "license": "MIT" }, @@ -4716,6 +4727,12 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, "node_modules/cssom": { "version": "0.5.0", "dev": true, @@ -6368,8 +6385,6 @@ }, "node_modules/flatted": { "version": "3.4.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", - "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", "dev": true, "license": "ISC" }, @@ -6406,18 +6421,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -8298,8 +8301,6 @@ }, "node_modules/lodash.truncate": { "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "license": "MIT" }, "node_modules/loose-envify": { @@ -9593,8 +9594,6 @@ }, "node_modules/slice-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -9918,8 +9917,6 @@ }, "node_modules/table": { "version": "6.9.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", - "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", "license": "BSD-3-Clause", "dependencies": { "ajv": "^8.0.1", @@ -9934,8 +9931,6 @@ }, "node_modules/table/node_modules/ajv": { "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -9950,8 +9945,6 @@ }, "node_modules/table/node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/test-exclude": { @@ -10263,21 +10256,6 @@ "fsevents": "~2.3.3" } }, - "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/tsx/node_modules/esbuild": { "version": "0.25.5", "dev": true, @@ -10923,8 +10901,6 @@ }, "node_modules/zod": { "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -10996,8 +10972,6 @@ }, "tools/client-subscriptions-management/node_modules/@aws-sdk/client-sts": { "version": "3.1005.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.1005.0.tgz", - "integrity": "sha512-TCCWkROc6akngO5595RBmD3Zeq9CFtoZa3TsugyvOoDEd0c/FFOnoAZ7TdTe3MLuvkbXH5yvN6hLyLQgytNIAg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", @@ -11046,8 +11020,6 @@ }, "tools/client-subscriptions-management/node_modules/@aws-sdk/util-endpoints": { "version": "3.996.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.4.tgz", - "integrity": "sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.5", @@ -11062,8 +11034,6 @@ }, "tools/client-subscriptions-management/node_modules/@types/node": { "version": "22.19.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", - "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", "dev": true, "license": "MIT", "dependencies": { @@ -11072,8 +11042,6 @@ }, "tools/client-subscriptions-management/node_modules/undici-types": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" } From c29d03d2ba00aac7bc670a970ba48f38224330ec Mon Sep 17 00:00:00 2001 From: Rhys Cox Date: Fri, 13 Mar 2026 11:59:14 +0000 Subject: [PATCH 02/17] CCM-15215 - Adjusted terraform config --- .../terraform/components/callbacks/README.md | 2 +- .../ssm_parameter_applications_map.tf | 20 ++++++++++++++++++- .../components/callbacks/variables.tf | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/infrastructure/terraform/components/callbacks/README.md b/infrastructure/terraform/components/callbacks/README.md index 88cbd6d3..3d860320 100644 --- a/infrastructure/terraform/components/callbacks/README.md +++ b/infrastructure/terraform/components/callbacks/README.md @@ -13,7 +13,7 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [applications\_map\_parameter\_name](#input\_applications\_map\_parameter\_name) | SSM Parameter Store path for the clientId-to-applicationId map used for payload signing | `string` | n/a | yes | +| [applications\_map\_parameter\_name](#input\_applications\_map\_parameter\_name) | SSM Parameter Store path for the clientId-to-applicationId map used for payload signing | `string` | `null` | no | | [aws\_account\_id](#input\_aws\_account\_id) | The AWS Account ID (numeric) | `string` | n/a | yes | | [clients](#input\_clients) | n/a |
list(object({
connection_name = string
destination_name = string
invocation_endpoint = string
invocation_rate_limit_per_second = optional(number, 10)
http_method = optional(string, "POST")
header_name = optional(string, "x-api-key")
header_value = string
client_detail = list(string)
}))
| `[]` | no | | [component](#input\_component) | The variable encapsulating the name of this component | `string` | `"callbacks"` | no | diff --git a/infrastructure/terraform/components/callbacks/ssm_parameter_applications_map.tf b/infrastructure/terraform/components/callbacks/ssm_parameter_applications_map.tf index f4aa4c67..0bd051f2 100644 --- a/infrastructure/terraform/components/callbacks/ssm_parameter_applications_map.tf +++ b/infrastructure/terraform/components/callbacks/ssm_parameter_applications_map.tf @@ -1,4 +1,4 @@ -resource "aws_ssm_parameter" "applications_map" { +resource "aws_ssm_parameter" "applications_map_ephemeral" { count = var.deploy_mock_webhook ? 1 : 0 name = local.applications_map_parameter_name @@ -8,4 +8,22 @@ resource "aws_ssm_parameter" "applications_map" { value = jsonencode({ "mock-client" = "mock-application-id" }) + + lifecycle { + ignore_changes = [value] + } +} + +resource "aws_ssm_parameter" "applications_map" { + count = var.deploy_mock_webhook ? 0 : 1 + + name = local.applications_map_parameter_name + type = "SecureString" + key_id = module.kms.key_arn + + value = jsonencode({}) + + lifecycle { + ignore_changes = [value] + } } diff --git a/infrastructure/terraform/components/callbacks/variables.tf b/infrastructure/terraform/components/callbacks/variables.tf index 87aa0a42..96f89d8e 100644 --- a/infrastructure/terraform/components/callbacks/variables.tf +++ b/infrastructure/terraform/components/callbacks/variables.tf @@ -182,5 +182,6 @@ variable "enable_debug_log_bucket" { variable "applications_map_parameter_name" { type = string + default = null description = "SSM Parameter Store path for the clientId-to-applicationId map used for payload signing" } From 504409b2435f0ed119a77289300cf3b4a7872200 Mon Sep 17 00:00:00 2001 From: Rhys Cox Date: Fri, 13 Mar 2026 13:49:41 +0000 Subject: [PATCH 03/17] CCM-15215 - PR feedback --- .../ssm_parameter_applications_map.tf | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/infrastructure/terraform/components/callbacks/ssm_parameter_applications_map.tf b/infrastructure/terraform/components/callbacks/ssm_parameter_applications_map.tf index 0bd051f2..1e9b6925 100644 --- a/infrastructure/terraform/components/callbacks/ssm_parameter_applications_map.tf +++ b/infrastructure/terraform/components/callbacks/ssm_parameter_applications_map.tf @@ -1,27 +1,11 @@ -resource "aws_ssm_parameter" "applications_map_ephemeral" { - count = var.deploy_mock_webhook ? 1 : 0 - - name = local.applications_map_parameter_name - type = "SecureString" - key_id = module.kms.key_arn - - value = jsonencode({ - "mock-client" = "mock-application-id" - }) - - lifecycle { - ignore_changes = [value] - } -} - resource "aws_ssm_parameter" "applications_map" { - count = var.deploy_mock_webhook ? 0 : 1 - name = local.applications_map_parameter_name type = "SecureString" key_id = module.kms.key_arn - value = jsonencode({}) + value = var.deploy_mock_webhook ? jsonencode({ + "mock-client" = "mock-application-id" + }) : jsonencode({}) lifecycle { ignore_changes = [value] From bc16beeae0c8a6e5d64724438fe861f4c19d6c69 Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 18 Mar 2026 09:59:30 +0000 Subject: [PATCH 04/17] Feedback: Set env vars in index.component.test before module import Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../src/__tests__/index.component.test.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts b/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts index 1e962e72..28904da4 100644 --- a/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts +++ b/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts @@ -26,6 +26,16 @@ jest.mock("@aws-sdk/client-ssm", () => { }; }); +// Set environment variables before importing the handler/module under test so that +// services constructed at module import time (e.g. applicationsMapService) see +// the correct configuration. +process.env.CLIENT_SUBSCRIPTION_CONFIG_BUCKET = "test-bucket"; +process.env.CLIENT_SUBSCRIPTION_CONFIG_PREFIX = "client_subscriptions/"; +process.env.CLIENT_SUBSCRIPTION_CACHE_TTL_SECONDS = "60"; +process.env.METRICS_NAMESPACE = "test-namespace"; +process.env.ENVIRONMENT = "test"; +process.env.APPLICATIONS_MAP_PARAMETER = "/test/applications-map"; + jest.mock("aws-embedded-metrics", () => ({ createMetricsLogger: jest.fn(() => ({ setNamespace: jest.fn(), @@ -112,14 +122,6 @@ const validMessageStatusEvent = (clientId: string, messageStatus: string) => ({ }); describe("Lambda handler with S3 subscription filtering", () => { - beforeAll(() => { - process.env.CLIENT_SUBSCRIPTION_CONFIG_BUCKET = "test-bucket"; - process.env.CLIENT_SUBSCRIPTION_CONFIG_PREFIX = "client_subscriptions/"; - process.env.CLIENT_SUBSCRIPTION_CACHE_TTL_SECONDS = "60"; - process.env.METRICS_NAMESPACE = "test-namespace"; - process.env.ENVIRONMENT = "test"; - process.env.APPLICATIONS_MAP_PARAMETER = "/test/applications-map"; - }); const applicationsMap = JSON.stringify({ "client-1": "app-id-1", From 0e630362c42c013ebd2b9788c08d150aaa2c89cb Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 18 Mar 2026 09:45:13 +0000 Subject: [PATCH 05/17] Feedback: Invalid JSON handling --- .../src/__tests__/index.component.test.ts | 11 ++++++++++- .../services/ssm-applications-map.test.ts | 18 ++++++++++++++++++ .../src/services/ssm-applications-map.ts | 12 ++++++++---- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts b/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts index 28904da4..f89f1f21 100644 --- a/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts +++ b/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts @@ -49,6 +49,16 @@ jest.mock("aws-embedded-metrics", () => ({ }, })); +// Set environment variables before importing the handler/module under test so that +// services constructed at module import time (e.g. applicationsMapService) see +// the correct configuration. +process.env.CLIENT_SUBSCRIPTION_CONFIG_BUCKET = "test-bucket"; +process.env.CLIENT_SUBSCRIPTION_CONFIG_PREFIX = "client_subscriptions/"; +process.env.CLIENT_SUBSCRIPTION_CACHE_TTL_SECONDS = "60"; +process.env.METRICS_NAMESPACE = "test-namespace"; +process.env.ENVIRONMENT = "test"; +process.env.APPLICATIONS_MAP_PARAMETER = "/test/applications-map"; + import { GetObjectCommand, NoSuchKey } from "@aws-sdk/client-s3"; import { GetParameterCommand } from "@aws-sdk/client-ssm"; import type { SQSRecord } from "aws-lambda"; @@ -122,7 +132,6 @@ const validMessageStatusEvent = (clientId: string, messageStatus: string) => ({ }); describe("Lambda handler with S3 subscription filtering", () => { - const applicationsMap = JSON.stringify({ "client-1": "app-id-1", "client-a": "app-id-a", diff --git a/lambdas/client-transform-filter-lambda/src/__tests__/services/ssm-applications-map.test.ts b/lambdas/client-transform-filter-lambda/src/__tests__/services/ssm-applications-map.test.ts index 6ec07353..ec6b92ac 100644 --- a/lambdas/client-transform-filter-lambda/src/__tests__/services/ssm-applications-map.test.ts +++ b/lambdas/client-transform-filter-lambda/src/__tests__/services/ssm-applications-map.test.ts @@ -1,6 +1,15 @@ import { GetParameterCommand, SSMClient } from "@aws-sdk/client-ssm"; import { ApplicationsMapService } from "services/ssm-applications-map"; +jest.mock("services/logger", () => ({ + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, +})); + const makeSsmClient = (value: string | undefined) => ({ send: jest @@ -88,6 +97,15 @@ describe("ApplicationsMapService", () => { ); }); + it("throws when SSM parameter contains invalid JSON", async () => { + const ssmClient = makeSsmClient("not valid json"); + const service = new ApplicationsMapService(ssmClient, "/test/param"); + + await expect(service.getApplicationId("client-1")).rejects.toThrow( + "SSM parameter '/test/param' contains invalid JSON", + ); + }); + it("reset clears the cache and forces reload on next call", async () => { const ssmClient = makeSsmClient(JSON.stringify({ "client-1": "app-id-1" })); const service = new ApplicationsMapService(ssmClient, "/test/param", 5000); diff --git a/lambdas/client-transform-filter-lambda/src/services/ssm-applications-map.ts b/lambdas/client-transform-filter-lambda/src/services/ssm-applications-map.ts index 8125fbf2..a71414fb 100644 --- a/lambdas/client-transform-filter-lambda/src/services/ssm-applications-map.ts +++ b/lambdas/client-transform-filter-lambda/src/services/ssm-applications-map.ts @@ -38,10 +38,14 @@ export class ApplicationsMapService { ); } - const parsed = JSON.parse(response.Parameter.Value) as Record< - string, - string - >; + let parsed: Record; + try { + parsed = JSON.parse(response.Parameter.Value) as Record; + } catch { + throw new Error( + `SSM parameter '${this.parameterName}' contains invalid JSON`, + ); + } this.cachedMap = new Map(Object.entries(parsed)); this.cacheExpiresAt = Date.now() + this.cacheTtlMs; logger.info("Applications map loaded from SSM", { From 61641c16d8102ef3a85287de1f0b9dd325efec15 Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 18 Mar 2026 10:12:40 +0000 Subject: [PATCH 06/17] Trivy: Override fast-xml-parser version --- package-lock.json | 331 +++++++++++++++++++++++++--------------------- 1 file changed, 181 insertions(+), 150 deletions(-) diff --git a/package-lock.json b/package-lock.json index 871500e0..6fc02487 100644 --- a/package-lock.json +++ b/package-lock.json @@ -142,6 +142,8 @@ }, "node_modules/@aws-crypto/crc32c": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -489,78 +491,65 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.996.0", + "version": "3.1011.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1011.0.tgz", + "integrity": "sha512-jY7CGX+vfM/DSi4K8UwaZKoXnhqchmAbKFB1kIuHMfPPqW7l3jC/fUVDb95/njMsB2ymYOTusZEzoCTeUB/4qA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.12", - "@aws-sdk/credential-provider-node": "^3.972.11", - "@aws-sdk/middleware-bucket-endpoint": "^3.972.3", - "@aws-sdk/middleware-expect-continue": "^3.972.3", - "@aws-sdk/middleware-flexible-checksums": "^3.972.10", - "@aws-sdk/middleware-host-header": "^3.972.3", - "@aws-sdk/middleware-location-constraint": "^3.972.3", - "@aws-sdk/middleware-logger": "^3.972.3", - "@aws-sdk/middleware-recursion-detection": "^3.972.3", - "@aws-sdk/middleware-sdk-s3": "^3.972.12", - "@aws-sdk/middleware-ssec": "^3.972.3", - "@aws-sdk/middleware-user-agent": "^3.972.12", - "@aws-sdk/region-config-resolver": "^3.972.3", - "@aws-sdk/signature-v4-multi-region": "3.996.0", - "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-endpoints": "3.996.0", - "@aws-sdk/util-user-agent-browser": "^3.972.3", - "@aws-sdk/util-user-agent-node": "^3.972.11", - "@smithy/config-resolver": "^4.4.6", - "@smithy/core": "^3.23.2", - "@smithy/eventstream-serde-browser": "^4.2.8", - "@smithy/eventstream-serde-config-resolver": "^4.3.8", - "@smithy/eventstream-serde-node": "^4.2.8", - "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/hash-blob-browser": "^4.2.9", - "@smithy/hash-node": "^4.2.8", - "@smithy/hash-stream-node": "^4.2.8", - "@smithy/invalid-dependency": "^4.2.8", - "@smithy/md5-js": "^4.2.8", - "@smithy/middleware-content-length": "^4.2.8", - "@smithy/middleware-endpoint": "^4.4.16", - "@smithy/middleware-retry": "^4.4.33", - "@smithy/middleware-serde": "^4.2.9", - "@smithy/middleware-stack": "^4.2.8", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/node-http-handler": "^4.4.10", - "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.11.5", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.32", - "@smithy/util-defaults-mode-node": "^4.2.35", - "@smithy/util-endpoints": "^3.2.8", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-retry": "^4.2.8", - "@smithy/util-stream": "^4.5.12", - "@smithy/util-utf8": "^4.2.0", - "@smithy/util-waiter": "^4.2.8", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-sdk-s3": "^3.972.12", - "@aws-sdk/types": "^3.973.1", - "@smithy/protocol-http": "^5.3.8", - "@smithy/signature-v4": "^5.3.8", - "@smithy/types": "^4.12.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/credential-provider-node": "^3.972.21", + "@aws-sdk/middleware-bucket-endpoint": "^3.972.8", + "@aws-sdk/middleware-expect-continue": "^3.972.8", + "@aws-sdk/middleware-flexible-checksums": "^3.974.0", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-location-constraint": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.8", + "@aws-sdk/middleware-sdk-s3": "^3.972.20", + "@aws-sdk/middleware-ssec": "^3.972.8", + "@aws-sdk/middleware-user-agent": "^3.972.21", + "@aws-sdk/region-config-resolver": "^3.972.8", + "@aws-sdk/signature-v4-multi-region": "^3.996.8", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.7", + "@smithy/config-resolver": "^4.4.11", + "@smithy/core": "^3.23.11", + "@smithy/eventstream-serde-browser": "^4.2.12", + "@smithy/eventstream-serde-config-resolver": "^4.3.12", + "@smithy/eventstream-serde-node": "^4.2.12", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-blob-browser": "^4.2.13", + "@smithy/hash-node": "^4.2.12", + "@smithy/hash-stream-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/md5-js": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.25", + "@smithy/middleware-retry": "^4.4.42", + "@smithy/middleware-serde": "^4.2.14", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.4.16", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.5", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.41", + "@smithy/util-defaults-mode-node": "^4.2.44", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.12", + "@smithy/util-stream": "^4.5.19", + "@smithy/util-utf8": "^4.2.2", + "@smithy/util-waiter": "^4.2.13", "tslib": "^2.6.2" }, "engines": { @@ -568,13 +557,15 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { - "version": "3.996.0", + "version": "3.996.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz", + "integrity": "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-endpoints": "^3.2.8", + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-endpoints": "^3.3.3", "tslib": "^2.6.2" }, "engines": { @@ -786,10 +777,12 @@ } }, "node_modules/@aws-sdk/crc64-nvme": { - "version": "3.972.0", + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.5.tgz", + "integrity": "sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -998,15 +991,17 @@ } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.972.3", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.8.tgz", + "integrity": "sha512-WR525Rr2QJSETa9a050isktyWi/4yIGcmY3BQ1kpHqb0LqUglQHCS8R27dTJxxWNZvQ0RVGtEZjTCbZJpyF3Aw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@aws-sdk/util-arn-parser": "^3.972.2", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-config-provider": "^4.2.0", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -1014,12 +1009,14 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.972.3", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.8.tgz", + "integrity": "sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -1027,22 +1024,24 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.972.10", + "version": "3.974.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.0.tgz", + "integrity": "sha512-BmdDjqvnuYaC4SY7ypHLXfCSsGYGUZkjCLSZyUAAYn1YT28vbNMJNDwhlfkvvE+hQHG5RJDlEmYuvBxcB9jX1g==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "^3.973.12", - "@aws-sdk/crc64-nvme": "3.972.0", - "@aws-sdk/types": "^3.973.1", - "@smithy/is-array-buffer": "^4.2.0", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-stream": "^4.5.12", - "@smithy/util-utf8": "^4.2.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/crc64-nvme": "^3.972.5", + "@aws-sdk/types": "^3.973.6", + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-stream": "^4.5.19", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -1065,11 +1064,13 @@ } }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.972.3", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.8.tgz", + "integrity": "sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@smithy/types": "^4.12.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -1107,22 +1108,24 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.972.16", + "version": "3.972.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.20.tgz", + "integrity": "sha512-yhva/xL5H4tWQgsBjwV+RRD0ByCzg0TcByDCLp3GXdn/wlyRNfy8zsswDtCvr1WSKQkSQYlyEzPuWkJG0f5HvQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.16", - "@aws-sdk/types": "^3.973.4", - "@aws-sdk/util-arn-parser": "^3.972.2", - "@smithy/core": "^3.23.7", - "@smithy/node-config-provider": "^4.3.10", - "@smithy/protocol-http": "^5.3.10", - "@smithy/signature-v4": "^5.3.10", - "@smithy/smithy-client": "^4.12.1", - "@smithy/types": "^4.13.0", - "@smithy/util-config-provider": "^4.2.1", - "@smithy/util-middleware": "^4.2.10", - "@smithy/util-stream": "^4.5.16", - "@smithy/util-utf8": "^4.2.1", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/core": "^3.23.11", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/smithy-client": "^4.12.5", + "@smithy/types": "^4.13.1", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-stream": "^4.5.19", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -1146,11 +1149,13 @@ } }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.972.3", + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.8.tgz", + "integrity": "sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.973.1", - "@smithy/types": "^4.12.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -1274,14 +1279,16 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.4", + "version": "3.996.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.8.tgz", + "integrity": "sha512-n1qYFD+tbqZuyskVaxUE+t10AUz9g3qzDw3Tp6QZDKmqsjfDmZBd4GIk2EKJJNtcCBtE5YiUjDYA+3djFAFBBg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "^3.972.16", - "@aws-sdk/types": "^3.973.4", - "@smithy/protocol-http": "^5.3.10", - "@smithy/signature-v4": "^5.3.10", - "@smithy/types": "^4.13.0", + "@aws-sdk/middleware-sdk-s3": "^3.972.20", + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -1320,7 +1327,9 @@ } }, "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.972.2", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.3.tgz", + "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -2652,7 +2661,9 @@ } }, "node_modules/@smithy/chunked-blob-reader": { - "version": "5.2.1", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.2.tgz", + "integrity": "sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -2662,10 +2673,12 @@ } }, "node_modules/@smithy/chunked-blob-reader-native": { - "version": "4.2.2", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.3.tgz", + "integrity": "sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-base64": "^4.3.1", + "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -2727,12 +2740,14 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "4.2.8", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.12.tgz", + "integrity": "sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.12.0", - "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/types": "^4.13.1", + "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -2740,11 +2755,13 @@ } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.2.8", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.12.tgz", + "integrity": "sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/eventstream-serde-universal": "^4.2.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2752,10 +2769,12 @@ } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.3.8", + "version": "4.3.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.12.tgz", + "integrity": "sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2763,11 +2782,13 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "4.2.8", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.12.tgz", + "integrity": "sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/eventstream-serde-universal": "^4.2.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2775,11 +2796,13 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.2.8", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.12.tgz", + "integrity": "sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^4.2.8", - "@smithy/types": "^4.12.0", + "@smithy/eventstream-codec": "^4.2.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2803,12 +2826,14 @@ } }, "node_modules/@smithy/hash-blob-browser": { - "version": "4.2.10", + "version": "4.2.13", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.13.tgz", + "integrity": "sha512-YrF4zWKh+ghLuquldj6e/RzE3xZYL8wIPfkt0MqCRphVICjyyjH8OwKD7LLlKpVEbk4FLizFfC1+gwK6XQdR3g==", "license": "Apache-2.0", "dependencies": { - "@smithy/chunked-blob-reader": "^5.2.1", - "@smithy/chunked-blob-reader-native": "^4.2.2", - "@smithy/types": "^4.12.1", + "@smithy/chunked-blob-reader": "^5.2.2", + "@smithy/chunked-blob-reader-native": "^4.2.3", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { @@ -2831,11 +2856,13 @@ } }, "node_modules/@smithy/hash-stream-node": { - "version": "4.2.9", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.12.tgz", + "integrity": "sha512-O3YbmGExeafuM/kP7Y8r6+1y0hIh3/zn6GROx0uNlB54K9oihAL75Qtc+jFfLNliTi6pxOAYZrRKD9A7iA6UFw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.1", - "@smithy/util-utf8": "^4.2.1", + "@smithy/types": "^4.13.1", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -2866,11 +2893,13 @@ } }, "node_modules/@smithy/md5-js": { - "version": "4.2.8", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.12.tgz", + "integrity": "sha512-W/oIpHCpWU2+iAkfZYyGWE+qkpuf3vEXHLxQQDx9FPNZTTdnul0dZ2d/gUFrtQ5je1G2kp4cjG0/24YueG2LbQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.12.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/types": "^4.13.1", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -3288,6 +3317,8 @@ }, "node_modules/@smithy/util-uri-escape": { "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", + "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" From 6f655ff42497de1abb1993c211acb20a5a68b9bb Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 18 Mar 2026 10:59:14 +0000 Subject: [PATCH 07/17] Add the headers to the event pipeline template --- .../terraform/components/callbacks/pipes_pipe_main.tf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/infrastructure/terraform/components/callbacks/pipes_pipe_main.tf b/infrastructure/terraform/components/callbacks/pipes_pipe_main.tf index e3c284ef..6c088133 100644 --- a/infrastructure/terraform/components/callbacks/pipes_pipe_main.tf +++ b/infrastructure/terraform/components/callbacks/pipes_pipe_main.tf @@ -26,7 +26,8 @@ resource "aws_pipes_pipe" "main" { input_template = <, - "transformedPayload": <$.transformedPayload> + "transformedPayload": <$.transformedPayload>, + "headers": <$.headers> } EOF } From c2e5a9d9491b66b4bf4e117f83c36fa40570e675 Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 18 Mar 2026 11:22:06 +0000 Subject: [PATCH 08/17] Feedback: validate APPLICATIONS_MAP_PARAMETER and cleanup application map construction --- .../services/ssm-applications-map.test.ts | 39 +++++++++++++++- .../src/index.ts | 20 +-------- .../src/services/ssm-applications-map.ts | 44 ++++++++++++++----- 3 files changed, 73 insertions(+), 30 deletions(-) diff --git a/lambdas/client-transform-filter-lambda/src/__tests__/services/ssm-applications-map.test.ts b/lambdas/client-transform-filter-lambda/src/__tests__/services/ssm-applications-map.test.ts index ec6b92ac..7123009a 100644 --- a/lambdas/client-transform-filter-lambda/src/__tests__/services/ssm-applications-map.test.ts +++ b/lambdas/client-transform-filter-lambda/src/__tests__/services/ssm-applications-map.test.ts @@ -1,5 +1,9 @@ import { GetParameterCommand, SSMClient } from "@aws-sdk/client-ssm"; -import { ApplicationsMapService } from "services/ssm-applications-map"; +import { + ApplicationsMapService, + createSsmClient, + resolveCacheTtlMs, +} from "services/ssm-applications-map"; jest.mock("services/logger", () => ({ logger: { @@ -86,6 +90,15 @@ describe("ApplicationsMapService", () => { ); }); + it("throws when APPLICATIONS_MAP_PARAMETER is not set", async () => { + const ssmClient = makeSsmClient(JSON.stringify({ "client-1": "app-id-1" })); + const service = new ApplicationsMapService(ssmClient, undefined); + + await expect(service.getApplicationId("client-1")).rejects.toThrow( + "APPLICATIONS_MAP_PARAMETER is required", + ); + }); + it("throws when SSM parameter has empty value", async () => { const ssmClient = { send: jest.fn().mockResolvedValue({ Parameter: { Value: "" } }), @@ -117,3 +130,27 @@ describe("ApplicationsMapService", () => { expect(ssmClient.send).toHaveBeenCalledTimes(2); }); }); + +describe("resolveCacheTtlMs", () => { + it("returns configured value in ms", () => { + expect( + resolveCacheTtlMs({ APPLICATIONS_MAP_CACHE_TTL_SECONDS: "30" }), + ).toBe(30_000); + }); + + it("returns default when env var is absent", () => { + expect(resolveCacheTtlMs({})).toBe(60_000); + }); + + it("returns default when env var is not a valid number", () => { + expect( + resolveCacheTtlMs({ APPLICATIONS_MAP_CACHE_TTL_SECONDS: "invalid" }), + ).toBe(60_000); + }); +}); + +describe("createSsmClient", () => { + it("returns an SSMClient instance", () => { + expect(createSsmClient({})).toBeInstanceOf(SSMClient); + }); +}); diff --git a/lambdas/client-transform-filter-lambda/src/index.ts b/lambdas/client-transform-filter-lambda/src/index.ts index 31fac0ec..818ad742 100644 --- a/lambdas/client-transform-filter-lambda/src/index.ts +++ b/lambdas/client-transform-filter-lambda/src/index.ts @@ -1,5 +1,4 @@ import type { SQSRecord } from "aws-lambda"; -import { SSMClient } from "@aws-sdk/client-ssm"; import { Logger, flushLogs } from "services/logger"; import { CallbackMetrics, createMetricLogger } from "services/metrics"; import { ObservabilityService } from "services/observability"; @@ -9,24 +8,7 @@ import { type TransformedEvent, processEvents } from "handler"; export const configLoaderService = new ConfigLoaderService(); -const DEFAULT_SSM_CACHE_TTL_SECONDS = 60; - -export const createSsmClient = ( - env: NodeJS.ProcessEnv = process.env, -): SSMClient => { - const endpoint = env.AWS_ENDPOINT_URL; - return new SSMClient({ endpoint }); -}; - -export const applicationsMapService = new ApplicationsMapService( - createSsmClient(), - process.env.APPLICATIONS_MAP_PARAMETER ?? "", - (Number.parseInt( - process.env.APPLICATIONS_MAP_CACHE_TTL_SECONDS ?? - `${DEFAULT_SSM_CACHE_TTL_SECONDS}`, - 10, - ) || DEFAULT_SSM_CACHE_TTL_SECONDS) * 1000, -); +export const applicationsMapService = new ApplicationsMapService(); export interface HandlerDependencies { createObservabilityService?: () => ObservabilityService; diff --git a/lambdas/client-transform-filter-lambda/src/services/ssm-applications-map.ts b/lambdas/client-transform-filter-lambda/src/services/ssm-applications-map.ts index a71414fb..87cead24 100644 --- a/lambdas/client-transform-filter-lambda/src/services/ssm-applications-map.ts +++ b/lambdas/client-transform-filter-lambda/src/services/ssm-applications-map.ts @@ -1,7 +1,27 @@ import { GetParameterCommand, SSMClient } from "@aws-sdk/client-ssm"; import { logger } from "services/logger"; -const DEFAULT_CACHE_TTL_MS = 60_000; +const DEFAULT_CACHE_TTL_SECONDS = 60; + +export const createSsmClient = ( + env: NodeJS.ProcessEnv = process.env, +): SSMClient => { + const endpoint = env.AWS_ENDPOINT_URL; + return new SSMClient({ endpoint }); +}; + +export const resolveCacheTtlMs = ( + env: NodeJS.ProcessEnv = process.env, +): number => { + const ttlSeconds = Number.parseInt( + env.APPLICATIONS_MAP_CACHE_TTL_SECONDS ?? `${DEFAULT_CACHE_TTL_SECONDS}`, + 10, + ); + return ( + (Number.isFinite(ttlSeconds) ? ttlSeconds : DEFAULT_CACHE_TTL_SECONDS) * + 1000 + ); +}; export class ApplicationsMapService { private cachedMap: Map | undefined; @@ -9,9 +29,10 @@ export class ApplicationsMapService { private cacheExpiresAt = 0; constructor( - private readonly ssmClient: SSMClient, - private readonly parameterName: string, - private readonly cacheTtlMs: number = DEFAULT_CACHE_TTL_MS, + private readonly ssmClient: SSMClient = createSsmClient(), + private readonly parameterName: string | undefined = process.env + .APPLICATIONS_MAP_PARAMETER, + private readonly cacheTtlMs: number = resolveCacheTtlMs(), ) {} async getApplicationId(clientId: string): Promise { @@ -20,6 +41,11 @@ export class ApplicationsMapService { } private async getMap(): Promise> { + if (!this.parameterName) { + throw new Error("APPLICATIONS_MAP_PARAMETER is required"); + } + const { parameterName } = this; + if (this.cachedMap && Date.now() < this.cacheExpiresAt) { logger.debug("Applications map loaded from cache"); return this.cachedMap; @@ -27,14 +53,14 @@ export class ApplicationsMapService { const response = await this.ssmClient.send( new GetParameterCommand({ - Name: this.parameterName, + Name: parameterName, WithDecryption: true, }), ); if (!response.Parameter?.Value) { throw new Error( - `SSM parameter '${this.parameterName}' not found or has no value`, + `SSM parameter '${parameterName}' not found or has no value`, ); } @@ -42,14 +68,12 @@ export class ApplicationsMapService { try { parsed = JSON.parse(response.Parameter.Value) as Record; } catch { - throw new Error( - `SSM parameter '${this.parameterName}' contains invalid JSON`, - ); + throw new Error(`SSM parameter '${parameterName}' contains invalid JSON`); } this.cachedMap = new Map(Object.entries(parsed)); this.cacheExpiresAt = Date.now() + this.cacheTtlMs; logger.info("Applications map loaded from SSM", { - parameterName: this.parameterName, + parameterName, }); return this.cachedMap; } From 7f9b2bd9b1dddf84050c6709726fcd7ac271cf84 Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 18 Mar 2026 12:12:04 +0000 Subject: [PATCH 09/17] Feedback: refactor client config load so cache can be used in both signing/filtering --- .../src/handler.ts | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/lambdas/client-transform-filter-lambda/src/handler.ts b/lambdas/client-transform-filter-lambda/src/handler.ts index 3c8190aa..8f38f101 100644 --- a/lambdas/client-transform-filter-lambda/src/handler.ts +++ b/lambdas/client-transform-filter-lambda/src/handler.ts @@ -2,6 +2,7 @@ import type { SQSRecord } from "aws-lambda"; import pMap from "p-map"; import type { ClientCallbackPayload, + ClientSubscriptionConfiguration, StatusPublishEvent, } from "@nhs-notify-client-callbacks/models"; import { validateStatusPublishEvent } from "services/validators/event-validator"; @@ -121,10 +122,12 @@ function processSingleEvent( }; } +type ClientConfigMap = Map; + async function signBatch( filteredEvents: UnsignedEvent[], applicationsMapService: ApplicationsMapService, - configLoader: ConfigLoader, + configByClientId: ClientConfigMap, stats: BatchStats, ): Promise { const results = await pMap( @@ -144,7 +147,7 @@ async function signBatch( return undefined; } - const clientConfig = await configLoader.loadClientConfig(clientId); + const clientConfig = configByClientId.get(clientId); const apiKey = clientConfig?.[0]?.Targets?.[0]?.APIKey?.HeaderValue; if (!apiKey) { stats.recordFiltered(); @@ -184,19 +187,12 @@ function recordDeliveryInitiated( } } -async function filterBatch( - transformedEvents: UnsignedEvent[], +async function loadClientConfigs( + events: UnsignedEvent[], configLoader: ConfigLoader, - observability: ObservabilityService, - stats: BatchStats, -): Promise { - observability.recordFilteringStarted({ batchSize: transformedEvents.length }); - - const uniqueClientIds = new Set( - transformedEvents.map((e) => e.data.clientId), - ); - - const configEntries = await pMap( +): Promise { + const uniqueClientIds = new Set(events.map((e) => e.data.clientId)); + const entries = await pMap( uniqueClientIds, async (clientId) => { const config = await configLoader.loadClientConfig(clientId); @@ -204,8 +200,16 @@ async function filterBatch( }, { concurrency: BATCH_CONCURRENCY }, ); + return new Map(entries); +} - const configByClientId = new Map(configEntries); +async function filterBatch( + transformedEvents: UnsignedEvent[], + configByClientId: ClientConfigMap, + observability: ObservabilityService, + stats: BatchStats, +): Promise { + observability.recordFilteringStarted({ batchSize: transformedEvents.length }); const filtered: UnsignedEvent[] = []; @@ -278,9 +282,14 @@ export async function processEvents( try { const transformedEvents = await transformBatch(event, observability, stats); - const filteredEvents = await filterBatch( + const configByClientId = await loadClientConfigs( transformedEvents, configLoader, + ); + + const filteredEvents = await filterBatch( + transformedEvents, + configByClientId, observability, stats, ); @@ -288,7 +297,7 @@ export async function processEvents( const signedEvents = await signBatch( filteredEvents, applicationsMapService, - configLoader, + configByClientId, stats, ); From c31683c94d2a0b86e0b0c3d17360eef41eb295e1 Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 18 Mar 2026 12:15:15 +0000 Subject: [PATCH 10/17] Feedback: replace crypto-js dependency --- lambdas/client-transform-filter-lambda/package.json | 2 -- .../src/__tests__/services/payload-signer.test.ts | 9 ++++----- .../src/services/payload-signer.ts | 9 ++++----- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/lambdas/client-transform-filter-lambda/package.json b/lambdas/client-transform-filter-lambda/package.json index 4a5a2221..ff3f544f 100644 --- a/lambdas/client-transform-filter-lambda/package.json +++ b/lambdas/client-transform-filter-lambda/package.json @@ -6,7 +6,6 @@ "@nhs-notify-client-callbacks/models": "*", "aws-embedded-metrics": "^4.2.1", "cloudevents": "^8.0.2", - "crypto-js": "^4.2.0", "esbuild": "^0.25.0", "p-map": "^4.0.0", "zod": "^4.1.13" @@ -14,7 +13,6 @@ "devDependencies": { "@tsconfig/node22": "^22.0.2", "@types/aws-lambda": "^8.10.148", - "@types/crypto-js": "^4.2.2", "@types/jest": "^29.5.14", "jest": "^29.7.0", "jest-mock-extended": "^3.0.7", diff --git a/lambdas/client-transform-filter-lambda/src/__tests__/services/payload-signer.test.ts b/lambdas/client-transform-filter-lambda/src/__tests__/services/payload-signer.test.ts index 368f5695..e1785d55 100644 --- a/lambdas/client-transform-filter-lambda/src/__tests__/services/payload-signer.test.ts +++ b/lambdas/client-transform-filter-lambda/src/__tests__/services/payload-signer.test.ts @@ -1,4 +1,4 @@ -import hmacsha256 from "crypto-js/hmac-sha256"; +import { createHmac } from "node:crypto"; import type { ClientCallbackPayload } from "@nhs-notify-client-callbacks/models"; import { signPayload } from "services/payload-signer"; @@ -11,10 +11,9 @@ describe("signPayload", () => { const applicationId = "app-id-1"; const apiKey = "api-key-1"; - const expected = hmacsha256( - JSON.stringify(payload), - `${applicationId}.${apiKey}`, - ).toString(); + const expected = createHmac("sha256", `${applicationId}.${apiKey}`) + .update(JSON.stringify(payload)) + .digest("hex"); expect(signPayload(payload, applicationId, apiKey)).toBe(expected); }); diff --git a/lambdas/client-transform-filter-lambda/src/services/payload-signer.ts b/lambdas/client-transform-filter-lambda/src/services/payload-signer.ts index f533c02a..cf69cac8 100644 --- a/lambdas/client-transform-filter-lambda/src/services/payload-signer.ts +++ b/lambdas/client-transform-filter-lambda/src/services/payload-signer.ts @@ -1,4 +1,4 @@ -import hmacsha256 from "crypto-js/hmac-sha256"; +import { createHmac } from "node:crypto"; import type { ClientCallbackPayload } from "@nhs-notify-client-callbacks/models"; export function signPayload( @@ -6,8 +6,7 @@ export function signPayload( applicationId: string, apiKey: string, ): string { - return hmacsha256( - JSON.stringify(payload), - `${applicationId}.${apiKey}`, - ).toString(); + return createHmac("sha256", `${applicationId}.${apiKey}`) + .update(JSON.stringify(payload)) + .digest("hex"); } From 4c2c81d19b69442b21e01db6312b73470809feb6 Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 18 Mar 2026 14:18:00 +0000 Subject: [PATCH 11/17] Log SQS send in integration tests at info level for easier debugging --- tests/integration/helpers/sqs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/helpers/sqs.ts b/tests/integration/helpers/sqs.ts index 20a905f7..49f70e78 100644 --- a/tests/integration/helpers/sqs.ts +++ b/tests/integration/helpers/sqs.ts @@ -70,7 +70,7 @@ export async function sendSqsEvent( queueUrl: string, event: T, ) { - logger.debug( + logger.info( `Sending SQS event to ${queueUrl} (eventId=${(event as any).id})`, ); return client.send( From 607c1881c84350edb5d4137d46ab7926f540744e Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 18 Mar 2026 14:34:40 +0000 Subject: [PATCH 12/17] Remove unnecessary deploy_mock_webhook override now we've configured the tfvar for it --- .github/workflows/cicd-1-pull-request.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd-1-pull-request.yaml b/.github/workflows/cicd-1-pull-request.yaml index 4dcd5dcc..cd4d89ef 100644 --- a/.github/workflows/cicd-1-pull-request.yaml +++ b/.github/workflows/cicd-1-pull-request.yaml @@ -171,7 +171,7 @@ jobs: --terraformAction "apply" \ --overrideProjectName "nhs" \ --overrideRoleName "nhs-main-acct-client-callbacks-github-deploy" \ - --overrides "branch_name=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}},deploy_mock_webhook=true" + --overrides "branch_name=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" acceptance-stage: # Recommended maximum execution time is 10 minutes name: "Acceptance stage" needs: [metadata, build-stage, pr-create-dynamic-environment] From 8756f844fa61ad35b546be7d7f8d0fca964eb738 Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 18 Mar 2026 14:45:48 +0000 Subject: [PATCH 13/17] fixup! Feedback: Set env vars in index.component.test before module import --- .../src/__tests__/index.component.test.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts b/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts index f89f1f21..dbacd922 100644 --- a/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts +++ b/lambdas/client-transform-filter-lambda/src/__tests__/index.component.test.ts @@ -49,16 +49,6 @@ jest.mock("aws-embedded-metrics", () => ({ }, })); -// Set environment variables before importing the handler/module under test so that -// services constructed at module import time (e.g. applicationsMapService) see -// the correct configuration. -process.env.CLIENT_SUBSCRIPTION_CONFIG_BUCKET = "test-bucket"; -process.env.CLIENT_SUBSCRIPTION_CONFIG_PREFIX = "client_subscriptions/"; -process.env.CLIENT_SUBSCRIPTION_CACHE_TTL_SECONDS = "60"; -process.env.METRICS_NAMESPACE = "test-namespace"; -process.env.ENVIRONMENT = "test"; -process.env.APPLICATIONS_MAP_PARAMETER = "/test/applications-map"; - import { GetObjectCommand, NoSuchKey } from "@aws-sdk/client-s3"; import { GetParameterCommand } from "@aws-sdk/client-ssm"; import type { SQSRecord } from "aws-lambda"; From 00292ff0804eeaaa340bebad92963ea027cd4046 Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 18 Mar 2026 14:50:45 +0000 Subject: [PATCH 14/17] Assert hmac signature is received in mock webhook --- .../src/__tests__/index.test.ts | 17 ++++++++++++++++- lambdas/mock-webhook-lambda/src/index.ts | 8 ++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lambdas/mock-webhook-lambda/src/__tests__/index.test.ts b/lambdas/mock-webhook-lambda/src/__tests__/index.test.ts index b11a8ea8..c7015e14 100644 --- a/lambdas/mock-webhook-lambda/src/__tests__/index.test.ts +++ b/lambdas/mock-webhook-lambda/src/__tests__/index.test.ts @@ -22,7 +22,10 @@ const mockLogger = jest.requireMock( "@nhs-notify-client-callbacks/logger", ).instance; -const DEFAULT_HEADERS = { "x-api-key": TEST_API_KEY }; +const DEFAULT_HEADERS = { + "x-api-key": TEST_API_KEY, + "x-hmac-sha256-signature": "abc123", +}; const createMockEvent = ( body: string | null, @@ -61,6 +64,18 @@ describe("Mock Webhook Lambda", () => { const body = JSON.parse(result.body); expect(body.message).toBe("Unauthorized"); }); + + it("should return 400 when x-hmac-sha256-signature header is missing", async () => { + const callback = { data: [] }; + const event = createMockEvent(JSON.stringify(callback), { + "x-api-key": TEST_API_KEY, + }); + const result = await handler(event); + + expect(result.statusCode).toBe(400); + const body = JSON.parse(result.body); + expect(body.message).toBe("Missing x-hmac-sha256-signature"); + }); }); describe("Happy Path", () => { diff --git a/lambdas/mock-webhook-lambda/src/index.ts b/lambdas/mock-webhook-lambda/src/index.ts index f7dfa18a..3cbb7b7a 100644 --- a/lambdas/mock-webhook-lambda/src/index.ts +++ b/lambdas/mock-webhook-lambda/src/index.ts @@ -52,6 +52,14 @@ async function buildResponse( }; } + if (!event.headers["x-hmac-sha256-signature"]) { + logger.error("Bad request: missing x-hmac-sha256-signature header"); + return { + statusCode: 400, + body: JSON.stringify({ message: "Missing x-hmac-sha256-signature" }), + }; + } + if (!event.body) { logger.error("No event body received"); From 5b48e6289a8664cdde716082af8edef7637eb366 Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 18 Mar 2026 15:04:45 +0000 Subject: [PATCH 15/17] Default project and component vars in ITs as they won't change --- tests/integration/helpers/deployment.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/integration/helpers/deployment.ts b/tests/integration/helpers/deployment.ts index d85832f1..f0ba00f2 100644 --- a/tests/integration/helpers/deployment.ts +++ b/tests/integration/helpers/deployment.ts @@ -9,19 +9,14 @@ export type DeploymentDetails = { export function getDeploymentDetails(): DeploymentDetails { const region = process.env.AWS_REGION ?? "eu-west-2"; const environment = process.env.ENVIRONMENT; - const project = process.env.PROJECT; - const component = process.env.COMPONENT; + const project = process.env.PROJECT ?? "nhs"; + const component = process.env.COMPONENT ?? "callbacks"; const accountId = process.env.AWS_ACCOUNT_ID; if (!environment) { throw new Error("ENVIRONMENT environment variable must be set"); } - if (!project) { - throw new Error("PROJECT environment variable must be set"); - } - if (!component) { - throw new Error("COMPONENT environment variable must be set"); - } + if (!accountId) { throw new Error("AWS_ACCOUNT_ID environment variable must be set"); } From 07b514e46d99d21c881929e631acf22efa2f85f5 Mon Sep 17 00:00:00 2001 From: Tim Marston Date: Fri, 20 Mar 2026 15:50:47 +0000 Subject: [PATCH 16/17] minor comment changes --- infrastructure/terraform/components/callbacks/README.md | 2 +- infrastructure/terraform/components/callbacks/variables.tf | 2 +- lambdas/mock-webhook-lambda/src/index.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/infrastructure/terraform/components/callbacks/README.md b/infrastructure/terraform/components/callbacks/README.md index 3d860320..c4e6d262 100644 --- a/infrastructure/terraform/components/callbacks/README.md +++ b/infrastructure/terraform/components/callbacks/README.md @@ -13,7 +13,7 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [applications\_map\_parameter\_name](#input\_applications\_map\_parameter\_name) | SSM Parameter Store path for the clientId-to-applicationId map used for payload signing | `string` | `null` | no | +| [applications\_map\_parameter\_name](#input\_applications\_map\_parameter\_name) | SSM Parameter Store path for the clientId-to-applicationData map, where applicationData is currently only the applicationId | `string` | `null` | no | | [aws\_account\_id](#input\_aws\_account\_id) | The AWS Account ID (numeric) | `string` | n/a | yes | | [clients](#input\_clients) | n/a |
list(object({
connection_name = string
destination_name = string
invocation_endpoint = string
invocation_rate_limit_per_second = optional(number, 10)
http_method = optional(string, "POST")
header_name = optional(string, "x-api-key")
header_value = string
client_detail = list(string)
}))
| `[]` | no | | [component](#input\_component) | The variable encapsulating the name of this component | `string` | `"callbacks"` | no | diff --git a/infrastructure/terraform/components/callbacks/variables.tf b/infrastructure/terraform/components/callbacks/variables.tf index 96f89d8e..b21d5697 100644 --- a/infrastructure/terraform/components/callbacks/variables.tf +++ b/infrastructure/terraform/components/callbacks/variables.tf @@ -183,5 +183,5 @@ variable "enable_debug_log_bucket" { variable "applications_map_parameter_name" { type = string default = null - description = "SSM Parameter Store path for the clientId-to-applicationId map used for payload signing" + description = "SSM Parameter Store path for the clientId-to-applicationData map, where applicationData is currently only the applicationId" } diff --git a/lambdas/mock-webhook-lambda/src/index.ts b/lambdas/mock-webhook-lambda/src/index.ts index 3cbb7b7a..3cf05b16 100644 --- a/lambdas/mock-webhook-lambda/src/index.ts +++ b/lambdas/mock-webhook-lambda/src/index.ts @@ -52,6 +52,7 @@ async function buildResponse( }; } + // TODO: verify the signature (CCM-15525) if (!event.headers["x-hmac-sha256-signature"]) { logger.error("Bad request: missing x-hmac-sha256-signature header"); return { From 0224e726c2b31e9f6c2f13238f92970f4cce0979 Mon Sep 17 00:00:00 2001 From: Tim Marston Date: Fri, 20 Mar 2026 15:57:22 +0000 Subject: [PATCH 17/17] remove TODO --- lambdas/mock-webhook-lambda/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lambdas/mock-webhook-lambda/src/index.ts b/lambdas/mock-webhook-lambda/src/index.ts index 3cf05b16..3cbb7b7a 100644 --- a/lambdas/mock-webhook-lambda/src/index.ts +++ b/lambdas/mock-webhook-lambda/src/index.ts @@ -52,7 +52,6 @@ async function buildResponse( }; } - // TODO: verify the signature (CCM-15525) if (!event.headers["x-hmac-sha256-signature"]) { logger.error("Bad request: missing x-hmac-sha256-signature header"); return {