Skip to content

Commit 13c5cd9

Browse files
authored
chore(config-resolver): add region validation cache (#1750)
* chore(config-resolver): add region validation cache * formatting
1 parent ef4feb4 commit 13c5cd9

File tree

7 files changed

+112
-6
lines changed

7 files changed

+112
-6
lines changed

.changeset/cold-dolls-applaud.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@smithy/config-resolver": minor
3+
---
4+
5+
validate region is hostname component

packages/config-resolver/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"@smithy/node-config-provider": "workspace:^",
2929
"@smithy/types": "workspace:^",
3030
"@smithy/util-config-provider": "workspace:^",
31+
"@smithy/util-endpoints": "workspace:^",
3132
"@smithy/util-middleware": "workspace:^",
3233
"tslib": "^2.6.2"
3334
},
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { isValidHostLabel } from "@smithy/util-endpoints";
2+
import { describe, expect, test as it, vi } from "vitest";
3+
4+
import { checkRegion } from "./checkRegion";
5+
6+
describe("checkRegion", () => {
7+
const acceptedRegionExamples = [
8+
"us-east-1",
9+
"ap-east-1",
10+
"ap-southeast-4",
11+
"ap-northeast-3",
12+
"ap-northeast-1",
13+
"eu-west-2",
14+
"il-central-1",
15+
"mx-central-1",
16+
"eu-isoe-santaclaus-125",
17+
"us-iso-reindeer-3000",
18+
"eusc-de-gingerbread-8000",
19+
"abcd",
20+
"12345",
21+
];
22+
23+
it("does not throw when the region is a valid host label", () => {
24+
for (const region of acceptedRegionExamples) {
25+
expect(() => checkRegion(region)).not.toThrow();
26+
}
27+
});
28+
29+
it("throws when the region is not a valid host label", () => {
30+
for (const region of [
31+
"us-east-1-",
32+
"a".repeat(64),
33+
"-us-east-1",
34+
"",
35+
"!",
36+
"@",
37+
"#",
38+
"$",
39+
"%",
40+
"^",
41+
"&",
42+
"*",
43+
"(",
44+
")",
45+
".",
46+
"[",
47+
"]",
48+
";",
49+
`'`,
50+
"?",
51+
"/",
52+
"\\",
53+
"|",
54+
"+-*/",
55+
]) {
56+
expect(() => checkRegion(region)).toThrow(
57+
`Region not accepted: region="${region}" is not a valid hostname component.`
58+
);
59+
}
60+
});
61+
62+
it("caches accepted regions", () => {
63+
const di = {
64+
isValidHostLabel,
65+
};
66+
for (const region of acceptedRegionExamples) {
67+
expect(() => checkRegion(region, di.isValidHostLabel)).not.toThrow();
68+
}
69+
vi.spyOn(di, "isValidHostLabel").mockImplementation(isValidHostLabel);
70+
for (const region of acceptedRegionExamples) {
71+
expect(() => checkRegion(region, di.isValidHostLabel)).not.toThrow();
72+
}
73+
expect(di.isValidHostLabel).toHaveBeenCalledTimes(0);
74+
expect(() => checkRegion("oh-canada", di.isValidHostLabel)).not.toThrow();
75+
expect(di.isValidHostLabel).toHaveBeenCalledTimes(1);
76+
});
77+
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { isValidHostLabel } from "@smithy/util-endpoints";
2+
3+
/**
4+
* @internal
5+
*/
6+
const validRegions = new Set<string>();
7+
8+
/**
9+
* Checks whether region can be a host component.
10+
*
11+
* @param region - to check.
12+
* @param check - checking function.
13+
*
14+
* @internal
15+
*/
16+
export const checkRegion = (region: string, check = isValidHostLabel) => {
17+
if (!validRegions.has(region) && !check(region)) {
18+
throw new Error(`Region not accepted: region="${region}" is not a valid hostname component.`);
19+
} else {
20+
validRegions.add(region);
21+
}
22+
};

packages/config-resolver/src/regionConfig/resolveRegionConfig.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Provider } from "@smithy/types";
22

3+
import { checkRegion } from "./checkRegion";
34
import { getRealRegion } from "./getRealRegion";
45
import { isFipsRegion } from "./isFipsRegion";
56

@@ -47,11 +48,10 @@ export const resolveRegionConfig = <T>(input: T & RegionInputConfig & Previously
4748

4849
return Object.assign(input, {
4950
region: async () => {
50-
if (typeof region === "string") {
51-
return getRealRegion(region);
52-
}
53-
const providedRegion = await region();
54-
return getRealRegion(providedRegion);
51+
const providedRegion = typeof region === "function" ? await region() : region;
52+
const realRegion = getRealRegion(providedRegion);
53+
checkRegion(realRegion);
54+
return realRegion;
5555
},
5656
useFipsEndpoint: async () => {
5757
const providedRegion = typeof region === "string" ? region : await region();

packages/util-endpoints/src/lib/isValidHostLabel.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ describe(isValidHostLabel.name, () => {
3232
expect(isValidHostLabel(hostLabelToTest, true)).toBe(output);
3333
});
3434

35-
describe("returns false is any subdomain is invalid", () => {
35+
describe("returns false if any subdomain is invalid", () => {
3636
const validHostLabel = testCases
3737
.filter(([outputEntry]) => outputEntry === true)
3838
.map(([, value]) => value)

yarn.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2514,6 +2514,7 @@ __metadata:
25142514
"@smithy/node-config-provider": "workspace:^"
25152515
"@smithy/types": "workspace:^"
25162516
"@smithy/util-config-provider": "workspace:^"
2517+
"@smithy/util-endpoints": "workspace:^"
25172518
"@smithy/util-middleware": "workspace:^"
25182519
concurrently: "npm:7.0.0"
25192520
downlevel-dts: "npm:0.10.1"

0 commit comments

Comments
 (0)