Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for django4.0.x and drop support for django2.x #10

Closed
wants to merge 14 commits into from
Closed
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
9 changes: 0 additions & 9 deletions security_txt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,3 @@

# django-security-txt
# security_txt/__init__.py


from typing import List


__all__: List[str] = ["default_app_config"]


default_app_config: str = "security_txt.apps.DjangoSecurityTxtConfig"
91 changes: 91 additions & 0 deletions security_txt/checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-

# django-security-txt
# security_txt/checks.py

from datetime import datetime
from typing import Any, List

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

from pgpy import PGPKey

from security_txt.models.contact import Contact
from tests.settings import ROOT_URLCONF

KEY_NOT_VALID_ERROR: Error = Error(
"SECURITY_TXT_SIGN is set to True but no valid signing key can be found at SECURITY_TXT_SIGNING_KEY.",
hint="Check your SECURITY_TXT_SIGNING_KEY setting.",
id="security-txt.E001"
)
NO_CONTACT_WARNING: Warning = Warning(
"Define at least one Contact for security-txt file.",
obj=Contact,
id="security-txt.W001",
)
EXPIRY_DATE_WARNING: Warning = Warning(
"SECURITY_TXT_EXPIRES date is in the past.",
id="security-txt.W002",
)
ROOT_URL_WARNING: Warning = Warning(
"ROOT_URL of security-txt app is not standard.",
id="security-txt.W002",
)

@register(Tags.files)
def check_if_key_exists_n_valid(_: List[AppConfig] = None, **__: Any) -> List[Error]:
"""
Check if key file exists and can be loaded if SECURITY_TXT_SIGN is set to True.

:param apps_config: app configurations
:type apps_config: AppConfig???
:return: list of errors
:rtype: List[Error]
"""
errors: List[Error] = []
SECURITY_TXT_SIGN: bool = getattr(settings, 'SECURITY_TXT_SIGN', False)
SECURITY_TXT_SIGNING_KEY: str = getattr(settings, 'SECURITY_TXT_SIGNING_KEY', False)
if SECURITY_TXT_SIGN:
try:
PGPKey.from_file(filename=SECURITY_TXT_SIGNING_KEY) # type: ignore # noqa: E501
except (ValueError, PermissionError, FileNotFoundError):
errors.append(KEY_NOT_VALID_ERROR)
return errors

@register(Tags.models)
def check_for_min_one_contact(_: List[AppConfig] = None, **__: Any) -> List[Warning]:
"""
Give a warning if not at least one contact is defined.
:return: list with one warning
:rtype: List[]
"""
warnings: List[Warning] = []
if not Contact.objects.exists():
warnings.append(NO_CONTACT_WARNING)
return warnings

@register(Tags.security)
def check_for_expiration_date(_: List[AppConfig] = None, **__: Any) -> List[Warning]:
"""
Give a warning if expiration date is in the past.
:return: list with one warning
:rtype: List[Warning]
"""
warnings: List[Warning] = []
SECURITY_TXT_EXPIRES: datetime = getattr(settings, 'SECURITY_TXT_EXPIRES')
if SECURITY_TXT_EXPIRES < datetime.now():
warnings.append(EXPIRY_DATE_WARNING)
return warnings

@register(Tags.security)
def check_root_url(_: List[AppConfig] = None, **__: Any) -> List[Warning]:
"""
Give a warning if root_url doesn't accord to standard path as /.well-known/security.txt or /security.txt
:return: list with one warning
:rtype: List[Warning]
"""
warnings: List[Warning] = []
warnings.append(ROOT_URL_WARNING)
return warnings
1 change: 1 addition & 0 deletions security_txt/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
class DjangoSecurityTxtAppConf(AppConf):
"""Django security.txt settings."""

