diff --git a/src/csp-perms-audit.sh b/src/csp-perms-audit.sh new file mode 100644 index 0000000..246bd94 --- /dev/null +++ b/src/csp-perms-audit.sh @@ -0,0 +1,105 @@ +# Function to audit CSP, Permissions Policy, and secure headers for a given URL. +csp_perms_audit() { + URL=$1 + if [ -z "$URL" ]; then + echo "Usage: csp_perms_audit " + return 1 + fi + + # Generate a nonce for CSP policies + NONCE=$(openssl rand -base64 12) + + # Instructions and example Traefik labels for setting CSP, Permissions Policy, and secure headers. + echo "=== Instructions ===" + echo "Use the following analysis to create a Traefik label-based Content-Security-Policy (CSP), Permissions Policy, and a set of secure headers:" + echo "- This CSP includes a dynamically generated nonce ($NONCE). Apply the same nonce to each inline script to allow it to execute." + echo "- 'unsafe-inline' is added to support older browsers that do not recognize nonces or hashes. Modern browsers will ignore it if a nonce or hash is present." + echo "- If you prefer hashes instead of nonces, precompute SHA-256 hashes for each inline script's content." + echo "- Remember: Traefik labels do not support multiline YAML syntax, so ensure the CSP, Permissions Policy, and secure headers are formatted as single-line strings." + + echo "\nImportant: Each service should have a unique `secure-headers` middleware to avoid conflicts. Use a specific name for each service's `secure-headers` middleware, like `serviceA-secure-headers`, to ensure settings are applied uniquely to that service." + + echo "\nExample Traefik labels for your Docker Compose or Traefik configuration:\n" + + echo "# Content-Security-Policy (CSP) Label" + echo "traefik.http.middlewares.csp-headers.headers.customResponseHeaders.Content-Security-Policy: \"default-src 'self'; script-src 'self' 'strict-dynamic' 'nonce-$NONCE' 'unsafe-inline'; style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self' https://use.typekit.net; frame-src 'none'; base-uri 'none'; object-src 'none'; require-trusted-types-for 'script';\"" + + echo "\n# Permissions-Policy Label" + echo "traefik.http.middlewares.permissions-policy.headers.customResponseHeaders.Permissions-Policy: \"geolocation=(), microphone=(), camera=(), payment=(), usb=()\"" + + echo "\n# Additional Secure Headers (ensure middleware is unique for each service)" + echo "traefik.http.middlewares.unique-secure-headers.headers.customResponseHeaders.X-Frame-Options: \"DENY\"" + echo "traefik.http.middlewares.unique-secure-headers.headers.customResponseHeaders.X-Content-Type-Options: \"nosniff\"" + echo "traefik.http.middlewares.unique-secure-headers.headers.customResponseHeaders.X-XSS-Protection: \"1; mode=block\"" + echo "traefik.http.middlewares.unique-secure-headers.headers.customResponseHeaders.Referrer-Policy: \"no-referrer\"" + echo "traefik.http.middlewares.unique-secure-headers.headers.customResponseHeaders.Strict-Transport-Security: \"max-age=63072000; includeSubDomains; preload\"" + + echo "\n=== Checking Headers for Permissions and Relevant Policies on $URL ===" + + # Fetch headers and parse for permissions policies + curl -s -D - "$URL" -o /dev/null | grep -i "feature-policy\|permissions-policy" || echo "No relevant headers found." + + # Extract embedded resources (scripts, stylesheets, images, frames) + echo "\n=== Embedded Resources (Scripts, Stylesheets, Images, Frames) ===" + embedded_resources=$(curl -s "$URL" | grep -Eo 'src="([^"]+)"|href="([^"]+)|url\(([^)]+)\)' | sed -E 's/src=|href=|url\(|\)|"//g') + for resource in $embedded_resources; do + echo "$resource" + done + + # Checking accessibility of external resources + echo "\n=== Checking External Resources Accessibility ===" + check_resource_accessibility() { + resource=$1 + # Perform a HEAD request and check for 200, 301, 302, 307, 308 status codes + if curl -s -o /dev/null -w "%{http_code}" --head "$resource" | grep -E "^(200|301|302|307|308)$" > /dev/null; then + echo "✔️ Accessible: $resource" + else + echo "❌ Inaccessible: $resource (This may break functionality)" + fi + } + + # Recursive extraction for linked CSS files to capture additional resources + for css_url in $(echo "$embedded_resources" | grep -E "\.css$"); do + css_resources=$(curl -s "$css_url" | grep -Eo 'url\(([^)]+)\)' | sed -E 's/url\(|\)|"//g') + for css_resource in $css_resources; do + echo "$css_resource (from $css_url)" + external_resources="$external_resources $css_resource" + done + done + + external_resources=$(echo "$embedded_resources" | grep -E "^https?://") + for resource in $external_resources; do + check_resource_accessibility "$resource" + done + + # Check for inline scripts + echo "\n=== Checking for Inline Scripts ===" + inline_scripts=$(curl -s "$URL" | grep -i "