From 943e8f402c291be927374c24b6c56262aa348a36 Mon Sep 17 00:00:00 2001 From: r-izumida <73263876+r-izumida@users.noreply.github.com> Date: Sat, 14 Sep 2024 19:41:48 +0900 Subject: [PATCH] fix: sort query parameter keys after encoding --- .changeset/ninety-hairs-obey.md | 5 +++++ .../signature-v4/src/getCanonicalQuery.spec.ts | 11 +++++++++++ packages/signature-v4/src/getCanonicalQuery.ts | 15 +++++++-------- 3 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 .changeset/ninety-hairs-obey.md diff --git a/.changeset/ninety-hairs-obey.md b/.changeset/ninety-hairs-obey.md new file mode 100644 index 00000000000..bdecc7d3630 --- /dev/null +++ b/.changeset/ninety-hairs-obey.md @@ -0,0 +1,5 @@ +--- +"@smithy/signature-v4": patch +--- + +fix: sort query parameter keys after encoding diff --git a/packages/signature-v4/src/getCanonicalQuery.spec.ts b/packages/signature-v4/src/getCanonicalQuery.spec.ts index 3a29b9861b1..15b68a6daca 100644 --- a/packages/signature-v4/src/getCanonicalQuery.spec.ts +++ b/packages/signature-v4/src/getCanonicalQuery.spec.ts @@ -37,6 +37,17 @@ describe("getCanonicalQuery", () => { ).toBe("baz=quux&fizz=buzz&foo=bar"); }); + it("should sort query keys alphabetically after URI-encode", () => { + expect( + getCanonicalQuery( + new HttpRequest({ + ...httpRequestOptions, + query: { A: "65", "[": "91", a: "97", "{": "123" }, + }) + ) + ).toBe("%5B=91&%7B=123&A=65&a=97"); + }); + it("should URI-encode keys and values", () => { expect( getCanonicalQuery( diff --git a/packages/signature-v4/src/getCanonicalQuery.ts b/packages/signature-v4/src/getCanonicalQuery.ts index 8bdc9633ee8..a4f70a5cee4 100644 --- a/packages/signature-v4/src/getCanonicalQuery.ts +++ b/packages/signature-v4/src/getCanonicalQuery.ts @@ -9,28 +9,27 @@ import { SIGNATURE_HEADER } from "./constants"; export const getCanonicalQuery = ({ query = {} }: HttpRequest): string => { const keys: Array = []; const serialized: Record = {}; - for (const key of Object.keys(query).sort()) { + for (const key of Object.keys(query)) { if (key.toLowerCase() === SIGNATURE_HEADER) { continue; } - keys.push(key); + const encodedKey = escapeUri(key); + keys.push(encodedKey); const value = query[key]; if (typeof value === "string") { - serialized[key] = `${escapeUri(key)}=${escapeUri(value)}`; + serialized[encodedKey] = `${encodedKey}=${escapeUri(value)}`; } else if (Array.isArray(value)) { - serialized[key] = value + serialized[encodedKey] = value .slice(0) - .reduce( - (encoded: Array, value: string) => encoded.concat([`${escapeUri(key)}=${escapeUri(value)}`]), - [] - ) + .reduce((encoded: Array, value: string) => encoded.concat([`${encodedKey}=${escapeUri(value)}`]), []) .sort() .join("&"); } } return keys + .sort() .map((key) => serialized[key]) .filter((serialized) => serialized) // omit any falsy values .join("&");