-
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcsp.py
More file actions
39 lines (29 loc) · 1.63 KB
/
Copy pathcsp.py
File metadata and controls
39 lines (29 loc) · 1.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import secrets
from typing import NamedTuple, Optional
from modules.userscript import Userscript
# Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
class Injection(NamedTuple):
userscript: Userscript
nonce: Optional[str]
def headerWithScriptsAllowed(cspHeaderValue: str, injections: list[Injection]) -> str:
# Example CSP header:
#
# Content-Security-Policy: default-src 'self'; frame-src 'self'; img-src https:; connect-src 'self'
#
cspKeyValuePairs = [ directive.strip().split(" ", 1) for directive in cspHeaderValue.split(';') ]
cspDict = { key: value for key, value in cspKeyValuePairs }
if "script-src" not in cspDict:
# Browsers fall back to default-src if there is no script-src.
# Since there was no script-src directive and we are adding one, we include the default-src (if present) in it to avoid breaking the site's effective CSP.
cspDict["script-src"] = cspDict["default-src"] if "default-src" in cspDict else ""
sourcesToAllow = [ source(i) for i in injections ]
cspDict["script-src"] += " " + " ".join(sourcesToAllow)
return '; '.join([ f'{key} {value}' for key, value in cspDict.items() ])
def source(injection: Injection) -> str:
if injection.nonce is not None:
return f"'nonce-{injection.nonce}'"
else:
# MDN about host (i.e. download URL) sources: "Unlike other values below, single quotes shouldn't be used."
return injection.userscript.downloadURL
def generateNonce() -> str:
return secrets.token_hex() # If no argument is passed, "a reasonable default is used" for the number of bytes.