2024-07-01 20:16:09 +03:00
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
2018-04-06 16:47:28 +03:00
|
|
|
import random
|
|
|
|
|
|
|
|
from django.conf import settings
|
|
|
|
|
|
|
|
from csp.middleware import CSPMiddleware
|
|
|
|
from csp.utils import build_policy
|
|
|
|
|
2024-07-01 20:16:09 +03:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from django.http import HttpRequest, HttpResponseBase
|
|
|
|
|
2018-04-06 16:47:28 +03:00
|
|
|
|
|
|
|
class RateLimitedCSPMiddleware(CSPMiddleware):
|
|
|
|
"""A CSP middleware that rate-limits the number of violation reports sent
|
|
|
|
to report-uri by excluding it from some requests."""
|
|
|
|
|
2024-07-01 20:16:09 +03:00
|
|
|
def build_policy(self, request: HttpRequest, response: HttpResponseBase) -> str:
|
2024-01-25 00:35:02 +03:00
|
|
|
config = getattr(response, "_csp_config", None)
|
|
|
|
update = getattr(response, "_csp_update", None)
|
|
|
|
replace = getattr(response, "_csp_replace", {})
|
|
|
|
nonce = getattr(request, "_csp_nonce", None)
|
2018-04-06 16:47:28 +03:00
|
|
|
|
2024-05-01 22:58:56 +03:00
|
|
|
policy = getattr(settings, "CONTENT_SECURITY_POLICY", None)
|
|
|
|
|
|
|
|
if policy is None:
|
|
|
|
return ""
|
|
|
|
|
|
|
|
report_percentage = policy.get("REPORT_PERCENTAGE", 100)
|
|
|
|
include_report_uri = random.randint(0, 100) < report_percentage
|
2018-04-06 16:47:28 +03:00
|
|
|
if not include_report_uri:
|
2024-01-25 00:35:02 +03:00
|
|
|
replace["report-uri"] = None
|
2018-04-06 16:47:28 +03:00
|
|
|
|
2024-01-25 00:35:02 +03:00
|
|
|
return build_policy(config=config, update=update, replace=replace, nonce=nonce)
|
2024-05-01 22:58:56 +03:00
|
|
|
|
2024-07-01 20:16:09 +03:00
|
|
|
def build_policy_ro(self, request: HttpRequest, response: HttpResponseBase) -> str:
|
2024-05-01 22:58:56 +03:00
|
|
|
config = getattr(response, "_csp_config_ro", None)
|
|
|
|
update = getattr(response, "_csp_update_ro", None)
|
|
|
|
replace = getattr(response, "_csp_replace_ro", {})
|
|
|
|
nonce = getattr(request, "_csp_nonce", None)
|
|
|
|
|
|
|
|
policy = getattr(settings, "CONTENT_SECURITY_POLICY_REPORT_ONLY", None)
|
|
|
|
|
|
|
|
if policy is None:
|
|
|
|
return ""
|
|
|
|
|
|
|
|
report_percentage = policy.get("REPORT_PERCENTAGE", 100)
|
|
|
|
include_report_uri = random.randint(0, 100) < report_percentage
|
|
|
|
if not include_report_uri:
|
|
|
|
replace["report-uri"] = None
|
|
|
|
|
|
|
|
return build_policy(config=config, update=update, replace=replace, nonce=nonce, report_only=True)
|