From 79ef1545d37390e97df8ccdef56313f4e4784460 Mon Sep 17 00:00:00 2001 From: George Fu Date: Wed, 26 Jun 2024 15:51:45 +0000 Subject: [PATCH] fix(eventstream-handler-node): add system clock offset to event signing streams --- .../src/EventSigningStream.spec.ts | 3 ++- .../src/EventSigningStream.ts | 7 +++++-- .../src/EventStreamPayloadHandler.spec.ts | 2 ++ .../src/EventStreamPayloadHandler.ts | 4 ++++ packages/eventstream-handler-node/src/provider.ts | 1 + .../src/EventStreamPayloadHandler.spec.ts | 14 ++++++++++++-- .../src/EventStreamPayloadHandler.ts | 6 +++++- .../src/eventstream-payload-handler-provider.ts | 1 + .../src/get-event-signing-stream.spec.ts | 3 ++- .../src/get-event-signing-stream.ts | 9 ++++++--- 10 files changed, 40 insertions(+), 10 deletions(-) diff --git a/packages/eventstream-handler-node/src/EventSigningStream.spec.ts b/packages/eventstream-handler-node/src/EventSigningStream.spec.ts index 964d062c63677..0b6903f1cb621 100644 --- a/packages/eventstream-handler-node/src/EventSigningStream.spec.ts +++ b/packages/eventstream-handler-node/src/EventSigningStream.spec.ts @@ -11,7 +11,7 @@ describe("EventSigningStream", () => { Date = originalDate; }); - it("should sign a eventstream payload properly", (done) => { + it("should sign an eventstream payload properly", (done) => { const eventStreamCodec = new EventStreamCodec(toUtf8, fromUtf8); const message1: Message = { headers: {}, @@ -62,6 +62,7 @@ describe("EventSigningStream", () => { signMessage: mockMessageSigner, }, eventStreamCodec, + systemClockOffsetProvider: async () => 0, }); const output: Array = []; signingStream.on("data", (chunk) => { diff --git a/packages/eventstream-handler-node/src/EventSigningStream.ts b/packages/eventstream-handler-node/src/EventSigningStream.ts index e45ef1a30c3a9..d8546dba47ab5 100644 --- a/packages/eventstream-handler-node/src/EventSigningStream.ts +++ b/packages/eventstream-handler-node/src/EventSigningStream.ts @@ -1,5 +1,5 @@ import { EventStreamCodec } from "@smithy/eventstream-codec"; -import { MessageHeaders, MessageSigner } from "@smithy/types"; +import { MessageHeaders, MessageSigner, Provider } from "@smithy/types"; import { Transform, TransformCallback, TransformOptions } from "stream"; /** @@ -9,6 +9,7 @@ export interface EventSigningStreamOptions extends TransformOptions { priorSignature: string; messageSigner: MessageSigner; eventStreamCodec: EventStreamCodec; + systemClockOffsetProvider: Provider; } /** @@ -20,6 +21,7 @@ export class EventSigningStream extends Transform { private priorSignature: string; private messageSigner: MessageSigner; private eventStreamCodec: EventStreamCodec; + private readonly systemClockOffsetProvider: Provider; constructor(options: EventSigningStreamOptions) { super({ @@ -32,11 +34,12 @@ export class EventSigningStream extends Transform { this.priorSignature = options.priorSignature; this.eventStreamCodec = options.eventStreamCodec; this.messageSigner = options.messageSigner; + this.systemClockOffsetProvider = options.systemClockOffsetProvider; } async _transform(chunk: Uint8Array, encoding: string, callback: TransformCallback): Promise { try { - const now = new Date(); + const now = new Date(new Date().getTime() + (await this.systemClockOffsetProvider())); const dateHeader: MessageHeaders = { ":date": { type: "timestamp", value: now }, }; diff --git a/packages/eventstream-handler-node/src/EventStreamPayloadHandler.spec.ts b/packages/eventstream-handler-node/src/EventStreamPayloadHandler.spec.ts index a37ef5e160cae..d2a774126c6a6 100644 --- a/packages/eventstream-handler-node/src/EventStreamPayloadHandler.spec.ts +++ b/packages/eventstream-handler-node/src/EventStreamPayloadHandler.spec.ts @@ -90,6 +90,7 @@ describe(EventStreamPayloadHandler.name, () => { priorSignature, eventStreamCodec: expect.anything(), messageSigner: expect.anything(), + systemClockOffsetProvider: expect.any(Function), }); }); @@ -121,6 +122,7 @@ describe(EventStreamPayloadHandler.name, () => { priorSignature, eventStreamCodec: expect.anything(), messageSigner: expect.anything(), + systemClockOffsetProvider: expect.any(Function), }); }); diff --git a/packages/eventstream-handler-node/src/EventStreamPayloadHandler.ts b/packages/eventstream-handler-node/src/EventStreamPayloadHandler.ts index 60fb215b4f2ea..c750f9158fa6d 100644 --- a/packages/eventstream-handler-node/src/EventStreamPayloadHandler.ts +++ b/packages/eventstream-handler-node/src/EventStreamPayloadHandler.ts @@ -23,6 +23,7 @@ export interface EventStreamPayloadHandlerOptions { messageSigner: Provider; utf8Encoder: Encoder; utf8Decoder: Decoder; + systemClockOffset?: number; } /** @@ -37,10 +38,12 @@ export interface EventStreamPayloadHandlerOptions { export class EventStreamPayloadHandler implements IEventStreamPayloadHandler { private readonly messageSigner: Provider; private readonly eventStreamCodec: EventStreamCodec; + private readonly systemClockOffsetProvider: Provider; constructor(options: EventStreamPayloadHandlerOptions) { this.messageSigner = options.messageSigner; this.eventStreamCodec = new EventStreamCodec(options.utf8Encoder, options.utf8Decoder); + this.systemClockOffsetProvider = async () => options.systemClockOffset ?? 0; } async handle( @@ -79,6 +82,7 @@ export class EventStreamPayloadHandler implements IEventStreamPayloadHandler { priorSignature, eventStreamCodec: this.eventStreamCodec, messageSigner: await this.messageSigner(), + systemClockOffsetProvider: this.systemClockOffsetProvider, }); pipeline(payloadStream, signingStream, request.body, (err: NodeJS.ErrnoException | null) => { diff --git a/packages/eventstream-handler-node/src/provider.ts b/packages/eventstream-handler-node/src/provider.ts index bd0706281ea8b..63a4cddc3a8e9 100644 --- a/packages/eventstream-handler-node/src/provider.ts +++ b/packages/eventstream-handler-node/src/provider.ts @@ -14,4 +14,5 @@ export const eventStreamPayloadHandlerProvider: EventStreamPayloadHandlerProvide utf8Encoder: Encoder; utf8Decoder: Decoder; messageSigner: Provider; + systemClockOffset?: number; }) => new EventStreamPayloadHandler(options); diff --git a/packages/middleware-websocket/src/EventStreamPayloadHandler.spec.ts b/packages/middleware-websocket/src/EventStreamPayloadHandler.spec.ts index 117ab62ab33c5..f4975d7583d0a 100644 --- a/packages/middleware-websocket/src/EventStreamPayloadHandler.spec.ts +++ b/packages/middleware-websocket/src/EventStreamPayloadHandler.spec.ts @@ -91,7 +91,12 @@ describe(EventStreamPayloadHandler.name, () => { }); expect(getEventSigningTransformStream).toHaveBeenCalledTimes(1); - expect(getEventSigningTransformStream).toHaveBeenCalledWith(priorSignature, expect.anything(), expect.anything()); + expect(getEventSigningTransformStream).toHaveBeenCalledWith( + priorSignature, + expect.anything(), + expect.anything(), + expect.anything() + ); }); it("should call event signer with request signature from query string if no signature headers are found", async () => { @@ -118,7 +123,12 @@ describe(EventStreamPayloadHandler.name, () => { }); expect(getEventSigningTransformStream).toHaveBeenCalledTimes(1); - expect(getEventSigningTransformStream).toHaveBeenCalledWith(priorSignature, expect.anything(), expect.anything()); + expect(getEventSigningTransformStream).toHaveBeenCalledWith( + priorSignature, + expect.anything(), + expect.anything(), + expect.anything() + ); }); it("should start piping to request payload through event signer if downstream middleware returns", async () => { diff --git a/packages/middleware-websocket/src/EventStreamPayloadHandler.ts b/packages/middleware-websocket/src/EventStreamPayloadHandler.ts index 5551a59fc4fb1..f33dbcbc7dea3 100644 --- a/packages/middleware-websocket/src/EventStreamPayloadHandler.ts +++ b/packages/middleware-websocket/src/EventStreamPayloadHandler.ts @@ -19,6 +19,7 @@ export interface EventStreamPayloadHandlerOptions { messageSigner: Provider; utf8Encoder: Encoder; utf8Decoder: Decoder; + systemClockOffset?: number; } /** @@ -31,10 +32,12 @@ export interface EventStreamPayloadHandlerOptions { export class EventStreamPayloadHandler implements IEventStreamPayloadHandler { private readonly messageSigner: Provider; private readonly eventStreamCodec: EventStreamCodec; + private readonly systemClockOffsetProvider: Provider; constructor(options: EventStreamPayloadHandlerOptions) { this.messageSigner = options.messageSigner; this.eventStreamCodec = new EventStreamCodec(options.utf8Encoder, options.utf8Decoder); + this.systemClockOffsetProvider = async () => options.systemClockOffset ?? 0; } async handle( @@ -69,7 +72,8 @@ export class EventStreamPayloadHandler implements IEventStreamPayloadHandler { const signingStream = getEventSigningTransformStream( priorSignature, await this.messageSigner(), - this.eventStreamCodec + this.eventStreamCodec, + this.systemClockOffsetProvider ); const signedPayload = payload.pipeThrough(signingStream); diff --git a/packages/middleware-websocket/src/eventstream-payload-handler-provider.ts b/packages/middleware-websocket/src/eventstream-payload-handler-provider.ts index e3d1b6ae87363..b5bb7f3fca60b 100644 --- a/packages/middleware-websocket/src/eventstream-payload-handler-provider.ts +++ b/packages/middleware-websocket/src/eventstream-payload-handler-provider.ts @@ -7,4 +7,5 @@ export const eventStreamPayloadHandlerProvider: EventStreamPayloadHandlerProvide utf8Encoder: Encoder; utf8Decoder: Decoder; messageSigner: Provider; + systemClockOffset?: number; }) => new EventStreamPayloadHandler(options); diff --git a/packages/middleware-websocket/src/get-event-signing-stream.spec.ts b/packages/middleware-websocket/src/get-event-signing-stream.spec.ts index d88f98d47c92b..1f82801f068e2 100644 --- a/packages/middleware-websocket/src/get-event-signing-stream.spec.ts +++ b/packages/middleware-websocket/src/get-event-signing-stream.spec.ts @@ -80,7 +80,8 @@ describe(getEventSigningTransformStream.name, () => { sign: mockMessageSigner, signMessage: mockMessageSigner, }, - eventStreamCodec + eventStreamCodec, + 0 ); const output: Array = []; diff --git a/packages/middleware-websocket/src/get-event-signing-stream.ts b/packages/middleware-websocket/src/get-event-signing-stream.ts index 9da5909797d0d..dc463916da8e0 100644 --- a/packages/middleware-websocket/src/get-event-signing-stream.ts +++ b/packages/middleware-websocket/src/get-event-signing-stream.ts @@ -1,23 +1,26 @@ import { EventStreamCodec } from "@smithy/eventstream-codec"; -import { MessageHeaders, MessageSigner } from "@smithy/types"; +import { MessageHeaders, MessageSigner, Provider } from "@smithy/types"; import { fromHex } from "@smithy/util-hex-encoding"; /** * Get a transform stream that signs the eventstream * Implementation replicated from @aws-sdk/eventstream-handler-node::EventSigningStream * but modified to be compatible with WHATWG stream interface + * + * @internal */ export const getEventSigningTransformStream = ( initialSignature: string, messageSigner: MessageSigner, - eventStreamCodec: EventStreamCodec + eventStreamCodec: EventStreamCodec, + systemClockOffsetProvider: Provider ): TransformStream => { let priorSignature = initialSignature; const transformer: Transformer = { start() {}, async transform(chunk, controller) { try { - const now = new Date(); + const now = new Date(new Date().getTime() + (await systemClockOffsetProvider())); const dateHeader: MessageHeaders = { ":date": { type: "timestamp", value: now }, };