diff --git a/netbox/extras/admin.py b/netbox/extras/admin.py index cac600626..5e2de7e16 100644 --- a/netbox/extras/admin.py +++ b/netbox/extras/admin.py @@ -16,11 +16,9 @@ class ConfigRevisionAdmin(admin.ModelAdmin): ('IPAM', { 'fields': ('ENFORCE_GLOBAL_UNIQUE', 'PREFER_IPV4'), }), - # ('Security', { - # 'fields': ( - # 'ALLOWED_URL_SCHEMES', 'EXEMPT_VIEW_PERMISSIONS', - # ), - # }), + ('Security', { + 'fields': ('ALLOWED_URL_SCHEMES',), + }), ('Banners', { 'fields': ('BANNER_LOGIN', 'BANNER_TOP', 'BANNER_BOTTOM'), }), diff --git a/netbox/netbox/config/parameters.py b/netbox/netbox/config/parameters.py index 4e1ff80f4..8ad02a5dd 100644 --- a/netbox/netbox/config/parameters.py +++ b/netbox/netbox/config/parameters.py @@ -1,4 +1,5 @@ from django import forms +from django.contrib.postgres.forms import SimpleArrayField class OptionalBooleanSelect(forms.Select): @@ -68,4 +69,17 @@ PARAMS = ( field=forms.IntegerField ), + # Security + ConfigParam( + name='ALLOWED_URL_SCHEMES', + label='Allowed URL schemes', + default=( + 'file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', + 'xmpp', + ), + description="Permitted schemes for URLs in user-provided content", + field=SimpleArrayField, + field_kwargs={'base_field': forms.CharField()} + ), + ) diff --git a/netbox/netbox/configuration.example.py b/netbox/netbox/configuration.example.py index bb4a9021e..63e74524a 100644 --- a/netbox/netbox/configuration.example.py +++ b/netbox/netbox/configuration.example.py @@ -72,11 +72,6 @@ ADMINS = [ # ('John Doe', 'jdoe@example.com'), ] -# URL schemes that are allowed within links in NetBox -ALLOWED_URL_SCHEMES = ( - 'file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp', -) - # Base URL path if accessing NetBox within a directory. For example, if installed at https://example.com/netbox/, set: # BASE_PATH = 'netbox/' BASE_PATH = '' diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 248b3d697..f42c99dbf 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -122,9 +122,6 @@ for param in PARAMS: if hasattr(configuration, param.name): globals()[param.name] = getattr(configuration, param.name) -ALLOWED_URL_SCHEMES = getattr(configuration, 'ALLOWED_URL_SCHEMES', ( - 'file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp', -)) CHANGELOG_RETENTION = getattr(configuration, 'CHANGELOG_RETENTION', 90) EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', []) GRAPHQL_ENABLED = getattr(configuration, 'GRAPHQL_ENABLED', True) diff --git a/netbox/utilities/templatetags/helpers.py b/netbox/utilities/templatetags/helpers.py index 1b5bb220d..833d19535 100644 --- a/netbox/utilities/templatetags/helpers.py +++ b/netbox/utilities/templatetags/helpers.py @@ -14,6 +14,7 @@ from django.utils.html import strip_tags from django.utils.safestring import mark_safe from markdown import markdown +from netbox.config import Config from utilities.forms import get_selected_values, TableConfigForm from utilities.utils import foreground_color @@ -44,7 +45,7 @@ def render_markdown(value): value = strip_tags(value) # Sanitize Markdown links - schemes = '|'.join(settings.ALLOWED_URL_SCHEMES) + schemes = '|'.join(Config().ALLOWED_URL_SCHEMES) pattern = fr'\[(.+)\]\((?!({schemes})).*:(.+)\)' value = re.sub(pattern, '[\\1](\\3)', value, flags=re.IGNORECASE) diff --git a/netbox/utilities/validators.py b/netbox/utilities/validators.py index b087b0867..5b5775482 100644 --- a/netbox/utilities/validators.py +++ b/netbox/utilities/validators.py @@ -1,9 +1,10 @@ import re -from django.conf import settings from django.core.exceptions import ValidationError from django.core.validators import _lazy_re_compile, BaseValidator, URLValidator +from netbox.config import Config + class EnhancedURLValidator(URLValidator): """ @@ -19,7 +20,11 @@ class EnhancedURLValidator(URLValidator): r'(?::\d{2,5})?' # Port number r'(?:[/?#][^\s]*)?' # Path r'\Z', re.IGNORECASE) - schemes = settings.ALLOWED_URL_SCHEMES + + def __init__(self, schemes=None, **kwargs): + super().__init__(**kwargs) + if schemes is not None: + self.schemes = Config().ALLOWED_URL_SCHEMES class ExclusionValidator(BaseValidator):