Skip to content

Commit 3233c73

Browse files
committed
Use backticks and arrays
1 parent bcd85ed commit 3233c73

File tree

2 files changed

+89
-82
lines changed

2 files changed

+89
-82
lines changed

bin/_headers.config.ts

Lines changed: 69 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
interface HeadersConfig {
88
paths: Array<{
99
pattern: string;
10-
headers: Record<string, string | Record<string, string[]>>;
10+
headers: Record<string, string[] | Record<string, string[]>>;
1111
}>;
1212
}
1313

@@ -18,7 +18,7 @@ const config: HeadersConfig = {
1818
headers: {
1919
'Content-Security-Policy': {
2020
'connect-src': [
21-
'\'self\'',
21+
`'self'`,
2222
'https://status.maxmind.com',
2323
'https://www.maxmind.com',
2424
'https://api.hubspot.com',
@@ -30,37 +30,22 @@ const config: HeadersConfig = {
3030
'https://*.g.doubleclick.net',
3131
'https://*.google.com',
3232
],
33-
'default-src': [
34-
'\'self\'',
35-
],
36-
'font-src': [
37-
'\'self\'',
38-
'https://fonts.gstatic.com',
39-
],
40-
'form-action': [
41-
'\'self\'',
42-
],
43-
'frame-ancestors': [
44-
'\'self\'',
45-
],
33+
'default-src': [`'self'`],
34+
'font-src': [`'self'`, 'https://fonts.gstatic.com'],
35+
'form-action': [`'self'`],
36+
'frame-ancestors': [`'self'`],
4637
'frame-src': [
47-
'\'self\'',
38+
`'self'`,
4839
'https://app.hubspot.com',
4940
'https://www.google.com',
5041
'https://www.googletagmanager.com',
5142
],
52-
'img-src': [
53-
'\'self\'',
54-
'data:',
55-
'https:',
56-
],
57-
'object-src': [
58-
'\'none\'',
59-
],
43+
'img-src': [`'self'`, 'data:', 'https:'],
44+
'object-src': [`'none'`],
6045
'script-src': [
61-
'\'self\'',
62-
'\'report-sample\'',
63-
'\'unsafe-inline\'',
46+
`'self'`,
47+
`'report-sample'`,
48+
`'unsafe-inline'`,
6449
'https://js.hs-scripts.com',
6550
'https://js.hs-analytics.net',
6651
'https://js.hs-banner.com',
@@ -73,34 +58,67 @@ const config: HeadersConfig = {
7358
'https://*.googletagmanager.com',
7459
],
7560
'style-src': [
76-
'\'self\'',
77-
'\'unsafe-inline\'',
61+
`'self'`,
62+
`'unsafe-inline'`,
7863
'https://fonts.googleapis.com',
7964
'https://www.gstatic.com',
8065
],
8166
},
82-
'Feature-Policy':
83-
'accelerometer \'none\'; autoplay \'none\'; camera \'none\'; ' +
84-
'encrypted-media \'none\'; fullscreen \'none\'; geolocation \'none\'; ' +
85-
'gyroscope \'none\'; magnetometer \'none\'; microphone \'none\'; ' +
86-
'midi \'none\'; payment \'none\'; picture-in-picture \'none\'; ' +
87-
'usb \'none\'; sync-xhr \'none\'',
88-
'Permissions-Policy':
89-
'accelerometer=(), ambient-light-sensor=(), autoplay=(), ' +
90-
'battery=(), camera=(), display-capture=(), document-domain=(), ' +
91-
'encrypted-media=(), execution-while-not-rendered=(), ' +
92-
'execution-while-out-of-viewport=(), fullscreen=(), gamepad=(), ' +
93-
'geolocation=(), gyroscope=(), hid=(), idle-detection=(), ' +
94-
'magnetometer=(), microphone=(), midi=(), payment=(), ' +
95-
'picture-in-picture=(), publickey-credentials-get=(), ' +
96-
'screen-wake-lock=(), serial=(), speaker-selection=(), usb=(), ' +
97-
'web-share=(), xr-spatial-tracking=()',
98-
'Referrer-Policy': 'strict-origin-when-cross-origin',
99-
'Strict-Transport-Security':
100-
'max-age=63072000; includeSubDomains; preload',
101-
'X-Content-Type-Options': 'nosniff',
102-
'X-Frame-Options': 'DENY',
103-
'X-XSS-Protection': '1; mode=block',
67+
'Feature-Policy': [
68+
`accelerometer 'none'`,
69+
`autoplay 'none'`,
70+
`camera 'none'`,
71+
`encrypted-media 'none'`,
72+
`fullscreen 'none'`,
73+
`geolocation 'none'`,
74+
`gyroscope 'none'`,
75+
`magnetometer 'none'`,
76+
`microphone 'none'`,
77+
`midi 'none'`,
78+
`payment 'none'`,
79+
`picture-in-picture 'none'`,
80+
`usb 'none'`,
81+
`sync-xhr 'none'`,
82+
],
83+
'Permissions-Policy': [
84+
'accelerometer=()',
85+
'ambient-light-sensor=()',
86+
'autoplay=()',
87+
'battery=()',
88+
'camera=()',
89+
'display-capture=()',
90+
'document-domain=()',
91+
'encrypted-media=()',
92+
'execution-while-not-rendered=()',
93+
'execution-while-out-of-viewport=()',
94+
'fullscreen=()',
95+
'gamepad=()',
96+
'geolocation=()',
97+
'gyroscope=()',
98+
'hid=()',
99+
'idle-detection=()',
100+
'magnetometer=()',
101+
'microphone=()',
102+
'midi=()',
103+
'payment=()',
104+
'picture-in-picture=()',
105+
'publickey-credentials-get=()',
106+
'screen-wake-lock=()',
107+
'serial=()',
108+
'speaker-selection=()',
109+
'usb=()',
110+
'web-share=()',
111+
'xr-spatial-tracking=()',
112+
],
113+
'Referrer-Policy': ['strict-origin-when-cross-origin'],
114+
'Strict-Transport-Security': [
115+
'max-age=63072000',
116+
'includeSubDomains',
117+
'preload',
118+
],
119+
'X-Content-Type-Options': ['nosniff'],
120+
'X-Frame-Options': ['DENY'],
121+
'X-XSS-Protection': ['1', 'mode=block'],
104122
},
105123
},
106124
],

bin/generate-headers.ts

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,21 @@ import config from './_headers.config.ts';
1212

1313
interface PathConfig {
1414
pattern: string;
15-
headers: Record<string, string | Record<string, string | string[]>>;
15+
headers: Record<string, string[] | Record<string, string[]>>;
1616
}
1717

1818
/**
1919
* Generate _headers file content from config
2020
*/
21-
function generateHeaders(
22-
config: { paths: PathConfig[] }
23-
): string {
21+
function generateHeaders(config: { paths: PathConfig[] }): string {
2422
let output = '';
2523

2624
// Add warning comment at the top
2725
output += '# ⚠️ DO NOT EDIT THIS FILE DIRECTLY!\n';
28-
output += '# This file is automatically generated from bin/_headers.config.ts\n';
29-
output += '# To make changes, edit bin/_headers.config.ts and run: npm run build:headers\n';
26+
output +=
27+
'# This file is automatically generated from bin/_headers.config.ts\n';
28+
output +=
29+
'# To make changes, edit bin/_headers.config.ts and run: npm run build:headers\n';
3030
output += '#\n';
3131
output += '# See README.md for more information\n';
3232
output += '\n';
@@ -35,36 +35,25 @@ function generateHeaders(
3535
// Write path pattern
3636
output += pathConfig.pattern + '\n';
3737

38-
// Process CSP first if it exists
39-
if (pathConfig.headers['Content-Security-Policy']) {
40-
const csp = pathConfig.headers['Content-Security-Policy'] as Record<
41-
string,
42-
string | string[]
43-
>;
44-
const directives: string[] = [];
38+
// Process all headers
39+
for (const [header, value] of Object.entries(pathConfig.headers)) {
40+
// Handle Content-Security-Policy (nested object)
41+
if (header === 'Content-Security-Policy') {
42+
const csp = value as Record<string, string[]>;
43+
const directives: string[] = [];
4544

46-
for (const [
47-
directive,
48-
sources,
49-
] of Object.entries(csp)) {
50-
if (Array.isArray(sources)) {
45+
for (const [directive, sources] of Object.entries(csp)) {
5146
directives.push(`${directive} ${sources.join(' ')}`);
52-
} else {
53-
directives.push(`${directive} ${sources}`);
5447
}
55-
}
56-
57-
output += ` Content-Security-Policy: ${directives.join('; ')}\n`;
58-
}
5948

60-
// Process other headers
61-
for (const [
62-
header,
63-
value,
64-
] of Object.entries(pathConfig.headers)) {
65-
if (header === 'Content-Security-Policy') continue;
49+
output += ` Content-Security-Policy: ${directives.join('; ')}\n`;
50+
continue;
51+
}
6652

67-
output += ` ${header}: ${value}\n`;
53+
// Handle all other headers (arrays)
54+
const values = value as string[];
55+
const separator = header === 'Permissions-Policy' ? ', ' : '; ';
56+
output += ` ${header}: ${values.join(separator)}\n`;
6857
}
6958
}
7059

0 commit comments

Comments
 (0)