Skip to content
Open
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
3 changes: 3 additions & 0 deletions src/django_nh3/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@
class DjangoNh3AppConfig(AppConfig):
name = "django_nh3"
verbose_name = "django-nh3"

def ready(self) -> None:
from . import checks # noqa: F401
38 changes: 38 additions & 0 deletions src/django_nh3/checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from typing import Any

from django.apps import AppConfig
from django.conf import settings
from django.core.checks import CheckMessage, Tags, Warning, register


@register(Tags.security)
def check_nh3_settings(
app_configs: list[AppConfig] | None, **kwargs: Any
) -> list[CheckMessage]:
"""
Inspects the NH3_ALLOWED_ATTRIBUTES setting to ensure that the 'style'
attribute is not allowed, as nh3 does not sanitize CSS content.
"""
errors: list[CheckMessage] = []
allowed_attributes = getattr(settings, "NH3_ALLOWED_ATTRIBUTES", {})

found_style = False
if isinstance(allowed_attributes, dict):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look like this block has test coverage - at least according to the coverage report.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I have updated the tests to explicitly cover scenarios where NH3_ALLOWED_ATTRIBUTES is None or an empty dictionary. This ensures the isinstance check is fully exercised and should satisfy the coverage report.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent - thank you!

for _tag, attrs in allowed_attributes.items():
if "style" in attrs:
found_style = True
break

if found_style:
errors.append(
Warning(
"The 'style' attribute is allowed in NH3_ALLOWED_ATTRIBUTES.",
hint=(
"Allowing 'style' poses a security risk (XSS) because nh3 "
"does not sanitize CSS content. Ensure you strictly trust "
"the input if this attribute is required."
),
id="django_nh3.W001",
)
)
return errors
28 changes: 28 additions & 0 deletions tests/test_checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from django.test import override_settings

from django_nh3.checks import check_nh3_settings


@override_settings(NH3_ALLOWED_ATTRIBUTES={"div": {"style"}})
def test_check_warns_about_style():
errors = check_nh3_settings(None)
assert len(errors) == 1
assert errors[0].id == "django_nh3.W001"


@override_settings(NH3_ALLOWED_ATTRIBUTES={"div": {"class", "id"}})
def test_check_is_silent_when_safe():
errors = check_nh3_settings(None)
assert len(errors) == 0


@override_settings(NH3_ALLOWED_ATTRIBUTES={})
def test_check_handles_empty_dict():
errors = check_nh3_settings(None)
assert len(errors) == 0


@override_settings(NH3_ALLOWED_ATTRIBUTES=None)
def test_check_handles_non_dict_attributes():
errors = check_nh3_settings(None)
assert len(errors) == 0
Loading