Skip to content

Commit 065eb7f

Browse files
authored
feat(signature-v4): validate credential is valid before signing (#3892)
1 parent 03950fa commit 065eb7f

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

packages/signature-v4/src/SignatureV4.spec.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@ describe("SignatureV4", () => {
5454
signingDate: new Date("2000-01-01T00:00:00.000Z"),
5555
};
5656

57+
it("should throw on invalid credential", async () => {
58+
const signer = new SignatureV4({ ...signerInit, credentials: {} as any });
59+
try {
60+
await signer.presign(minimalRequest, presigningOptions);
61+
fail("This test is expected to fail");
62+
} catch (e) {
63+
expect(e.message).toBe("Resolved credential object is not valid");
64+
}
65+
});
66+
5767
it("should sign requests without bodies", async () => {
5868
const { query } = await signer.presign(minimalRequest, presigningOptions);
5969
expect(query).toEqual({
@@ -364,6 +374,16 @@ describe("SignatureV4", () => {
364374
});
365375

366376
describe("#sign (request)", () => {
377+
it("should throw on invalid credential", async () => {
378+
const signer = new SignatureV4({ ...signerInit, credentials: {} as any });
379+
try {
380+
await signer.sign(minimalRequest);
381+
fail("This test is expected to fail");
382+
} catch (e) {
383+
expect(e.message).toBe("Resolved credential object is not valid");
384+
}
385+
});
386+
367387
it("should sign requests without bodies", async () => {
368388
const { headers } = await signer.sign(minimalRequest, {
369389
signingDate: new Date("2000-01-01T00:00:00.000Z"),
@@ -658,6 +678,16 @@ describe("SignatureV4", () => {
658678
sha256: Sha256,
659679
};
660680

681+
it("should throw on invalid credential", async () => {
682+
const signer = new SignatureV4({ ...signerInit, credentials: {} as any });
683+
try {
684+
await signer.sign("STRING_TO_SIGN");
685+
fail("This test is expected to fail");
686+
} catch (e) {
687+
expect(e.message).toBe("Resolved credential object is not valid");
688+
}
689+
});
690+
661691
it("should produce signatures matching known outputs", async () => {
662692
// Example copied from https://github.com/aws/aws-sdk-php/blob/3.42.0/tests/S3/PostObjectV4Test.php#L37
663693
const signer = new SignatureV4(signerInit);
@@ -700,6 +730,25 @@ describe("SignatureV4", () => {
700730
sha256: Sha256,
701731
};
702732

733+
it("should throw on invalid credential", async () => {
734+
const signer = new SignatureV4({ ...signerInit, credentials: {} as any });
735+
try {
736+
await signer.sign(
737+
{
738+
headers: Uint8Array.from([5, 58, 100, 97, 116, 101, 8, 0, 0, 1, 103, 247, 125, 87, 112]),
739+
payload: "foo" as any,
740+
},
741+
{
742+
signingDate: new Date(1369353600000),
743+
priorSignature: "",
744+
}
745+
);
746+
fail("This test is expected to fail");
747+
} catch (e) {
748+
expect(e.message).toBe("Resolved credential object is not valid");
749+
}
750+
});
751+
703752
it("support event signing", async () => {
704753
const signer = new SignatureV4(signerInit);
705754
const eventSignature = await signer.sign(

packages/signature-v4/src/SignatureV4.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ export class SignatureV4 implements RequestPresigner, RequestSigner, StringSigne
127127
signingService,
128128
} = options;
129129
const credentials = await this.credentialProvider();
130+
this.validateResolvedCredentials(credentials);
130131
const region = signingRegion ?? (await this.regionProvider());
131132

132133
const { longDate, shortDate } = formatDate(signingDate);
@@ -200,6 +201,7 @@ export class SignatureV4 implements RequestPresigner, RequestSigner, StringSigne
200201
{ signingDate = new Date(), signingRegion, signingService }: SigningArguments = {}
201202
): Promise<string> {
202203
const credentials = await this.credentialProvider();
204+
this.validateResolvedCredentials(credentials);
203205
const region = signingRegion ?? (await this.regionProvider());
204206
const { shortDate } = formatDate(signingDate);
205207

@@ -219,6 +221,7 @@ export class SignatureV4 implements RequestPresigner, RequestSigner, StringSigne
219221
}: RequestSigningArguments = {}
220222
): Promise<HttpRequest> {
221223
const credentials = await this.credentialProvider();
224+
this.validateResolvedCredentials(credentials);
222225
const region = signingRegion ?? (await this.regionProvider());
223226
const request = prepareRequest(requestToSign);
224227
const { longDate, shortDate } = formatDate(signingDate);
@@ -327,6 +330,18 @@ ${toHex(hashedRequest)}`;
327330
): Promise<Uint8Array> {
328331
return getSigningKey(this.sha256, credentials, shortDate, region, service || this.service);
329332
}
333+
334+
private validateResolvedCredentials(credentials: unknown) {
335+
if (
336+
typeof credentials !== "object" ||
337+
// @ts-expect-error: Property 'accessKeyId' does not exist on type 'object'.ts(2339)
338+
typeof credentials.accessKeyId !== "string" ||
339+
// @ts-expect-error: Property 'secretAccessKey' does not exist on type 'object'.ts(2339)
340+
typeof credentials.secretAccessKey !== "string"
341+
) {
342+
throw new Error("Resolved credential object is not valid");
343+
}
344+
}
330345
}
331346

332347
const formatDate = (now: DateInput): { longDate: string; shortDate: string } => {

0 commit comments

Comments
 (0)