Skip to content

Commit

Permalink
feat(signature-v4): presigner skips headers from hosting to query
Browse files Browse the repository at this point in the history
  • Loading branch information
AllanZhengYP committed Nov 19, 2020
1 parent 2cb016f commit f430d94
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 3 deletions.
24 changes: 24 additions & 0 deletions packages/signature-v4/src/SignatureV4.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,30 @@ describe("SignatureV4", () => {
});
});

it("should sign request without hoisting some headers", async () => {
const { query, headers } = await signer.presign(
{
...minimalRequest,
headers: {
...minimalRequest.headers,
"x-amz-not-hoisted": "test",
},
},
{ ...presigningOptions, unhoistableHeaders: new Set(["x-amz-not-hoisted"]) }
);
expect(query).toEqual({
[ALGORITHM_QUERY_PARAM]: ALGORITHM_IDENTIFIER,
[CREDENTIAL_QUERY_PARAM]: "foo/20000101/us-bar-1/foo/aws4_request",
[AMZ_DATE_QUERY_PARAM]: "20000101T000000Z",
[EXPIRES_QUERY_PARAM]: presigningOptions.expiresIn.toString(),
[SIGNED_HEADERS_QUERY_PARAM]: `${HOST_HEADER};x-amz-not-hoisted`,
[SIGNATURE_QUERY_PARAM]: "3c3ef586754b111e9528009710b797a07457d6a671058ba89041a06bab45f585",
});
expect(headers).toMatchObject({
"x-amz-not-hoisted": "test",
});
});

it("should support overriding region and service in the signer instance", async () => {
const signer = new SignatureV4({
...signerInit,
Expand Down
3 changes: 2 additions & 1 deletion packages/signature-v4/src/SignatureV4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export class SignatureV4 implements RequestPresigner, RequestSigner, StringSigne
signingDate = new Date(),
expiresIn = 3600,
unsignableHeaders,
unhoistableHeaders,
signableHeaders,
signingRegion,
signingService,
Expand All @@ -135,7 +136,7 @@ export class SignatureV4 implements RequestPresigner, RequestSigner, StringSigne
}

const scope = createScope(shortDate, region, signingService ?? this.service);
const request = moveHeadersToQuery(prepareRequest(originalRequest));
const request = moveHeadersToQuery(prepareRequest(originalRequest), { unhoistableHeaders });

if (credentials.sessionToken) {
request.query[TOKEN_QUERY_PARAM] = credentials.sessionToken;
Expand Down
31 changes: 31 additions & 0 deletions packages/signature-v4/src/moveHeadersToQuery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,35 @@ describe("moveHeadersToQuery", () => {
"X-Amz-Storage-Class": "STANDARD_IA",
});
});

it("should skip hoisting headers to the querystring supplied in unhoistedHeaders", () => {
const req = moveHeadersToQuery(
new HttpRequest({
...minimalRequest,
headers: {
Host: "www.example.com",
"X-Amz-Website-Redirect-Location": "/index.html",
Foo: "bar",
fizz: "buzz",
SNAP: "crackle, pop",
"X-Amz-Storage-Class": "STANDARD_IA",
},
}),
{
unhoistableHeaders: new Set(["x-amz-website-redirect-location"]),
}
);

expect(req.query).toEqual({
"X-Amz-Storage-Class": "STANDARD_IA",
});

expect(req.headers).toEqual({
Host: "www.example.com",
"X-Amz-Website-Redirect-Location": "/index.html",
Foo: "bar",
fizz: "buzz",
SNAP: "crackle, pop",
});
});
});
7 changes: 5 additions & 2 deletions packages/signature-v4/src/moveHeadersToQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import { cloneRequest } from "./cloneRequest";
/**
* @internal
*/
export function moveHeadersToQuery(request: HttpRequest): HttpRequest & { query: QueryParameterBag } {
export function moveHeadersToQuery(
request: HttpRequest,
options: { unhoistableHeaders?: Set<string> } = {}
): HttpRequest & { query: QueryParameterBag } {
const { headers, query = {} as QueryParameterBag } =
typeof (request as any).clone === "function" ? (request as any).clone() : cloneRequest(request);
for (const name of Object.keys(headers)) {
const lname = name.toLowerCase();
if (lname.substr(0, 6) === "x-amz-") {
if (lname.substr(0, 6) === "x-amz-" && !options.unhoistableHeaders?.has(lname)) {
query[name] = headers[name];
delete headers[name];
}
Expand Down
11 changes: 11 additions & 0 deletions packages/types/src/signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ export interface RequestPresigningArguments extends RequestSigningArguments {
* The number of seconds before the presigned URL expires
*/
expiresIn?: number;

/**
* A set of string whose members represents headers that should not be hoisted
* to presigned request's query string. If not supplied, the presigner will
* move all the AWS-specific headers(starting with `x-amz-`) to the request
* query string. If supplied, these headers remain in the presigned request's
* header.
* All headers in the provided request will have their names converted to
* lower case and then checked for existence in the unhoistableHeaders set.
*/
unhoistableHeaders?: Set<string>;
}

export interface EventSigningArguments extends SigningArguments {
Expand Down

0 comments on commit f430d94

Please sign in to comment.