Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions csp/tests/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ def test_both_headers() -> None:
assert HEADER_REPORT_ONLY in response


@override_settings(
CONTENT_SECURITY_POLICY={"DIRECTIVES": {"default-src": {"example.com"}}},
CONTENT_SECURITY_POLICY_REPORT_ONLY={"DIRECTIVES": {"default-src": {SELF}}},
)
def test_directives_configured_as_sets() -> None:
request = rf.get("/")
response = HttpResponse()
mw.process_response(request, response)
assert HEADER in response
assert HEADER_REPORT_ONLY in response


def test_exempt() -> None:
request = rf.get("/")
response = HttpResponse()
Expand Down
2 changes: 1 addition & 1 deletion csp/tests/test_templatetags.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_async_attribute_with_falsey(self) -> None:
{% script src="foo.com/bar.js" async=False %}
{% endscript %}"""

expected = '<script nonce="{}" src="foo.com/bar.js" async=false>' "</script>"
expected = '<script nonce="{}" src="foo.com/bar.js" async=false></script>'

self.assert_template_eq(*self.process_templates(tpl, expected))

Expand Down
54 changes: 54 additions & 0 deletions csp/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ def test_default_src() -> None:
policy_eq("default-src example.com example2.com", policy)


@override_settings(CONTENT_SECURITY_POLICY={"DIRECTIVES": {"default-src": {"example.com", "example2.com"}}})
def test_default_src_is_set() -> None:
policy = build_policy()
policy_eq("default-src example.com example2.com", policy)


@override_settings(CONTENT_SECURITY_POLICY={"DIRECTIVES": {"script-src": ["example.com"]}})
def test_script_src() -> None:
policy = build_policy()
Expand Down Expand Up @@ -212,6 +218,25 @@ def test_replace_string() -> None:
policy_eq("default-src 'self'; img-src example2.com", policy)


@override_settings(CONTENT_SECURITY_POLICY={"DIRECTIVES": {"img-src": ("example.com",)}})
def test_update_set() -> None:
"""
GitHub issue #40 - given project settings as a tuple, and
an update/replace with a string, concatenate correctly.
"""
policy = build_policy(update={"img-src": {"example2.com"}})
policy_eq("default-src 'self'; img-src example.com example2.com", policy)


@override_settings(CONTENT_SECURITY_POLICY={"DIRECTIVES": {"img-src": ("example.com",)}})
def test_replace_set() -> None:
"""
Demonstrate that GitHub issue #40 doesn't affect replacements
"""
policy = build_policy(replace={"img-src": {"example2.com"}})
policy_eq("default-src 'self'; img-src example2.com", policy)


@override_settings(CONTENT_SECURITY_POLICY={"DIRECTIVES": {"form-action": ["example.com"]}})
def test_form_action() -> None:
policy = build_policy()
Expand Down Expand Up @@ -313,6 +338,35 @@ def test_only_nonce_in_value() -> None:
policy_eq("default-src 'nonce-abc123'", policy)


@override_settings(CONTENT_SECURITY_POLICY={"DIRECTIVES": {"img-src": ["example.com", "example.com"]}})
def test_deduplicate_values() -> None:
"""
GitHub issue #40 - given project settings as a tuple, and
an update/replace with a string, concatenate correctly.
"""
policy = build_policy()
policy_eq("default-src 'self'; img-src example.com", policy)


@override_settings(CONTENT_SECURITY_POLICY={"DIRECTIVES": {"img-src": ["example.com", "example.com"]}})
def test_deduplicate_values_update() -> None:
"""
GitHub issue #40 - given project settings as a tuple, and
an update/replace with a string, concatenate correctly.
"""
policy = build_policy(update={"img-src": "example.com"})
policy_eq("default-src 'self'; img-src example.com", policy)


@override_settings(CONTENT_SECURITY_POLICY={"DIRECTIVES": {"img-src": ("example.com",)}})
def test_deduplicate_values_replace() -> None:
"""
Demonstrate that GitHub issue #40 doesn't affect replacements
"""
policy = build_policy(replace={"img-src": ["example2.com", "example2.com"]})
policy_eq("default-src 'self'; img-src example2.com", policy)


def test_boolean_directives() -> None:
for directive in ["upgrade-insecure-requests", "block-all-mixed-content"]:
with override_settings(CONTENT_SECURITY_POLICY={"DIRECTIVES": {directive: True}}):
Expand Down
6 changes: 6 additions & 0 deletions csp/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,17 @@ def build_policy(
v = config[k]
if v is not None:
v = copy.copy(v)
if isinstance(v, set):
v = sorted(v)
if not isinstance(v, (list, tuple)):
v = (v,)
csp[k] = v

for k, v in update.items():
if v is not None:
v = copy.copy(v)
if isinstance(v, set):
v = sorted(v)
if not isinstance(v, (list, tuple)):
v = (v,)
if csp.get(k) is None:
Expand All @@ -128,6 +133,7 @@ def build_policy(
# Strip the `NONCE` sentinel value if no nonce is provided.
value = [v for v in value if v != NONCE]

value = list(dict.fromkeys(value)) # Deduplicate
policy_parts[key] = " ".join(value)

if report_uri:
Expand Down
Loading