Skip to content

Commit

Permalink
fix(signature-v4): normalized the path before double encoding it (#3408)
Browse files Browse the repository at this point in the history
  • Loading branch information
AllanZhengYP committed Mar 10, 2022
1 parent a4cc40a commit b20d431
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 5 deletions.
35 changes: 32 additions & 3 deletions packages/signature-v4/src/SignatureV4.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,19 @@ describe("SignatureV4", () => {
expect(query[SIGNATURE_QUERY_PARAM]).toBe("6267d8b6f44d165d2b9f4d2c2b45fd6971de0962820243669bf685818c9c7849");
});

it("should normalize relative path by default", async () => {
const { query = {} } = await signer.presign(
{ ...minimalRequest, path: "/abc/../foo%3Dbar" },
presigningOptions
);
expect(query[SIGNATURE_QUERY_PARAM]).toBe("6267d8b6f44d165d2b9f4d2c2b45fd6971de0962820243669bf685818c9c7849");
});

it("should normalize path with consecutive slashes by default", async () => {
const { query = {} } = await signer.presign({ ...minimalRequest, path: "//foo%3Dbar" }, presigningOptions);
expect(query[SIGNATURE_QUERY_PARAM]).toBe("6267d8b6f44d165d2b9f4d2c2b45fd6971de0962820243669bf685818c9c7849");
});

it("should not URI-encode the path if URI path escaping was disabled on the signer", async () => {
// Setting `uriEscapePath` to `false` creates an
// S3-compatible signer. The expected signature included
Expand Down Expand Up @@ -573,15 +586,31 @@ describe("SignatureV4", () => {
hostname: "foo.us-bar-1.amazonaws.com",
});

const signingOptions = {
signingDate: new Date("2000-01-01T00:00:00.000Z"),
};

it("should URI-encode the path by default", async () => {
const { headers } = await signer.sign(minimalRequest, {
signingDate: new Date("2000-01-01T00:00:00.000Z"),
});
const { headers } = await signer.sign(minimalRequest, signingOptions);
expect(headers[AUTH_HEADER]).toBe(
"AWS4-HMAC-SHA256 Credential=foo/20000101/us-bar-1/foo/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=fb4948cab44a9c47ce3b1a2489d01ec939fea9e79eccdb4593c11a94f207e075"
);
});

it("should normalize relative path by default", async () => {
const { headers } = await signer.sign({ ...minimalRequest, path: "/abc/../foo%3Dbar" }, signingOptions);
expect(headers[AUTH_HEADER]).toEqual(
expect.stringContaining("Signature=fb4948cab44a9c47ce3b1a2489d01ec939fea9e79eccdb4593c11a94f207e075")
);
});

it("should normalize path with consecutive slashes by default", async () => {
const { headers } = await signer.sign({ ...minimalRequest, path: "//foo%3Dbar" }, signingOptions);
expect(headers[AUTH_HEADER]).toEqual(
expect.stringContaining("Signature=fb4948cab44a9c47ce3b1a2489d01ec939fea9e79eccdb4593c11a94f207e075")
);
});

it("should not URI-encode the path if URI path escaping was disabled on the signer", async () => {
// Setting `uriEscapePath` to `false` creates an
// S3-compatible signer. The expected authorization header
Expand Down
24 changes: 22 additions & 2 deletions packages/signature-v4/src/SignatureV4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,30 @@ ${toHex(hashedRequest)}`;

private getCanonicalPath({ path }: HttpRequest): string {
if (this.uriEscapePath) {
const doubleEncoded = encodeURIComponent(path.replace(/^\//, ""));
return `/${doubleEncoded.replace(/%2F/g, "/")}`;
// Non-S3 services, we normalize the path and then double URI encode it.
// Ref: "Remove Dot Segments" https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
const normalizedPathSegments = [];
for (const pathSegment of path.split("/")) {
if (pathSegment?.length === 0) continue;
if (pathSegment === ".") continue;
if (pathSegment === "..") {
normalizedPathSegments.pop();
} else {
normalizedPathSegments.push(pathSegment);
}
}
// Joining by single slashes to remove consecutive slashes.
const normalizedPath = `${path?.startsWith("/") ? "/" : ""}${normalizedPathSegments.join("/")}${
normalizedPathSegments.length > 0 && path?.endsWith("/") ? "/" : ""
}`;

const doubleEncoded = encodeURIComponent(normalizedPath);
return doubleEncoded.replace(/%2F/g, "/");
}

// For S3, we shouldn't normalize the path. For example, object name
// my-object//example//photo.user should not be normalized to
// my-object/example/photo.user
return path;
}

Expand Down

0 comments on commit b20d431

Please sign in to comment.