mirror of
https://github.com/Rarebuffalo/securelens-backend.git
synced 2026-06-19 07:00:30 +00:00
141 lines
5.7 KiB
Python
141 lines
5.7 KiB
Python
import httpx
|
|
|
|
from app.schemas.scan import Issue
|
|
from app.services.scanner.base import BaseScanner
|
|
|
|
|
|
class HeaderScanner(BaseScanner):
|
|
async def scan(self, url: str, response: httpx.Response) -> list[Issue]:
|
|
issues: list[Issue] = []
|
|
headers = response.headers
|
|
|
|
if "Content-Security-Policy" not in headers:
|
|
issues.append(Issue(
|
|
issue="Missing Content-Security-Policy header",
|
|
severity="Warning",
|
|
layer="Server Config Layer",
|
|
fix="Add header: Content-Security-Policy: default-src 'self';",
|
|
))
|
|
else:
|
|
csp = headers["Content-Security-Policy"]
|
|
if "'unsafe-inline'" in csp:
|
|
issues.append(Issue(
|
|
issue="Content-Security-Policy allows 'unsafe-inline'",
|
|
severity="Warning",
|
|
layer="Server Config Layer",
|
|
fix="Remove 'unsafe-inline' from CSP and use nonces or hashes for inline scripts/styles",
|
|
))
|
|
if "'unsafe-eval'" in csp:
|
|
issues.append(Issue(
|
|
issue="Content-Security-Policy allows 'unsafe-eval'",
|
|
severity="Warning",
|
|
layer="Server Config Layer",
|
|
fix="Remove 'unsafe-eval' from CSP to prevent dynamic code execution via eval()",
|
|
))
|
|
if "*" in csp.split():
|
|
issues.append(Issue(
|
|
issue="Content-Security-Policy uses wildcard (*) source",
|
|
severity="Warning",
|
|
layer="Server Config Layer",
|
|
fix="Replace wildcard (*) in CSP with specific trusted domains",
|
|
))
|
|
|
|
if "X-Frame-Options" not in headers:
|
|
issues.append(Issue(
|
|
issue="Missing X-Frame-Options header",
|
|
severity="Warning",
|
|
layer="Server Config Layer",
|
|
fix="Add header: X-Frame-Options: SAMEORIGIN",
|
|
))
|
|
|
|
if "X-Content-Type-Options" not in headers:
|
|
issues.append(Issue(
|
|
issue="Missing X-Content-Type-Options header",
|
|
severity="Warning",
|
|
layer="Server Config Layer",
|
|
fix="Add header: X-Content-Type-Options: nosniff",
|
|
))
|
|
|
|
if "Referrer-Policy" not in headers:
|
|
issues.append(Issue(
|
|
issue="Missing Referrer-Policy header",
|
|
severity="Info",
|
|
layer="Server Config Layer",
|
|
fix="Add header: Referrer-Policy: strict-origin-when-cross-origin",
|
|
))
|
|
|
|
if "Permissions-Policy" not in headers:
|
|
issues.append(Issue(
|
|
issue="Missing Permissions-Policy header",
|
|
severity="Info",
|
|
layer="Server Config Layer",
|
|
fix="Add header: Permissions-Policy: geolocation=(), camera=(), microphone=()",
|
|
))
|
|
|
|
if headers.get("Access-Control-Allow-Origin") == "*":
|
|
issues.append(Issue(
|
|
issue="CORS allows all origins (*)",
|
|
severity="Warning",
|
|
layer="Server Config Layer",
|
|
fix="Restrict Access-Control-Allow-Origin to trusted domains",
|
|
))
|
|
|
|
server = headers.get("Server", "")
|
|
if server:
|
|
issues.append(Issue(
|
|
issue=f"Server header discloses technology: {server}",
|
|
severity="Info",
|
|
layer="Server Config Layer",
|
|
fix="Remove or obfuscate the Server header to prevent information disclosure",
|
|
))
|
|
|
|
if "X-Powered-By" in headers:
|
|
issues.append(Issue(
|
|
issue=f"X-Powered-By header discloses technology: {headers['X-Powered-By']}",
|
|
severity="Info",
|
|
layer="Server Config Layer",
|
|
fix="Remove the X-Powered-By header to prevent information disclosure",
|
|
))
|
|
|
|
cache_control = headers.get("Cache-Control", "")
|
|
if not cache_control:
|
|
issues.append(Issue(
|
|
issue="Missing Cache-Control header",
|
|
severity="Info",
|
|
layer="Server Config Layer",
|
|
fix="Add Cache-Control header with appropriate directives (e.g., no-store for sensitive pages)",
|
|
))
|
|
elif "no-store" not in cache_control.lower() and "private" not in cache_control.lower():
|
|
issues.append(Issue(
|
|
issue="Cache-Control does not prevent caching of potentially sensitive content",
|
|
severity="Info",
|
|
layer="Server Config Layer",
|
|
fix="Add 'no-store' or 'private' to Cache-Control for pages with sensitive data",
|
|
))
|
|
|
|
if "Cross-Origin-Opener-Policy" not in headers:
|
|
issues.append(Issue(
|
|
issue="Missing Cross-Origin-Opener-Policy (COOP) header",
|
|
severity="Info",
|
|
layer="Server Config Layer",
|
|
fix="Add header: Cross-Origin-Opener-Policy: same-origin",
|
|
))
|
|
|
|
if "Cross-Origin-Resource-Policy" not in headers:
|
|
issues.append(Issue(
|
|
issue="Missing Cross-Origin-Resource-Policy (CORP) header",
|
|
severity="Info",
|
|
layer="Server Config Layer",
|
|
fix="Add header: Cross-Origin-Resource-Policy: same-origin",
|
|
))
|
|
|
|
if "Cross-Origin-Embedder-Policy" not in headers:
|
|
issues.append(Issue(
|
|
issue="Missing Cross-Origin-Embedder-Policy (COEP) header",
|
|
severity="Info",
|
|
layer="Server Config Layer",
|
|
fix="Add header: Cross-Origin-Embedder-Policy: require-corp",
|
|
))
|
|
|
|
return issues
|