#!/bin/bash # ===================================================================== # update-csp-hashes.sh - Update Content Security Policy hashes # ===================================================================== # This script updates the CSP hashes for: # 1. All JavaScript and CSS files # 2. All inline style attributes in HTML files # 3. Adds CSP meta tags to HTML files # After running this script, restart the server using: # ./caddy.sh # ===================================================================== set -e echo "Updating CSP hashes for all JavaScript, CSS files, and inline styles..." # Directory containing the files BASE_DIR="$(pwd)" # Check if we're in a Docker environment if [ -f "/etc/caddy/Caddyfile" ]; then CADDYFILE="/etc/caddy/Caddyfile" else CADDYFILE="$BASE_DIR/Caddyfile" fi TEMP_INLINE_HASHES_FILE=$(mktemp) # Arrays to store hashes SCRIPT_HASHES=() STYLE_HASHES=() # Calculate hash for a file calculate_hash() { local file=$1 sha256sum "$file" | awk '{print $1}' | xxd -r -p | base64 } # Calculate hash for inline style calculate_inline_hash() { local style_content=$1 echo -n "$style_content" | sha256sum | awk '{print $1}' | xxd -r -p | base64 } # Process JavaScript files echo "Processing JavaScript files..." for js_file in $(find "$BASE_DIR" -name "*.js" -type f); do echo "Processing $js_file" file_name=$(basename "$js_file") hash=$(calculate_hash "$js_file") SCRIPT_HASHES+=("'sha256-$hash'") # Update HTML files that reference this JS file for html_file in $(find "$BASE_DIR" -name "*.html" -type f); do if grep -q "$file_name" "$html_file"; then echo "Updating $file_name in $html_file" # Create a temporary file for the replacement tmp_file=$(mktemp) # For files with existing integrity attribute if grep -q "$file_name.*integrity" "$html_file"; then # Use awk for safer text processing awk -v fname="$file_name" -v newhash="$hash" ' { if ($0 ~ fname && $0 ~ /integrity/) { gsub(/integrity="sha256-[^"]*"/, "integrity=\"sha256-" newhash "\""); } print; }' "$html_file" > "$tmp_file" else # Add integrity attribute if it doesn't exist awk -v fname="$file_name" -v newhash="$hash" ' { if ($0 ~ fname && $0 ~ /src/ && !($0 ~ /integrity/)) { gsub(/src="[^"]*"/, "&" " integrity=\"sha256-" newhash "\""); } print; }' "$html_file" > "$tmp_file" fi # Replace original file with modified content mv "$tmp_file" "$html_file" fi done done # Process CSS files echo "Processing CSS files..." for css_file in $(find "$BASE_DIR" -name "*.css" -type f); do echo "Processing $css_file" file_name=$(basename "$css_file") hash=$(calculate_hash "$css_file") STYLE_HASHES+=("'sha256-$hash'") # Update HTML files that reference this CSS file for html_file in $(find "$BASE_DIR" -name "*.html" -type f); do if grep -q "$file_name" "$html_file"; then echo "Updating $file_name in $html_file" # Create a temporary file for the replacement tmp_file=$(mktemp) # For files with existing integrity attribute if grep -q "$file_name.*integrity" "$html_file"; then # Use awk for safer text processing awk -v fname="$file_name" -v newhash="$hash" ' { if ($0 ~ fname && $0 ~ /integrity/) { gsub(/integrity="sha256-[^"]*"/, "integrity=\"sha256-" newhash "\""); } print; }' "$html_file" > "$tmp_file" else # Add integrity attribute if it doesn't exist awk -v fname="$file_name" -v newhash="$hash" ' { if ($0 ~ fname && $0 ~ /href/ && !($0 ~ /integrity/)) { gsub(/href="[^"]*"/, "&" " integrity=\"sha256-" newhash "\""); } print; }' "$html_file" > "$tmp_file" fi # Replace original file with modified content mv "$tmp_file" "$html_file" fi done done # Find and process inline styles - using a more thorough approach echo "Processing HTML files for inline styles..." find "$BASE_DIR" -name "*.html" -type f | while read -r html_file; do echo "Processing $html_file for inline styles..." # Use a more comprehensive grep pattern to catch all inline styles # This includes both style="..." and style = "..." patterns grep -o 'style\s*=\s*"[^"]*"' "$html_file" | sed 's/style\s*=\s*"\(.*\)"/\1/' | while read -r style_content; do if [ -n "$style_content" ]; then hash=$(calculate_inline_hash "$style_content") echo "Found inline style: '$style_content'" echo "Calculated hash: sha256-$hash" echo "'sha256-$hash'" >> "$TEMP_INLINE_HASHES_FILE" fi done done # Sort and remove duplicates from inline style hashes if [ -f "$TEMP_INLINE_HASHES_FILE" ]; then sort -u "$TEMP_INLINE_HASHES_FILE" > "${TEMP_INLINE_HASHES_FILE}.sorted" mv "${TEMP_INLINE_HASHES_FILE}.sorted" "$TEMP_INLINE_HASHES_FILE" # Add inline style hashes to the STYLE_HASHES array while read -r hash; do STYLE_HASHES+=("$hash") done < "$TEMP_INLINE_HASHES_FILE" # Clean up rm -f "$TEMP_INLINE_HASHES_FILE" fi # Combine all hashes for CSP echo "Updating Caddyfile CSP headers..." SCRIPT_HASHES_STR=$(printf " %s" "${SCRIPT_HASHES[@]}") STYLE_HASHES_STR=$(printf " %s" "${STYLE_HASHES[@]}") # Create the CSP string CSP_STRING="default-src 'none'; script-src 'self'$SCRIPT_HASHES_STR; style-src 'self'$STYLE_HASHES_STR; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none';" # Check if Caddyfile exists before attempting to modify it if [ -f "$CADDYFILE" ]; then # Create a temporary file for the Caddyfile update tmp_file=$(mktemp) # Update CSP in Caddyfile using awk for more reliable text processing awk -v csp_string="$CSP_STRING" ' { if ($0 ~ /Content-Security-Policy/) { gsub(/Content-Security-Policy "[^"]*"/, "Content-Security-Policy \"" csp_string "\""); } print; }' "$CADDYFILE" > "$tmp_file" # Replace original Caddyfile with modified content mv "$tmp_file" "$CADDYFILE" else echo "Warning: Caddyfile not found at $CADDYFILE" fi # Also update Caddyfile.local if it exists if [ -f "$BASE_DIR/Caddyfile.local" ]; then echo "Updating Caddyfile.local CSP headers..." tmp_file=$(mktemp) awk -v csp_string="$CSP_STRING" ' { if ($0 ~ /Content-Security-Policy/) { gsub(/Content-Security-Policy "[^"]*"/, "Content-Security-Policy \"" csp_string "\""); } print; }' "$BASE_DIR/Caddyfile.local" > "$tmp_file" # Replace original Caddyfile.local with modified content mv "$tmp_file" "$BASE_DIR/Caddyfile.local" fi # Add CSP meta tags to HTML files echo "Adding CSP meta tags to HTML files..." for html_file in $(find "$BASE_DIR" -name "*.html" -type f); do echo "Adding CSP meta tag to $html_file" # Create a temporary file for the replacement tmp_file=$(mktemp) # Check if the file already has a CSP meta tag if grep -q ' "$tmp_file" else # Add CSP meta tag after the last meta tag awk -v csp_string="$CSP_STRING" ' { print; if ($0 ~ /<\/head>/ && !added_csp) { print " "; added_csp = 1; } }' "$html_file" > "$tmp_file" fi # Replace original file with modified content mv "$tmp_file" "$html_file" done echo "CSP hashes updated successfully!" echo "To apply changes, restart the server using: ./caddy.sh"