-
Notifications
You must be signed in to change notification settings - Fork 369
/
index.ts
136 lines (119 loc) · 3.95 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { IncomingMessage, ServerResponse } from "http";
export interface ContentSecurityPolicyOptions {
directives?: {
[directiveName: string]: Iterable<string>;
};
reportOnly?: boolean;
}
const isRawPolicyDirectiveNameInvalid = (rawDirectiveName: string): boolean =>
rawDirectiveName.length === 0 || /[^a-zA-Z0-9-]/.test(rawDirectiveName);
const dashify = (str: string): string =>
str.replace(/[A-Z]/g, (capitalLetter) => "-" + capitalLetter.toLowerCase());
function getHeaderNameFromOptions({
reportOnly,
}: ContentSecurityPolicyOptions): string {
if (reportOnly) {
return "Content-Security-Policy-Report-Only";
} else {
return "Content-Security-Policy";
}
}
function getHeaderValueFromOptions(
options: ContentSecurityPolicyOptions
): string {
if ("loose" in options) {
console.warn(
"Content-Security-Policy middleware no longer needs the `loose` parameter. You should remove it."
);
}
if ("setAllHeaders" in options) {
console.warn(
"Content-Security-Policy middleware no longer supports the `setAllHeaders` parameter. See <https://github.com/helmetjs/helmet/wiki/Setting-legacy-Content-Security-Policy-headers-in-Helmet-4>."
);
}
["disableAndroid", "browserSniff"].forEach((deprecatedOption) => {
if (deprecatedOption in options) {
console.warn(
`Content-Security-Policy middleware no longer does browser sniffing, so you can remove the \`${deprecatedOption}\` option. See <https://github.com/helmetjs/csp/issues/97> for discussion.`
);
}
});
const {
directives = {
"default-src": ["'self'"],
"base-uri": ["'self'"],
"block-all-mixed-content": [],
"font-src": ["'self'", "https:", "data:"],
"frame-ancestors": ["'self'"],
"img-src": ["'self'", "data:"],
"object-src": ["'none'"],
"script-src": ["'self'"],
"script-src-attr": ["'none'"],
"style-src": ["'self'", "https:", "'unsafe-inline'"],
"upgrade-insecure-requests": [],
},
} = options;
const directiveNamesUsed = new Set<string>();
const result = Object.entries(directives)
.map(([rawDirectiveName, rawDirectiveValue]) => {
if (isRawPolicyDirectiveNameInvalid(rawDirectiveName)) {
throw new Error(
`Content-Security-Policy received an invalid directive name ${JSON.stringify(
rawDirectiveName
)}`
);
}
const directiveName = dashify(rawDirectiveName);
if (directiveNamesUsed.has(directiveName)) {
throw new Error(
`Content-Security-Policy received a duplicate directive ${JSON.stringify(
directiveName
)}`
);
}
directiveNamesUsed.add(directiveName);
let directiveValue: string;
if (typeof rawDirectiveValue === "string") {
directiveValue = " " + rawDirectiveValue;
} else {
directiveValue = "";
for (const element of rawDirectiveValue) {
directiveValue += " " + element;
}
}
if (!directiveValue) {
return directiveName;
}
if (/;|,/.test(directiveValue)) {
throw new Error(
`Content-Security-Policy received an invalid directive value for ${JSON.stringify(
directiveName
)}`
);
}
return `${directiveName}${directiveValue}`;
})
.join(";");
if (!directiveNamesUsed.has("default-src")) {
throw new Error(
"Content-Security-Policy needs a default-src but none was provided"
);
}
return result;
}
function contentSecurityPolicy(
options: Readonly<ContentSecurityPolicyOptions> = {}
) {
const headerName = getHeaderNameFromOptions(options);
const headerValue = getHeaderValueFromOptions(options);
return function contentSecurityPolicyMiddleware(
_req: IncomingMessage,
res: ServerResponse,
next: () => void
) {
res.setHeader(headerName, headerValue);
next();
};
}
module.exports = contentSecurityPolicy;
export default contentSecurityPolicy;