Nautobot's BANNER_* configuration can be used to inject arbitrary HTML content into Nautobot pages
Description
Nautobot is a Network Source of Truth and Network Automation Platform. A Nautobot user with admin privileges can modify the BANNER_TOP, BANNER_BOTTOM, and BANNER_LOGIN configuration settings via the /admin/constance/config/ endpoint. Normally these settings are used to provide custom banner text at the top and bottom of all Nautobot web pages (or specifically on the login page in the case of BANNER_LOGIN) but it was reported that an admin user can make use of these settings to inject arbitrary HTML, potentially exposing Nautobot users to security issues such as cross-site scripting (stored XSS). The vulnerability is fixed in Nautobot 1.6.22 and 2.2.4.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Admin users can inject arbitrary HTML via Nautobot banner configuration settings, leading to stored XSS against other users.
Vulnerability
CVE-2024-34707 describes a stored cross-site scripting (XSS) vulnerability in Nautobot, a Network Source of Truth and Network Automation Platform. The issue resides in the /admin/constance/config/ endpoint, where admin users can modify the BANNER_TOP, BANNER_BOTTOM, and BANNER_LOGIN configuration settings [1]. These settings are intended for custom banner text on web pages, but they lacked sufficient sanitization, allowing injection of arbitrary HTML and scripts [4].
Exploitation
To exploit this vulnerability, an attacker must have admin-level privileges in Nautobot. With those privileges, they can craft malicious HTML payloads and set them via the banner configuration endpoint. Since the banner content is stored in the application's database or configuration and rendered on all pages (or the login page), the attack does not require user interaction beyond viewing the pages where the banner appears. The lack of output encoding in the banner rendering allows the injected HTML to be executed in the context of other users' browsers [4].
Impact
A successful exploit leads to stored XSS, which can result in session theft, credential harvesting, or unauthorized actions performed on behalf of the victim user. The impact is broad because the banners are displayed to all users, including administrators, making this a potentially high-severity vector for persistent cross-site scripting attacks [1].
Mitigation
The vulnerability is patched in Nautobot versions 1.6.22 and 2.2.4. The fix adds sanitization of HTML tags in banner content and transitions to Markdown/limited HTML support to prevent script injection [2][3]. Users should upgrade to the patched versions immediately. No workarounds are available beyond restricting admin access to trusted personnel [4].
- NVD - CVE-2024-34707
- [LTM] Add 'javascript' and 'css' to BRANDING_FILEPATHS options. Sanitize BANNER_ content by glennmatthews · Pull Request #5698 · nautobot/nautobot
- Add 'javascript' and 'css' to BRANDING_FILEPATHS options. Sanitize BANNER_ content by glennmatthews · Pull Request #5697 · nautobot/nautobot
- [LTM] Add 'javascript' and 'css' to BRANDING_FILEPATHS options. Sanit… · nautobot/nautobot@4f0a66b
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
nautobotPyPI | < 1.6.22 | 1.6.22 |
nautobotPyPI | >= 2.0.0, < 2.2.4 | 2.2.4 |
Affected products
2- nautobot/nautobotv5Range: < 1.6.22
Patches
2f640aedc69c8Add 'javascript' and 'css' to BRANDING_FILEPATHS options. Sanitize BANNER_ content (#5697)
11 files changed · +106 −25
changes/1858.added+2 −0 added@@ -0,0 +1,2 @@ +Added support in `BRANDING_FILEPATHS` configuration to specify a custom `css` and/or `javascript` file to be added to Nautobot page content. +Added Markdown support to the `BANNER_TOP`, `BANNER_BOTTOM`, and `BANNER_LOGIN` configuration settings.
changes/1858.security+1 −0 added@@ -0,0 +1 @@ +Added sanitization of HTML tags in the content of `BANNER_TOP`, `BANNER_BOTTOM`, and `BANNER_LOGIN` configuration to prevent against potential injection of malicious scripts (stored XSS) via these features ([GHSA-r2hr-4v48-fjv3](https://github.com/nautobot/nautobot/security/advisories/GHSA-r2hr-4v48-fjv3)).
nautobot/core/settings.py+6 −4 modified@@ -69,7 +69,7 @@ "xmpp", ] -# Banners to display to users. HTML is allowed. +# Banners to display to users. Markdown and limited HTML are allowed. if "NAUTOBOT_BANNER_BOTTOM" in os.environ and os.environ["NAUTOBOT_BANNER_BOTTOM"] != "": BANNER_BOTTOM = os.environ["NAUTOBOT_BANNER_BOTTOM"] if "NAUTOBOT_BANNER_LOGIN" in os.environ and os.environ["NAUTOBOT_BANNER_LOGIN"] != "": @@ -686,15 +686,15 @@ ), "BANNER_BOTTOM": ConstanceConfigItem( default="", - help_text="Custom HTML to display in a banner at the bottom of all pages.", + help_text="Custom Markdown or limited HTML to display in a banner at the bottom of all pages.", ), "BANNER_LOGIN": ConstanceConfigItem( default="", - help_text="Custom HTML to display in a banner at the top of the login page.", + help_text="Custom Markdown or limited HTML to display in a banner at the top of the login page.", ), "BANNER_TOP": ConstanceConfigItem( default="", - help_text="Custom HTML to display in a banner at the top of all pages.", + help_text="Custom Markdown or limited HTML to display in a banner at the top of all pages.", ), "CHANGELOG_RETENTION": ConstanceConfigItem( default=90, @@ -972,6 +972,8 @@ "NAUTOBOT_BRANDING_FILEPATHS_HEADER_BULLET", None ), # bullet image used for various view headers "nav_bullet": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_NAV_BULLET", None), # bullet image used for nav menu headers + "css": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_CSS", None), # Custom global CSS + "javascript": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_JAVASCRIPT", None), # Custom global JavaScript } # Title to use in place of "Nautobot"
nautobot/core/settings.yaml+50 −15 modified@@ -128,82 +128,117 @@ properties: type: "array" BANNER_BOTTOM: default: "" - description: "Custom content to be displayed in a banner at the bottom of the page. HTML is allowed." + description: >- + Custom content to be displayed in a banner at the bottom of all Nautobot pages. + details: |- + +/- 2.2.4 + Markdown formatting is supported within this message, as well as + [a limited subset of HTML](../../platform-functionality/template-filters.md#render_markdown). environment_variable: "NAUTOBOT_BANNER_BOTTOM" is_constance_config: true type: "string" BANNER_LOGIN: default: "" - description: "Custom content to be displayed on the login page above the login form. HTML is allowed." + description: >- + Custom content to be displayed in a banner on the login page above the login form. + details: |- + +/- 2.2.4 + Markdown formatting is supported within this message, as well as + [a limited subset of HTML](../../platform-functionality/template-filters.md#render_markdown). environment_variable: "NAUTOBOT_BANNER_LOGIN" is_constance_config: true type: "string" BANNER_TOP: default: "" - description: "Custom content to be displayed in a banner at the top of the page. HTML is allowed." + description: >- + Custom content to be displayed in a banner at the top of all Nautobot pages. + details: |- + +/- 2.2.4 + Markdown formatting is supported within this message, as well as + [a limited subset of HTML](../../platform-functionality/template-filters.md#render_markdown). environment_variable: "NAUTOBOT_BANNER_TOP" is_constance_config: true type: "string" BRANDING_FILEPATHS: default: + css: null favicon: null header_bullet: null icon_16: null icon_32: null icon_180: null icon_192: null icon_mask: null + javascript: null logo: null nav_bullet: null description: >- - A set of filepaths relative to the [`MEDIA_ROOT`](#media_root) which locate image assets used for - custom branding. Each of these assets takes the place of the corresponding stock Nautobot asset. + A set of filepaths relative to the [`MEDIA_ROOT`](#media_root) which locate assets used for + custom branding of your Nautobot instance. + With the exception of `css` and `javascript`, which provide the option to add an _additional_ file to Nautobot + page content, each of the other assets takes the place of the corresponding stock Nautobot asset. This allows for, for instance, providing your own navbar logo and favicon. - If a custom image asset is not provided for any of the above options, the stock Nautobot asset is used. + If a custom asset is not provided for any of the above options, the stock Nautobot asset is used. + details: |- + +++ 2.1.0 + The `header_bullet` and `nav_bullet` assets were added as options. + + +++ 2.2.4 + The `css` and `javascript` assets were added as options. properties: + css: + "$ref": "#/definitions/relative_path" + default: null + description: "Custom global CSS file" + environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_CSS" favicon: "$ref": "#/definitions/relative_path" - default: "" + default: null description: "Browser favicon" environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_FAVICON" header_bullet: "$ref": "#/definitions/relative_path" - default: "" + default: null description: "Bullet image used for various view headers" environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_HEADER_BULLET" icon_16: "$ref": "#/definitions/relative_path" - default: "" + default: null description: "16x16px icon" environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_ICON_16" icon_180: "$ref": "#/definitions/relative_path" - default: "" + default: null description: "180x180px icon - used for the apple-touch-icon header" environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_ICON_180" icon_192: "$ref": "#/definitions/relative_path" - default: "" + default: null description: "192x192px icon" environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_ICON_192" icon_32: "$ref": "#/definitions/relative_path" - default: "" + default: null description: "32x32px icon" environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_ICON_32" icon_mask: "$ref": "#/definitions/relative_path" - default: "" + default: null description: "Mono-chrome icon used for the mask-icon header" environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_ICON_MASK" + javascript: + "$ref": "#/definitions/relative_path" + default: null + description: "Custom global JavaScript file" + environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_JAVASCRIPT" logo: "$ref": "#/definitions/relative_path" - default: "" + default: null description: "Navbar logo" environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_LOGO" nav_bullet: "$ref": "#/definitions/relative_path" - default: "" + default: null description: "Bullet image used for nav menu headers" environment_variable: "NAUTOBOT_BRANDING_FILEPATHS_NAV_BULLET" type: "object"
nautobot/core/templates/admin/base.html+2 −2 modified@@ -56,7 +56,7 @@ {% if not is_popup %} {% if "BANNER_TOP"|settings_or_config %} <div class="alert alert-info text-center" role="alert"> - {{ "BANNER_TOP"|settings_or_config|safe }} + {{ "BANNER_TOP"|settings_or_config|render_markdown }} </div> {% endif %} {% if settings.MAINTENANCE_MODE %} @@ -107,7 +107,7 @@ <h1>{% render_with_template_if_exist title|lower|add:"/admin_app_name.html" titl <div class="push"></div> {% if "BANNER_BOTTOM"|settings_or_config %} <div class="alert alert-info text-center banner-bottom" role="alert"> - {{ "BANNER_BOTTOM"|settings_or_config|safe }} + {{ "BANNER_BOTTOM"|settings_or_config|render_markdown }} </div> {% endif %} </div>
nautobot/core/templates/base_django.html+2 −2 modified@@ -16,7 +16,7 @@ {% if request.user.is_authenticated %} {% if "BANNER_TOP"|settings_or_config %} <div class="alert alert-info text-center" role="alert"> - {{ "BANNER_TOP"|settings_or_config|safe }} + {{ "BANNER_TOP"|settings_or_config|render_markdown }} </div> {% endif %} {% endif %} @@ -42,7 +42,7 @@ <h4><i class="mdi mdi-alert"></i> Maintenance Mode</h4> {% if request.user.is_authenticated %} {% if "BANNER_BOTTOM"|settings_or_config %} <div class="alert alert-info text-center banner-bottom" role="alert"> - {{ "BANNER_BOTTOM"|settings_or_config|safe }} + {{ "BANNER_BOTTOM"|settings_or_config|render_markdown }} </div> {% endif %} {% endif %}
nautobot/core/templates/inc/javascript.html+3 −0 modified@@ -40,3 +40,6 @@ hljs.configure({ cssSelector: '.language-graphql, .language-json, .language-xml, .language-yaml' }); hljs.highlightAll(); </script> +{% if settings.BRANDING_FILEPATHS.javascript %} +<script src="{% custom_branding_or_static 'javascript' None %}"></script> +{% endif %}
nautobot/core/templates/inc/media.html+3 −0 modified@@ -34,6 +34,9 @@ <link rel="stylesheet" id="base-theme" href="{% versioned_static 'css/base.css' %}" onerror="window.location='{% url 'media_failure' %}?filename=css/base.css'"> + {% if settings.BRANDING_FILEPATHS.css %} + <link rel="stylesheet" id="custom-css" href="{% custom_branding_or_static 'css' None %}"> + {% endif %} <link rel="apple-touch-icon" sizes="180x180" href="{% custom_branding_or_static 'icon_180' 'img/nautobot_icon_180x180.png' %}"> <link rel="icon" type="image/png" sizes="32x32" href="{% custom_branding_or_static 'icon_32' 'img/nautobot_icon_32x32.png' %}"> <link rel="icon" type="image/png" sizes="16x16" href="{% custom_branding_or_static 'icon_16' 'img/nautobot_icon_16x16.png' %}">
nautobot/core/templates/login.html+2 −2 modified@@ -53,8 +53,8 @@ <div class="row" style="margin-top: {% if 'BANNER_LOGIN'|settings_or_config %}100{% else %}150{% endif %}px;"> <div class="col-sm-4 col-sm-offset-4"> {% if "BANNER_LOGIN"|settings_or_config %} - <div style="margin-bottom: 25px"> - {{ "BANNER_LOGIN"|settings_or_config|safe }} + <div class="alert alert-info text-center" role="alert"> + {{ "BANNER_LOGIN"|settings_or_config|render_markdown }} </div> {% endif %} {% if form.non_field_errors %}
nautobot/core/templates/nautobot_config.py.j2+2 −0 modified@@ -248,6 +248,8 @@ SECRET_KEY = os.getenv("NAUTOBOT_SECRET_KEY", "{{ secret_key }}") # "NAUTOBOT_BRANDING_FILEPATHS_HEADER_BULLET", None # ), # bullet image used for various view headers # "nav_bullet": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_NAV_BULLET", None), # bullet image used for nav menu headers +# "css": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_CSS", None), # Custom global CSS +# "javascript": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_JAVASCRIPT", None), # Custom global JavaScript # } # Prepended to CSV, YAML and export template filenames (i.e. `nautobot_device.yml`)
nautobot/core/tests/test_views.py+33 −0 modified@@ -146,6 +146,39 @@ def test_footer_version_visible_authenticated_users_only(self): response_content = response.content.decode(response.charset).replace("\n", "") self.assertNotRegex(response_content, footer_hostname_version_pattern) + def test_banners_markdown(self): + url = reverse("home") + with override_settings( + BANNER_TOP="# Hello world", + BANNER_BOTTOM="[info](https://nautobot.com)", + ): + response = self.client.get(url) + self.assertInHTML("<h1>Hello world</h1>", response.content.decode(response.charset)) + self.assertInHTML( + '<a href="https://nautobot.com" rel="noopener noreferrer">info</a>', + response.content.decode(response.charset), + ) + + with override_settings(BANNER_LOGIN="_Welcome to Nautobot!_"): + self.client.logout() + response = self.client.get(reverse("login")) + self.assertInHTML("<em>Welcome to Nautobot!</em>", response.content.decode(response.charset)) + + def test_banners_no_xss(self): + url = reverse("home") + with override_settings( + BANNER_TOP='<script>alert("Hello from above!");</script>', + BANNER_BOTTOM='<script>alert("Hello from below!");</script>', + ): + response = self.client.get(url) + self.assertNotIn("Hello from above", response.content.decode(response.charset)) + self.assertNotIn("Hello from below", response.content.decode(response.charset)) + + with override_settings(BANNER_LOGIN='<script>alert("Welcome to Nautobot!");</script>'): + self.client.logout() + response = self.client.get(reverse("login")) + self.assertNotIn("Welcome to Nautobot!", response.content.decode(response.charset)) + @override_settings(BRANDING_TITLE="Nautobot") class SearchFieldsTestCase(TestCase):
4f0a66bd6307[LTM] Add 'javascript' and 'css' to BRANDING_FILEPATHS options. Sanitize BANNER_ content (#5698)
12 files changed · +94 −13
changes/1858.added+2 −0 added@@ -0,0 +1,2 @@ +Added support in `BRANDING_FILEPATHS` configuration to specify a custom `css` and/or `javascript` file to be added to Nautobot page content. +Added Markdown support to the `BANNER_TOP`, `BANNER_BOTTOM`, and `BANNER_LOGIN` configuration settings.
changes/1858.security+1 −0 added@@ -0,0 +1 @@ +Added sanitization of HTML tags in the content of `BANNER_TOP`, `BANNER_BOTTOM`, and `BANNER_LOGIN` configuration to prevent against potential injection of malicious scripts (stored XSS) via these features ([GHSA-r2hr-4v48-fjv3](https://github.com/nautobot/nautobot/security/advisories/GHSA-r2hr-4v48-fjv3)).
nautobot/core/settings.py+13 −3 modified@@ -59,6 +59,14 @@ "xmpp", ) +# Banners to display to users. Markdown and limited HTML are allowed. +if "NAUTOBOT_BANNER_BOTTOM" in os.environ and os.environ["NAUTOBOT_BANNER_BOTTOM"] != "": + BANNER_BOTTOM = os.environ["NAUTOBOT_BANNER_BOTTOM"] +if "NAUTOBOT_BANNER_LOGIN" in os.environ and os.environ["NAUTOBOT_BANNER_LOGIN"] != "": + BANNER_LOGIN = os.environ["NAUTOBOT_BANNER_LOGIN"] +if "NAUTOBOT_BANNER_TOP" in os.environ and os.environ["NAUTOBOT_BANNER_TOP"] != "": + BANNER_TOP = os.environ["NAUTOBOT_BANNER_TOP"] + # Base directory wherein all created files (jobs, git repositories, file uploads, static files) will be stored) NAUTOBOT_ROOT = os.getenv("NAUTOBOT_ROOT", os.path.expanduser("~/.nautobot")) @@ -586,15 +594,15 @@ ], "BANNER_BOTTOM": [ "", - "Custom HTML to display in a banner at the bottom of all pages.", + "Custom Markdown or limited HTML to display in a banner at the bottom of all pages.", ], "BANNER_LOGIN": [ "", - "Custom HTML to display in a banner at the top of the login page.", + "Custom Markdown or limited HTML to display in a banner at the top of the login page.", ], "BANNER_TOP": [ "", - "Custom HTML to display in a banner at the top of all pages.", + "Custom Markdown or limited HTML to display in a banner at the top of all pages.", ], "CHANGELOG_RETENTION": [ 90, @@ -869,6 +877,8 @@ "icon_mask": os.getenv( "NAUTOBOT_BRANDING_FILEPATHS_ICON_MASK", None ), # mono-chrome icon used for the mask-icon header + "css": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_CSS", None), # Custom global CSS + "javascript": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_JAVASCRIPT", None), # Custom global JavaScript } # Title to use in place of "Nautobot"
nautobot/core/templates/admin/base.html+8 −2 modified@@ -32,6 +32,9 @@ <link rel="stylesheet" id="base-theme" href="{% static 'css/base.css' %}?v{{ settings.VERSION }}" onerror="window.location='{% url 'media_failure' %}?filename=css/base.css'"> + {% if settings.BRANDING_FILEPATHS.css %} + <link rel="stylesheet" id="custom-css" href="{% custom_branding_or_static 'css' None %}"> + {% endif %} <link rel="apple-touch-icon" sizes="180x180" href="{% static 'img/nautobot_icon_180x180.png' %}"> <link rel="icon" type="image/png" sizes="32x32" href="{% static 'img/nautobot_icon_32x32.png' %}"> <link rel="icon" type="image/png" sizes="16x16" href="{% static 'img/nautobot_icon_16x16.png' %}"> @@ -101,7 +104,7 @@ <div class="container-fluid wrapper" {% if is_popup %}style="padding-bottom: 0px;"{% endif %}> {% if "BANNER_TOP"|settings_or_config %} <div class="alert alert-info text-center" role="alert"> - {{ "BANNER_TOP"|settings_or_config|safe }} + {{ "BANNER_TOP"|settings_or_config|render_markdown }} </div> {% endif %} {% if settings.MAINTENANCE_MODE %} @@ -153,7 +156,7 @@ <h1>{% render_with_template_if_exist title|lower|add:"/admin_app_name.html" titl <div class="push"></div> {% if "BANNER_BOTTOM"|settings_or_config %} <div class="alert alert-info text-center banner-bottom" role="alert"> - {{ "BANNER_BOTTOM"|settings_or_config|safe }} + {{ "BANNER_BOTTOM"|settings_or_config|render_markdown }} </div> {% endif %} </div> @@ -189,6 +192,9 @@ <h1>{% render_with_template_if_exist title|lower|add:"/admin_app_name.html" titl {% endif %} {% include 'modals/modal_theme.html' with name='theme'%} <script src="{% static 'js/theme.js' %}"></script> +{% if settings.BRANDING_FILEPATHS.javascript %} +<script src="{% custom_branding_or_static 'javascript' None %}"></script> +{% endif %} {% block javascript %}{% endblock %} </body> </html>
nautobot/core/templates/base.html+2 −2 modified@@ -15,7 +15,7 @@ {% if request.user.is_authenticated or not "HIDE_RESTRICTED_UI"|settings_or_config %} {% if "BANNER_TOP"|settings_or_config %} <div class="alert alert-info text-center" role="alert"> - {{ "BANNER_TOP"|settings_or_config|safe }} + {{ "BANNER_TOP"|settings_or_config|render_markdown }} </div> {% endif %} {% endif %} @@ -40,7 +40,7 @@ <h4><i class="mdi mdi-alert"></i> Maintenance Mode</h4> {% if request.user.is_authenticated or not "HIDE_RESTRICTED_UI"|settings_or_config %} {% if "BANNER_BOTTOM"|settings_or_config %} <div class="alert alert-info text-center banner-bottom" role="alert"> - {{ "BANNER_BOTTOM"|settings_or_config|safe }} + {{ "BANNER_BOTTOM"|settings_or_config|render_markdown }} </div> {% endif %} {% endif %}
nautobot/core/templates/graphene/graphiql.html+3 −0 modified@@ -36,6 +36,9 @@ <link rel="stylesheet" href="{% static 'css/base.css' %}?v{{ settings.VERSION }}" onerror="window.location='{% url 'media_failure' %}?filename=css/base.css'"> + {% if settings.BRANDING_FILEPATHS.css %} + <link rel="stylesheet" id="custom-css" href="{% custom_branding_or_static 'css' None %}"> + {% endif %} <link rel="apple-touch-icon" sizes="180x180" href="{% custom_branding_or_static 'icon_180' 'img/nautobot_icon_180x180.png' %}"> <link rel="icon" type="image/png" sizes="32x32" href="{% custom_branding_or_static 'icon_32' 'img/nautobot_icon_32x32.png' %}"> <link rel="icon" type="image/png" sizes="16x16" href="{% custom_branding_or_static 'icon_16' 'img/nautobot_icon_16x16.png' %}">
nautobot/core/templates/inc/javascript.html+3 −0 modified@@ -52,3 +52,6 @@ dropdown.removeClass('edge'); }) </script> +{% if settings.BRANDING_FILEPATHS.javascript %} +<script src="{% custom_branding_or_static 'javascript' None %}"></script> +{% endif %}
nautobot/core/templates/inc/media.html+3 −0 modified@@ -26,6 +26,9 @@ <link rel="stylesheet" id="base-theme" href="{% static 'css/base.css' %}?v{{ settings.VERSION }}" onerror="window.location='{% url 'media_failure' %}?filename=css/base.css'"> + {% if settings.BRANDING_FILEPATHS.css %} + <link rel="stylesheet" id="custom-css" href="{% custom_branding_or_static 'css' None %}"> + {% endif %} <link rel="apple-touch-icon" sizes="180x180" href="{% custom_branding_or_static 'icon_180' 'img/nautobot_icon_180x180.png' %}"> <link rel="icon" type="image/png" sizes="32x32" href="{% custom_branding_or_static 'icon_32' 'img/nautobot_icon_32x32.png' %}"> <link rel="icon" type="image/png" sizes="16x16" href="{% custom_branding_or_static 'icon_16' 'img/nautobot_icon_16x16.png' %}">
nautobot/core/templates/login.html+2 −2 modified@@ -53,8 +53,8 @@ <div class="row" style="margin-top: {% if 'BANNER_LOGIN'|settings_or_config %}100{% else %}150{% endif %}px;"> <div class="col-sm-4 col-sm-offset-4"> {% if "BANNER_LOGIN"|settings_or_config %} - <div style="margin-bottom: 25px"> - {{ "BANNER_LOGIN"|settings_or_config|safe }} + <div class="alert alert-info text-center" role="alert"> + {{ "BANNER_LOGIN"|settings_or_config|render_markdown }} </div> {% endif %} {% if form.non_field_errors %}
nautobot/core/templates/nautobot_config.py.j2+2 −0 modified@@ -263,6 +263,8 @@ SECRET_KEY = os.getenv("NAUTOBOT_SECRET_KEY", "{{ secret_key }}") # "icon_mask": os.getenv( # "NAUTOBOT_BRANDING_FILEPATHS_ICON_MASK", None # ), # mono-chrome icon used for the mask-icon header +# "css": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_CSS", None), # Custom global CSS +# "javascript": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_JAVASCRIPT", None), # Custom global JavaScript # } # Prepended to CSV, YAML and export template filenames (i.e. `nautobot_device.yml`)
nautobot/core/tests/test_views.py+33 −0 modified@@ -99,6 +99,39 @@ def test_footer_version_visible_authenticated_users_only(self): response_content = response.content.decode(response.charset).replace("\n", "") self.assertNotRegex(response_content, footer_hostname_version_pattern) + def test_banners_markdown(self): + url = reverse("home") + with override_settings( + BANNER_TOP="# Hello world", + BANNER_BOTTOM="[info](https://nautobot.com)", + ): + response = self.client.get(url) + self.assertInHTML("<h1>Hello world</h1>", response.content.decode(response.charset)) + self.assertInHTML( + '<a href="https://nautobot.com" rel="noopener noreferrer">info</a>', + response.content.decode(response.charset), + ) + + with override_settings(BANNER_LOGIN="_Welcome to Nautobot!_"): + self.client.logout() + response = self.client.get(reverse("login")) + self.assertInHTML("<em>Welcome to Nautobot!</em>", response.content.decode(response.charset)) + + def test_banners_no_xss(self): + url = reverse("home") + with override_settings( + BANNER_TOP='<script>alert("Hello from above!");</script>', + BANNER_BOTTOM='<script>alert("Hello from below!");</script>', + ): + response = self.client.get(url) + self.assertNotIn("Hello from above", response.content.decode(response.charset)) + self.assertNotIn("Hello from below", response.content.decode(response.charset)) + + with override_settings(BANNER_LOGIN='<script>alert("Welcome to Nautobot!");</script>'): + self.client.logout() + response = self.client.get(reverse("login")) + self.assertNotIn("Welcome to Nautobot!", response.content.decode(response.charset)) + @override_settings(BRANDING_TITLE="Nautobot") class SearchFieldsTestCase(TestCase):
nautobot/docs/configuration/optional-settings.md+22 −4 modified@@ -66,7 +66,14 @@ A list of permitted URL schemes referenced when rendering links within Nautobot. Default: `""` (Empty string) -Setting these variables will display custom content in a banner at the top and/or bottom of the page, respectively. HTML is allowed. To replicate the content of the top banner in the bottom banner, set: +Environment Variable: `NAUTOBOT_BANNER_TOP` +Environment Variable: `NAUTOBOT_BANNER_BOTTOM` + +Setting these variables will display custom content in a banner at the top and/or bottom of the page, respectively. + +Markdown formatting is supported within these messages, as well as [a limited subset of HTML](../additional-features/template-filters.md#render_markdown). + +To replicate the content of the top banner in the bottom banner, set: ```python BANNER_TOP = 'Your banner text' @@ -82,7 +89,11 @@ BANNER_BOTTOM = BANNER_TOP Default: `""` (Empty string) -This defines custom content to be displayed on the login page above the login form. HTML is allowed. +Environment Variable: `NAUTOBOT_BANNER_LOGIN` + +This defines custom content to be displayed on the login page above the login form. + +Markdown formatting is supported within this message, as well as [a limited subset of HTML](../additional-features/template-filters.md#render_markdown). +++ 1.2.0 If you do not set a value for this setting in your `nautobot_config.py`, it can be configured dynamically by an admin user via the Nautobot Admin UI. If you do have a value for this setting in `nautobot_config.py`, it will override any dynamically configured value. @@ -102,10 +113,12 @@ Default: "icon_180": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_ICON_180", None), # 180x180px icon - used for the apple-touch-icon header "icon_192": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_ICON_192", None), # 192x192px icon "icon_mask": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_ICON_MASK", None), # mono-chrome icon used for the mask-icon header + "css": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_CSS", None), # custom global CSS file + "javascript": os.getenv("NAUTOBOT_BRANDING_FILEPATHS_JAVASCRIPT", None), # custom global Javascript file } ``` -A set of filepaths relative to the [`MEDIA_ROOT`](#media_root) which locate image assets used for custom branding. Each of these assets takes the place of the corresponding stock Nautobot asset. This allows for instance, providing your own navbar logo and favicon. +A set of filepaths relative to the [`MEDIA_ROOT`](#media_root) which locate assets used for custom branding of your Nautobot instance. With the exception of `css` and `javascript`, which provide the option to add an _additional_ file to Nautobot page content, each of the other assets takes the place of the corresponding stock Nautobot asset. This allows for instance, providing your own navbar logo and favicon. These environment variables may be used to specify the values: @@ -116,8 +129,13 @@ These environment variables may be used to specify the values: * `NAUTOBOT_BRANDING_FILEPATHS_ICON_180` * `NAUTOBOT_BRANDING_FILEPATHS_ICON_192` * `NAUTOBOT_BRANDING_FILEPATHS_ICON_MASK` +* `NAUTOBOT_BRANDING_FILEPATHS_CSS` +* `NAUTOBOT_BRANDING_FILEPATHS_JAVASCRIPT` + +If a custom asset is not provided for any of the above options, the stock Nautobot asset is used. -If a custom image asset is not provided for any of the above options, the stock Nautobot asset is used. ++++ 1.6.22 + The `css` and `javascript` assets were added as options. ---
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-r2hr-4v48-fjv3ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-34707ghsaADVISORY
- github.com/nautobot/nautobot/commit/4f0a66bd6307bfe0e0acb899233e0d4ad516f51cghsax_refsource_MISCWEB
- github.com/nautobot/nautobot/commit/f640aedc69c848d3d1be57f0300fc40033ff6423ghsax_refsource_MISCWEB
- github.com/nautobot/nautobot/pull/5697ghsax_refsource_MISCWEB
- github.com/nautobot/nautobot/pull/5698ghsax_refsource_MISCWEB
- github.com/nautobot/nautobot/security/advisories/GHSA-r2hr-4v48-fjv3ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.