Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cold-dolls-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@smithy/config-resolver": minor
---

validate region is hostname component
1 change: 1 addition & 0 deletions packages/config-resolver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@smithy/node-config-provider": "workspace:^",
"@smithy/types": "workspace:^",
"@smithy/util-config-provider": "workspace:^",
"@smithy/util-endpoints": "workspace:^",
"@smithy/util-middleware": "workspace:^",
"tslib": "^2.6.2"
},
Expand Down
77 changes: 77 additions & 0 deletions packages/config-resolver/src/regionConfig/checkRegion.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { isValidHostLabel } from "@smithy/util-endpoints";
import { describe, expect, test as it, vi } from "vitest";

import { checkRegion } from "./checkRegion";

describe("checkRegion", () => {
const acceptedRegionExamples = [
"us-east-1",
"ap-east-1",
"ap-southeast-4",
"ap-northeast-3",
"ap-northeast-1",
"eu-west-2",
"il-central-1",
"mx-central-1",
"eu-isoe-santaclaus-125",
"us-iso-reindeer-3000",
"eusc-de-gingerbread-8000",
"abcd",
"12345",
];

it("does not throw when the region is a valid host label", () => {
for (const region of acceptedRegionExamples) {
expect(() => checkRegion(region)).not.toThrow();
}
});

it("throws when the region is not a valid host label", () => {
for (const region of [
"us-east-1-",
"a".repeat(64),
"-us-east-1",
"",
"!",
"@",
"#",
"$",
"%",
"^",
"&",
"*",
"(",
")",
".",
"[",
"]",
";",
`'`,
"?",
"/",
"\\",
"|",
"+-*/",
]) {
expect(() => checkRegion(region)).toThrow(
`Region not accepted: region="${region}" is not a valid hostname component.`
);
}
});

it("caches accepted regions", () => {
const di = {
isValidHostLabel,
};
for (const region of acceptedRegionExamples) {
expect(() => checkRegion(region, di.isValidHostLabel)).not.toThrow();
}
vi.spyOn(di, "isValidHostLabel").mockImplementation(isValidHostLabel);
for (const region of acceptedRegionExamples) {
expect(() => checkRegion(region, di.isValidHostLabel)).not.toThrow();
}
expect(di.isValidHostLabel).toHaveBeenCalledTimes(0);
expect(() => checkRegion("oh-canada", di.isValidHostLabel)).not.toThrow();
expect(di.isValidHostLabel).toHaveBeenCalledTimes(1);
});
});
22 changes: 22 additions & 0 deletions packages/config-resolver/src/regionConfig/checkRegion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { isValidHostLabel } from "@smithy/util-endpoints";

/**
* @internal
*/
const validRegions = new Set<string>();

/**
* Checks whether region can be a host component.
*
* @param region - to check.
* @param check - checking function.
*
* @internal
*/
export const checkRegion = (region: string, check = isValidHostLabel) => {
if (!validRegions.has(region) && !check(region)) {
throw new Error(`Region not accepted: region="${region}" is not a valid hostname component.`);
} else {
validRegions.add(region);
}
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Provider } from "@smithy/types";

import { checkRegion } from "./checkRegion";
import { getRealRegion } from "./getRealRegion";
import { isFipsRegion } from "./isFipsRegion";

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

return Object.assign(input, {
region: async () => {
if (typeof region === "string") {
return getRealRegion(region);
}
const providedRegion = await region();
return getRealRegion(providedRegion);
const providedRegion = typeof region === "function" ? await region() : region;
const realRegion = getRealRegion(providedRegion);
checkRegion(realRegion);
return realRegion;
},
useFipsEndpoint: async () => {
const providedRegion = typeof region === "string" ? region : await region();
Expand Down
2 changes: 1 addition & 1 deletion packages/util-endpoints/src/lib/isValidHostLabel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe(isValidHostLabel.name, () => {
expect(isValidHostLabel(hostLabelToTest, true)).toBe(output);
});

describe("returns false is any subdomain is invalid", () => {
describe("returns false if any subdomain is invalid", () => {
const validHostLabel = testCases
.filter(([outputEntry]) => outputEntry === true)
.map(([, value]) => value)
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2514,6 +2514,7 @@ __metadata:
"@smithy/node-config-provider": "workspace:^"
"@smithy/types": "workspace:^"
"@smithy/util-config-provider": "workspace:^"
"@smithy/util-endpoints": "workspace:^"
"@smithy/util-middleware": "workspace:^"
concurrently: "npm:7.0.0"
downlevel-dts: "npm:0.10.1"
Expand Down