-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenerate-headers.js
88 lines (77 loc) · 3.44 KB
/
generate-headers.js
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
import Base64 from 'crypto-js/enc-base64.js';
import sha256 from 'crypto-js/sha256.js';
import { config } from 'dotenv';
import fs from 'fs';
import { parse } from 'node-html-parser';
import path from 'path';
config();
const __dirname = path.resolve();
const rootDomain = process.env.VITE_DOMAIN;
const buildDir = path.join(__dirname, 'build');
const buildPathLength = path.dirname(buildDir).length + path.basename(buildDir).length + 1;
function getStyleSrcAttrCsp(filename) {
const htmlSource = fs.readFileSync(filename, { encoding: 'utf-8' });
const root = parse(htmlSource);
let styleSrcAttrHashes = new Set();
const styleAttributesTags = ['div', 'g', 'img', 'span', 'svg'];
styleAttributesTags.forEach((tag) => {
root.getElementsByTagName(tag).forEach((element) => {
if (element.hasAttribute('style')) {
const hash = sha256(element.getAttribute('style'));
styleSrcAttrHashes.add(hash.toString(Base64));
}
});
});
return [...styleSrcAttrHashes].map((element) => `'sha256-${element}'`).join(' ');
}
let pageCspElements = [];
function findHtmlFiles(startPath, filter = /\.html$/) {
if (!fs.existsSync(startPath)) {
console.log(`Missing file: ${startPath}`);
return;
}
const files = fs.readdirSync(startPath);
files.forEach((element) => {
const filename = path.join(startPath, element);
const stat = fs.lstatSync(filename);
if (stat.isDirectory()) {
findHtmlFiles(filename, filter);
} else if (filter.test(filename)) {
const route = path.dirname(filename).slice(buildPathLength);
pageCspElements.push(`${route[0] === '/' ? route : `/${route}`}
Content-Security-Policy-Report-Only: base-uri 'self'; child-src 'self'; connect-src 'self' ws://${
process.env.VITE_DOMAIN
} https://hcaptcha.com https://*.hcaptcha.com ${
process.env.VITE_WORKER_URL
}; img-src 'self' data\:; font-src 'self' data\:; form-action 'self'; frame-ancestors 'self'; frame-src 'self' https://hcaptcha.com https://*.hcaptcha.com; manifest-src 'self'; media-src 'self' data:; object-src 'none'; script-src 'self' 'unsafe-inline' https://hcaptcha.com https://*.hcaptcha.com; worker-src 'self'; report-to csp-endpoint; report-uri https://sentry.io/api/${
process.env.SENTRY_PROJECT_ID
}/security/?sentry_key=${
process.env.SENTRY_KEY
}; style-src 'self' 'unsafe-hashes' ${getStyleSrcAttrCsp(
filename,
)} https://hcaptcha.com https://*.hcaptcha.com;`);
}
});
}
function createHeaders() {
const headers = `/*
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Permissions-Policy: accelerometer=(), autoplay=(), camera=(), document-domain=(), encrypted-media=(), fullscreen=(), gyroscope=(), interest-cohort=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), sync-xhr=(), usb=(), xr-spatial-tracking=(), geolocation=()
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Report-To: {"group": "csp-endpoint", "max_age": 10886400, "endpoints": [{"url": "https://sentry.io/api/${
process.env.SENTRY_PROJECT_ID
}/security/?sentry_key=${process.env.SENTRY_KEY}"}]}
${pageCspElements.join('\n')}
`;
const headersFile = path.join(buildDir, '_headers');
console.log('headers: ', headers);
fs.writeFileSync(headersFile, headers);
}
async function main() {
findHtmlFiles(buildDir);
createHeaders();
}
main();