PATH: str = getattr(settings, "SECURITY_TXT_PATH", ".well-known/security.txt")
EXPIRES: datetime = getattr(settings, "SECURITY_TXT_EXPIRES", None)
PREFERRED_LANGUAGES: List[str] = getattr(
settings, "SECURITY_TXT_PREFERRED_LANGUAGES", None
Expand Down
6 changes: 6 additions & 0 deletions security_txt/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,9 @@

ENCRYPTION_DNS_REGEX: str = r"([0-9 A-F a-f]{16,64})(._openpgpkey.)((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9])(\?type=OPENPGPKEY$)" # noqa: E501
ENCRYPTION_FINGERPRINT_REGEX: str = r"([0-9 A-F a-f]{16,64}$)"

CONTENT_TYPE = "text/plain"
CHARSET = "utf8"
BEGIN_PGP_SIGNED_MESSAGE = "-----BEGIN PGP SIGNED MESSAGE-----"
PGP_HASH = "Hash: SHA256"
SEPARATOR: str = "\n"
3 changes: 3 additions & 0 deletions security_txt/models/acknowledgment.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
class Acknowledgment(models.Model): # noqa: DJ10,DJ1
"""Acknowledgment model."""

prefix: str = "Acknowledgments:"
comment: str = "# Our security acknowledgments page"

url = models.URLField(
verbose_name=_("URL"),
help_text=_("link to page where security researchers are recognized"),
Expand Down
3 changes: 3 additions & 0 deletions security_txt/models/canonical.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
class Canonical(models.Model): # noqa: DJ10,DJ1
"""Canonical model."""

prefix: str = "Canonical:"
comment: str = "# Canonical URI"

url = models.URLField(
verbose_name=_("URL"),
help_text=_(
Expand Down
3 changes: 3 additions & 0 deletions security_txt/models/contact.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
class Contact(models.Model): # noqa: DJ10,DJ1
"""Contact model."""

prefix: str = "Contact:"
comment: str = "# Our security address"

TYPE_EMAIL, TYPE_PHONE, TYPE_URL = (
CONTACT_TYPE_EMAIL,
CONTACT_TYPE_PHONE,
Expand Down
3 changes: 3 additions & 0 deletions security_txt/models/encryption.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
class Encryption(models.Model): # noqa: DJ10,DJ1
"""Encryption model."""

prefix: str = "Encryption:"
comment: str = "# Our OpenPGP key"

TYPE_URL, TYPE_DNS, TYPE_FINGERPRINT = (
ENCRYPTION_TYPE_URL,
ENCRYPTION_TYPE_DNS,
Expand Down
3 changes: 3 additions & 0 deletions security_txt/models/hiring.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
class Hiring(models.Model): # noqa: DJ10,DJ1
"""Hiring model."""

prefix: str = "Hiring:"
comment: str = ""

url = models.URLField(
verbose_name=_("URL"),
help_text=_("used for linking to the vendor's security-related job positions"),
Expand Down
39 changes: 39 additions & 0 deletions security_txt/models/languages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-

# django-security-txt
# security_txt/models/languages.py


from typing import List
from dataclasses import dataclass
from faulthandler import is_enabled

from django.conf import settings


__all__: List[str] = ["Language"]


@dataclass
class Language:
"""Placeholder for Languages model."""

prefix: str = "Preferred-Languages:"
comment: str = ""
_separator: str = ", "

@property
def is_enabled(self) -> bool:
""" """
return bool(settings.SECURITY_TXT_PREFERRED_LANGUAGES)

def __str__(self) -> str:
"""
Generates preferred languages, comma separated.

:return: preferred languages
:rtype: str
"""
return self._separator.join(
language for language in settings.SECURITY_TXT_PREFERRED_LANGUAGES
)
3 changes: 3 additions & 0 deletions security_txt/models/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
class Policy(models.Model): # noqa: DJ10,DJ1
"""Policy model."""

prefix: str = "Policy:"
comment: str = "# Our security policy"

url = models.URLField(
verbose_name=_("URL"),
help_text=_(
Expand Down
14 changes: 0 additions & 14 deletions security_txt/templates/security_txt/security_txt.txt

This file was deleted.

This file was deleted.

This file was deleted.

1 change: 0 additions & 1 deletion security_txt/templatetags/security_txt_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ def sign_security_txt(data: str) -> Dict[str, str]:
if (settings.SECURITY_TXT_SIGN and not settings.SECURITY_TXT_SIGNING_KEY) or ( # type: ignore # noqa: E501
not Path(settings.SECURITY_TXT_SIGNING_KEY).exists() # type: ignore # noqa: E501
):

raise ImproperlyConfigured

key, _ = PGPKey.from_file(filename=settings.SECURITY_TXT_SIGNING_KEY) # type: ignore # noqa: E501
Expand Down
14 changes: 5 additions & 9 deletions security_txt/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,20 @@

from typing import List, Union

from django.conf.urls import url
from django.views.generic import TemplateView
from django.urls import path
from django.urls.resolvers import URLPattern, URLResolver

from security_txt.conf import settings
from security_txt.views import SecurityTxtView


__all__ = ["urlpatterns"]


# security.txt urls
urlpatterns: List[Union[URLPattern, URLResolver]] = [
url(
r"^$",
TemplateView.as_view(
template_name="security_txt/security_txt.txt",
extra_context={"SIGN": settings.SECURITY_TXT_SIGN}, # type: ignore
),
path(
"",
SecurityTxtView.as_view(),
name="security-txt",
),
]
Loading