Skip to content

Commit

Permalink
Increase TypeScript strictness for policies
Browse files Browse the repository at this point in the history
Before, the checks were all at runtime. Now they're at runtime and
compile time.

See [#369][0].

[0]: #369
  • Loading branch information
EvanHahn committed Jun 25, 2022
1 parent 2df93b5 commit 7e98093
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 54 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Changed

- **Breaking:** Where possible, increase TypeScript strictness around some strings. Only affects TypeScript users. See [#369](https://github.com/helmetjs/helmet/issues/369)

### Removed

- **Breaking:** Dropped support for Node 12 and 13. Node 14+ is now required
Expand Down
2 changes: 1 addition & 1 deletion middlewares/cross-origin-embedder-policy/index.ts
@@ -1,7 +1,7 @@
import { IncomingMessage, ServerResponse } from "http";

export interface CrossOriginEmbedderPolicyOptions {
policy?: string;
policy?: "require-corp" | "credentialless";
}

const ALLOWED_POLICIES = new Set(["require-corp", "credentialless"]);
Expand Down
2 changes: 1 addition & 1 deletion middlewares/cross-origin-opener-policy/index.ts
@@ -1,7 +1,7 @@
import { IncomingMessage, ServerResponse } from "http";

export interface CrossOriginOpenerPolicyOptions {
policy?: string;
policy?: "same-origin" | "same-origin-allow-popups" | "unsafe-none";
}

const ALLOWED_POLICIES = new Set([
Expand Down
2 changes: 1 addition & 1 deletion middlewares/cross-origin-resource-policy/index.ts
@@ -1,7 +1,7 @@
import { IncomingMessage, ServerResponse } from "http";

export interface CrossOriginResourcePolicyOptions {
policy?: string;
policy?: "same-origin" | "same-site" | "cross-origin";
}

const ALLOWED_POLICIES = new Set(["same-origin", "same-site", "cross-origin"]);
Expand Down
17 changes: 14 additions & 3 deletions middlewares/referrer-policy/index.ts
@@ -1,10 +1,21 @@
import { IncomingMessage, ServerResponse } from "http";

type ReferrerPolicyToken =
| "no-referrer"
| "no-referrer-when-downgrade"
| "same-origin"
| "origin"
| "strict-origin"
| "origin-when-cross-origin"
| "strict-origin-when-cross-origin"
| "unsafe-url"
| "";

export interface ReferrerPolicyOptions {
policy?: string | string[];
policy?: ReferrerPolicyToken | ReferrerPolicyToken[];
}

const ALLOWED_TOKENS = new Set<string>([
const ALLOWED_TOKENS = new Set<ReferrerPolicyToken>([
"no-referrer",
"no-referrer-when-downgrade",
"same-origin",
Expand All @@ -25,7 +36,7 @@ function getHeaderValueFromOptions({
throw new Error("Referrer-Policy received no policy tokens");
}

const tokensSeen = new Set<string>();
const tokensSeen = new Set<ReferrerPolicyToken>();
tokens.forEach((token) => {
if (!ALLOWED_TOKENS.has(token)) {
throw new Error(
Expand Down
2 changes: 1 addition & 1 deletion middlewares/x-permitted-cross-domain-policies/index.ts
@@ -1,7 +1,7 @@
import { IncomingMessage, ServerResponse } from "http";

export interface XPermittedCrossDomainPoliciesOptions {
permittedPolicies?: string;
permittedPolicies?: "none" | "master-only" | "by-content-type" | "all";
}

const ALLOWED_PERMITTED_POLICIES = new Set([
Expand Down
18 changes: 9 additions & 9 deletions test/cross-origin-embedder-policy.test.ts
Expand Up @@ -18,7 +18,7 @@ describe("Cross-Origin-Embedder-Policy middleware", () => {
);
});

["require-corp", "credentialless"].forEach((policy) => {
(["require-corp", "credentialless"] as const).forEach((policy) => {
it(`sets "Cross-Origin-Embedder-Policy: ${policy}" when told to`, async () => {
await check(crossOriginEmbedderPolicy({ policy }), {
"cross-origin-embedder-policy": policy,
Expand All @@ -29,16 +29,16 @@ describe("Cross-Origin-Embedder-Policy middleware", () => {
it("throws when setting the policy to an invalid value", () => {
const invalidValues = [
"",
"NONE",
"by-ftp-filename",
123 as any,
null as any,
new String("none") as any,
"foo",
"CREDENTIALLESS",
123,
null,
new String("credentialless"),
];
for (const policy of invalidValues) {
expect(() => crossOriginEmbedderPolicy({ policy })).toThrow(
/^Cross-Origin-Embedder-Policy does not support /
);
expect(() =>
crossOriginEmbedderPolicy({ policy: policy as any })
).toThrow(/^Cross-Origin-Embedder-Policy does not support /);
}
});
});
14 changes: 7 additions & 7 deletions test/cross-origin-opener-policy.test.ts
Expand Up @@ -15,7 +15,7 @@ describe("Cross-Origin-Opener-Policy middleware", () => {
);
});

["same-origin", "same-origin-allow-popups", "unsafe-none"].forEach(
(["same-origin", "same-origin-allow-popups", "unsafe-none"] as const).forEach(
(policy) => {
it(`sets "Cross-Origin-Opener-Policy: ${policy}" when told to`, async () => {
await check(crossOriginOpenerPolicy({ policy }), {
Expand All @@ -28,14 +28,14 @@ describe("Cross-Origin-Opener-Policy middleware", () => {
it("throws when setting the policy to an invalid value", () => {
const invalidValues = [
"",
"NONE",
"by-ftp-filename",
123 as any,
null as any,
new String("none") as any,
"foo",
"SAME-ORIGIN",
123,
null,
new String("same-origin"),
];
for (const policy of invalidValues) {
expect(() => crossOriginOpenerPolicy({ policy })).toThrow(
expect(() => crossOriginOpenerPolicy({ policy: policy as any })).toThrow(
/^Cross-Origin-Opener-Policy does not support /
);
}
Expand Down
18 changes: 9 additions & 9 deletions test/cross-origin-resource-policy.test.ts
Expand Up @@ -18,7 +18,7 @@ describe("Cross-Origin-Resource-Policy middleware", () => {
);
});

["same-origin", "same-site", "cross-origin"].forEach((policy) => {
(["same-origin", "same-site", "cross-origin"] as const).forEach((policy) => {
it(`sets "Cross-Origin-Resource-Policy: ${policy}" when told to`, async () => {
await check(crossOriginResourcePolicy({ policy }), {
"cross-origin-resource-policy": policy,
Expand All @@ -29,16 +29,16 @@ describe("Cross-Origin-Resource-Policy middleware", () => {
it("throws when setting the policy to an invalid value", () => {
const invalidValues = [
"",
"NONE",
"by-ftp-filename",
123 as any,
null as any,
new String("none") as any,
"foo",
"CROSS-ORIGIN",
123,
null,
new String("none"),
];
for (const policy of invalidValues) {
expect(() => crossOriginResourcePolicy({ policy })).toThrow(
/^Cross-Origin-Resource-Policy does not support /
);
expect(() =>
crossOriginResourcePolicy({ policy: policy as any })
).toThrow(/^Cross-Origin-Resource-Policy does not support /);
}
});
});
34 changes: 17 additions & 17 deletions test/referrer-policy.test.ts
Expand Up @@ -17,17 +17,19 @@ describe("Referrer-Policy middleware", () => {
});
});

[
"no-referrer",
"no-referrer-when-downgrade",
"same-origin",
"origin",
"strict-origin",
"origin-when-cross-origin",
"strict-origin-when-cross-origin",
"unsafe-url",
"",
].forEach((policy) => {
(
[
"no-referrer",
"no-referrer-when-downgrade",
"same-origin",
"origin",
"strict-origin",
"origin-when-cross-origin",
"strict-origin-when-cross-origin",
"unsafe-url",
"",
] as const
).forEach((policy) => {
it(`can set the header to "${policy}" by specifying it as a string`, async () => {
await check(referrerPolicy({ policy }), {
"referrer-policy": policy,
Expand All @@ -48,12 +50,10 @@ describe("Referrer-Policy middleware", () => {
});

it("fails with a bad policy", () => {
expect(referrerPolicy.bind(null, { policy: "garbage" })).toThrow();
expect(referrerPolicy.bind(null, { policy: "sameorigin" })).toThrow();
expect(referrerPolicy.bind(null, { policy: 123 as any })).toThrow();
expect(referrerPolicy.bind(null, { policy: false as any })).toThrow();
expect(referrerPolicy.bind(null, { policy: null as any })).toThrow();
expect(referrerPolicy.bind(null, { policy: {} as any })).toThrow();
const invalidValues = ["foo", "sameorigin", "ORIGIN", 123, false, null, {}];
for (const policy of invalidValues) {
expect(referrerPolicy.bind(null, { policy: policy as any })).toThrow();
}
});

it("fails with an empty array", () => {
Expand Down
12 changes: 7 additions & 5 deletions test/x-permitted-cross-domain-policies.test.ts
Expand Up @@ -18,7 +18,7 @@ describe("X-Permitted-Cross-Domain-Policies middleware", () => {
);
});

["none", "master-only", "by-content-type", "all"].forEach(
(["none", "master-only", "by-content-type", "all"] as const).forEach(
(permittedPolicies) => {
it(`sets "X-Permitted-Cross-Domain-Policies: ${permittedPolicies}" when told to`, async () => {
await check(xPermittedCrossDomainPolicies({ permittedPolicies }), {
Expand All @@ -33,13 +33,15 @@ describe("X-Permitted-Cross-Domain-Policies middleware", () => {
"",
"NONE",
"by-ftp-filename",
123 as any,
null as any,
new String("none") as any,
123,
null,
new String("none"),
];
for (const permittedPolicies of invalidValues) {
expect(() =>
xPermittedCrossDomainPolicies({ permittedPolicies })
xPermittedCrossDomainPolicies({
permittedPolicies: permittedPolicies as any,
})
).toThrow(/^X-Permitted-Cross-Domain-Policies does not support /);
}
});
Expand Down

0 comments on commit 7e98093

Please sign in to comment.