Update Content Security Policy with specific hashes for inline scripts and styles
ci/woodpecker/push/woodpecker Pipeline failed
Details
ci/woodpecker/push/woodpecker Pipeline failed
Details
This commit is contained in:
parent
d2c70ee746
commit
02f1c33b71
|
@ -0,0 +1,140 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSP Hash Collector
|
||||||
|
*
|
||||||
|
* This script scans all HTML files in a directory structure,
|
||||||
|
* extracts inline scripts and styles, and calculates SHA-256 hashes
|
||||||
|
* for use in Content Security Policy headers.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node collect-all-csp-hashes.js <directory-path>
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* node collect-all-csp-hashes.js ./docker/showerloop/public
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
// Function to calculate CSP hash
|
||||||
|
function calculateHash(content) {
|
||||||
|
const hash = crypto.createHash('sha256').update(content, 'utf8').digest('base64');
|
||||||
|
return `'sha256-${hash}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to extract inline scripts from HTML
|
||||||
|
function extractInlineScripts(html) {
|
||||||
|
const scriptRegex = /<script(?:\s[^>]*)?>([\s\S]*?)<\/script>/gi;
|
||||||
|
const scripts = [];
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = scriptRegex.exec(html)) !== null) {
|
||||||
|
const scriptContent = match[1].trim();
|
||||||
|
if (scriptContent && !scriptContent.includes('src=')) {
|
||||||
|
scripts.push(scriptContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scripts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to extract inline styles from HTML
|
||||||
|
function extractInlineStyles(html) {
|
||||||
|
const styleRegex = /<style(?:\s[^>]*)?>([\s\S]*?)<\/style>/gi;
|
||||||
|
const styles = [];
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = styleRegex.exec(html)) !== null) {
|
||||||
|
const styleContent = match[1].trim();
|
||||||
|
if (styleContent) {
|
||||||
|
styles.push(styleContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return styles;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to find all HTML files in a directory recursively
|
||||||
|
function findHtmlFiles(dir, fileList = []) {
|
||||||
|
const files = fs.readdirSync(dir);
|
||||||
|
|
||||||
|
files.forEach(file => {
|
||||||
|
const filePath = path.join(dir, file);
|
||||||
|
const stat = fs.statSync(filePath);
|
||||||
|
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
findHtmlFiles(filePath, fileList);
|
||||||
|
} else if (path.extname(file).toLowerCase() === '.html') {
|
||||||
|
fileList.push(filePath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return fileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main function
|
||||||
|
function main() {
|
||||||
|
if (process.argv.length < 3) {
|
||||||
|
console.error('Error: Please provide a directory path.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const directoryPath = process.argv[2];
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`\nScanning directory: ${directoryPath}`);
|
||||||
|
const htmlFiles = findHtmlFiles(directoryPath);
|
||||||
|
console.log(`Found ${htmlFiles.length} HTML files.`);
|
||||||
|
|
||||||
|
const scriptHashes = new Set();
|
||||||
|
const styleHashes = new Set();
|
||||||
|
let totalScripts = 0;
|
||||||
|
let totalStyles = 0;
|
||||||
|
|
||||||
|
htmlFiles.forEach(filePath => {
|
||||||
|
try {
|
||||||
|
const html = fs.readFileSync(filePath, 'utf8');
|
||||||
|
|
||||||
|
const scripts = extractInlineScripts(html);
|
||||||
|
const styles = extractInlineStyles(html);
|
||||||
|
|
||||||
|
totalScripts += scripts.length;
|
||||||
|
totalStyles += styles.length;
|
||||||
|
|
||||||
|
scripts.forEach(script => {
|
||||||
|
const hash = calculateHash(script);
|
||||||
|
scriptHashes.add(hash);
|
||||||
|
});
|
||||||
|
|
||||||
|
styles.forEach(style => {
|
||||||
|
const hash = calculateHash(style);
|
||||||
|
styleHashes.add(hash);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error processing file ${filePath}: ${err.message}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`\nProcessed ${totalScripts} inline scripts and ${totalStyles} inline styles.`);
|
||||||
|
console.log(`Found ${scriptHashes.size} unique script hashes and ${styleHashes.size} unique style hashes.`);
|
||||||
|
|
||||||
|
console.log('\n=== Complete CSP Directives ===');
|
||||||
|
|
||||||
|
console.log('\nScript CSP directive:');
|
||||||
|
console.log(`script-src 'self' blob: ${Array.from(scriptHashes).join(' ')}`);
|
||||||
|
|
||||||
|
console.log('\nStyle CSP directive:');
|
||||||
|
console.log(`style-src 'self' 'unsafe-inline' ${Array.from(styleHashes).join(' ')}`);
|
||||||
|
|
||||||
|
console.log('\nComplete CSP header:');
|
||||||
|
console.log(`Content-Security-Policy: default-src 'self'; script-src 'self' blob: ${Array.from(scriptHashes).join(' ')}; style-src 'self' ${Array.from(styleHashes).join(' ')}; img-src 'self' data: blob:; media-src 'self' blob:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; worker-src 'self' blob:"`);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error: ${err.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSP Hash Calculator
|
||||||
|
*
|
||||||
|
* This script calculates SHA-256 hashes for inline scripts and styles
|
||||||
|
* to be used in Content Security Policy headers.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node csp-hash-calculator.js "<inline-code>"
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* node csp-hash-calculator.js "document.addEventListener('DOMContentLoaded', function() { console.log('Hello'); });"
|
||||||
|
*/
|
||||||
|
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
// Function to calculate CSP hash
|
||||||
|
function calculateHash(content) {
|
||||||
|
const hash = crypto.createHash('sha256').update(content, 'utf8').digest('base64');
|
||||||
|
return `'sha256-${hash}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get content from command line argument or stdin
|
||||||
|
const content = process.argv[2] || '';
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
console.error('Error: No content provided. Please provide content as a command line argument.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate and display the hash
|
||||||
|
const hash = calculateHash(content);
|
||||||
|
console.log('CSP Hash:');
|
||||||
|
console.log(hash);
|
||||||
|
console.log('\nAdd to your CSP header:');
|
||||||
|
console.log('For script: script-src ' + hash);
|
||||||
|
console.log('For style: style-src ' + hash);
|
|
@ -48,8 +48,8 @@
|
||||||
# Frame Options (prevents clickjacking)
|
# Frame Options (prevents clickjacking)
|
||||||
X-Frame-Options "SAMEORIGIN"
|
X-Frame-Options "SAMEORIGIN"
|
||||||
|
|
||||||
# Update CSP to allow media content, scripts, and blob URLs
|
# Update CSP to allow media content, scripts, and blob URLs with hashes
|
||||||
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; media-src 'self' blob:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; worker-src 'self' blob:"
|
Content-Security-Policy "default-src 'self'; script-src 'self' blob: 'sha256-ahmIukdQI/ax+3q3Lrjh0Nuqv1/WxkBIGp+vaji6n8w=' 'sha256-qXRIcycfl2OwZL/s1bd83TFw2OcgcMsv1efLB/P3oOs=' 'sha256-SBn8uB66KTUeApEMuYlK6vZG0XcFpXLKlsbXswTKang=' 'sha256-/nvt7GhhWJsKGTVATnlAsNH54uy+pwbcjfx9Z9CT/u0=' 'sha256-rEjWap8xDw9tc9ULmaSD7VQycQasVvSd1OUiH9xKMTM=' 'sha256-9YtzahjQAT4luPVKC0lfwKhhBxWtN3zkQm99EHsc1bk=' 'sha256-PdtHVmWDPYQUs6SFGLloIwo3P4rG5A7ACmYWE1W4Gmk=' 'sha256-ALpx63KUUcf6ky/Teq3GLd+LlD+t+TpXN+bv/1++prU=' 'sha256-llDQiboC1dyoUHsUebHmXSwCs/k0znV6kWogS1Govvs=' 'sha256-zhuCqwglnTqPZ3YumUUbXlmgy3fN4NGHmK+wQzsoQic=' 'sha256-aCakwry3g1c1frt10sPVerFht/3JKT8i7ij3Aoxtsqw=' 'sha256-WE9M5TeJ2Xj1O9eh+0bg7XLyucO5+HCMccMznmiyocw=' 'sha256-FcjCj8HX/odDguAR0bldjsSdXOQMOLnCBKvlLHMZPZI=' 'sha256-tz6nsCI6ZDRK9g0tLDGMU5j9DBRx74XOe8xqaag7D3E=' 'sha256-IsinOLsxFzlWG2kdQIgMjg7l2ebbAaMbWWNSComW7EE=' 'sha256-p92qjinn1HJIBQCKu3QBxLsdKRh4NTdjvCax1ifSpw4=' 'sha256-17JNXqVQbWEbcxlPw9O3wCCa8PEFW9lwv6rOxRzkmXI=' 'sha256-uRkRZZ6nSw2qypQ46ShF3X/DRaPwWezfixlC4pkDuwo=' 'sha256-7bYe3kxYZPs9D4vqScBDsNEjqOw+n8pUFwyFObBKIjw=' 'sha256-IQIGMyVnkPj80HHZ8/Z8ZyxRC5ZPSFiGtTKsUdDqqOs='; style-src 'self' 'sha256-BBl1Pb4QBQZyj2HmRgFr/OhuPRYwV0zoE6G+08FM5TM=' 'sha256-DPggA6+WHJsxuaWoYLnB8XoTcBjKTnq+AmEhXZ2wJfw=' 'sha256-VyDqCue31iv/ickZ+WUp5RF3wMLAGo01mUL0VdbSTc8=' 'sha256-0ZDDv9ptap3zxZW4gGFrmDP9Y5osppDLJj0gRhecFN8=' 'sha256-c9m3RGxNzIy6ShTOIsmAgY77OyuTfgYCG3B2secjHc4=' 'sha256-rweYv4ZmpQ37GLZ2aJrWCpv486xCBOtOb6ngN4dBn8s=' 'sha256-dE50whpmj5sYr02WC5zh9QQNj6tVUQz1eTMmzJh6OU8=' 'sha256-3av5Wckr9yfHOVSXT8j0+EhuI9xI0Jld43e2jilZsro='; img-src 'self' data: blob:; media-src 'self' blob:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; worker-src 'self' blob:"
|
||||||
|
|
||||||
# Remove Server header
|
# Remove Server header
|
||||||
-Server
|
-Server
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSP Hash Extractor and Calculator
|
||||||
|
*
|
||||||
|
* This script extracts inline scripts and styles from HTML files
|
||||||
|
* and calculates SHA-256 hashes for use in Content Security Policy headers.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node extract-and-hash-csp.js <html-file-path>
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* node extract-and-hash-csp.js ./docker/showerloop/public/index.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
// Function to calculate CSP hash
|
||||||
|
function calculateHash(content) {
|
||||||
|
const hash = crypto.createHash('sha256').update(content, 'utf8').digest('base64');
|
||||||
|
return `'sha256-${hash}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to extract inline scripts from HTML
|
||||||
|
function extractInlineScripts(html) {
|
||||||
|
const scriptRegex = /<script(?:\s[^>]*)?>([\s\S]*?)<\/script>/gi;
|
||||||
|
const scripts = [];
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = scriptRegex.exec(html)) !== null) {
|
||||||
|
const scriptContent = match[1].trim();
|
||||||
|
if (scriptContent && !scriptContent.includes('src=')) {
|
||||||
|
scripts.push(scriptContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scripts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to extract inline styles from HTML
|
||||||
|
function extractInlineStyles(html) {
|
||||||
|
const styleRegex = /<style(?:\s[^>]*)?>([\s\S]*?)<\/style>/gi;
|
||||||
|
const styles = [];
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = styleRegex.exec(html)) !== null) {
|
||||||
|
const styleContent = match[1].trim();
|
||||||
|
if (styleContent) {
|
||||||
|
styles.push(styleContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return styles;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main function
|
||||||
|
function main() {
|
||||||
|
if (process.argv.length < 3) {
|
||||||
|
console.error('Error: Please provide an HTML file path.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = process.argv[2];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const html = fs.readFileSync(filePath, 'utf8');
|
||||||
|
|
||||||
|
const scripts = extractInlineScripts(html);
|
||||||
|
const styles = extractInlineStyles(html);
|
||||||
|
|
||||||
|
console.log(`\nAnalyzing file: ${filePath}`);
|
||||||
|
console.log('\n=== Inline Scripts ===');
|
||||||
|
|
||||||
|
if (scripts.length === 0) {
|
||||||
|
console.log('No inline scripts found.');
|
||||||
|
} else {
|
||||||
|
console.log(`Found ${scripts.length} inline scripts.`);
|
||||||
|
const scriptHashes = new Set();
|
||||||
|
|
||||||
|
scripts.forEach((script, index) => {
|
||||||
|
const hash = calculateHash(script);
|
||||||
|
scriptHashes.add(hash);
|
||||||
|
console.log(`\nScript #${index + 1}:`);
|
||||||
|
console.log(`${script.substring(0, 100)}${script.length > 100 ? '...' : ''}`);
|
||||||
|
console.log(`Hash: ${hash}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('\nScript CSP directive:');
|
||||||
|
console.log(`script-src 'self' ${Array.from(scriptHashes).join(' ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n=== Inline Styles ===');
|
||||||
|
|
||||||
|
if (styles.length === 0) {
|
||||||
|
console.log('No inline styles found.');
|
||||||
|
} else {
|
||||||
|
console.log(`Found ${styles.length} inline styles.`);
|
||||||
|
const styleHashes = new Set();
|
||||||
|
|
||||||
|
styles.forEach((style, index) => {
|
||||||
|
const hash = calculateHash(style);
|
||||||
|
styleHashes.add(hash);
|
||||||
|
console.log(`\nStyle #${index + 1}:`);
|
||||||
|
console.log(`${style.substring(0, 100)}${style.length > 100 ? '...' : ''}`);
|
||||||
|
console.log(`Hash: ${hash}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('\nStyle CSP directive:');
|
||||||
|
console.log(`style-src 'self' ${Array.from(styleHashes).join(' ')}`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error: ${err.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
Loading…
Reference in New Issue