Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:horizon
User: [email protected]
Usertags: pu

Hi,

[ Reason ]
I'd like to fix what's been disclosed as:
https://wiki.openstack.org/wiki/OSSN/OSSN-0097

[ Impact ]
Script injection in openrc file served by Horizon.

[ Tests ]
Extensive unit tests suite from upstream are run when building package.

[ Risks ]
Not much, patch is simple.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]
Change in the list of escaped chars.

Please allow me to upload Horizon 3:25.3.0-3+deb13u1 to Trixie.

Cheers,

Thomas Goirand (zigo)
diff -Nru horizon-25.3.0/debian/changelog horizon-25.3.0/debian/changelog
--- horizon-25.3.0/debian/changelog     2025-07-10 11:09:04.000000000 +0200
+++ horizon-25.3.0/debian/changelog     2026-06-09 10:20:14.000000000 +0200
@@ -1,3 +1,11 @@
+horizon (3:25.3.0-3+deb13u1) trixie; urgency=medium
+
+  * OSSN-0097: Horizon RC file generation does not escape special characters in
+    project. Applied upstream patch: "Escape $ character in shellfilter, and
+    use it consistently" (Closes: #1138845).
+
+ -- Thomas Goirand <[email protected]>  Tue, 09 Jun 2026 10:20:14 +0200
+
 horizon (3:25.3.0-3) unstable; urgency=medium
 
   * Refreshed:
diff -Nru 
horizon-25.3.0/debian/patches/OSSN-0097_Escape_dollar_character_in_shellfilter_and_use_it_consistently.patch
 
horizon-25.3.0/debian/patches/OSSN-0097_Escape_dollar_character_in_shellfilter_and_use_it_consistently.patch
--- 
horizon-25.3.0/debian/patches/OSSN-0097_Escape_dollar_character_in_shellfilter_and_use_it_consistently.patch
        1970-01-01 01:00:00.000000000 +0100
+++ 
horizon-25.3.0/debian/patches/OSSN-0097_Escape_dollar_character_in_shellfilter_and_use_it_consistently.patch
        2026-06-09 10:20:14.000000000 +0200
@@ -0,0 +1,133 @@
+From 933adb45c43ac5571f1f8d00526a673016f9b0a8 Mon Sep 17 00:00:00 2001
+From: Radomir Dopieralski <[email protected]>
+Date: Tue, 12 May 2026 19:18:50 +0200
+Subject: [PATCH] Escape $ character in shellfilter, and use it consistently
+
+Make sure the special characters included in user-provided values
+don't break the shell scripts generated by Horizon.
+
+Fixes-bug: 2152240
+
+Change-Id: Iec01e820a7ff0d4d3bed7f8929cb9c3229deb502
+Signed-off-by: Radomir Dopieralski <[email protected]>
+---
+
+diff --git a/horizon/templatetags/shellfilter.py 
b/horizon/templatetags/shellfilter.py
+index 167a222..a3c83a5 100644
+--- a/horizon/templatetags/shellfilter.py
++++ b/horizon/templatetags/shellfilter.py
+@@ -23,6 +23,8 @@
+     """Replace HTML chars for shell usage."""
+     replacements = {'\\': '\\\\',
+                     '`': '\\`',
++                    '$': '\\$',
++                    '!': '\\!',
+                     "'": "\\'",
+                     '"': '\\"'}
+     for search, repl in replacements.items():
+diff --git 
a/openstack_dashboard/dashboards/identity/application_credentials/templates/application_credentials/openrc.sh.template
 
b/openstack_dashboard/dashboards/identity/application_credentials/templates/application_credentials/openrc.sh.template
+index 670cc33..d04d26f 100644
+--- 
a/openstack_dashboard/dashboards/identity/application_credentials/templates/application_credentials/openrc.sh.template
++++ 
b/openstack_dashboard/dashboards/identity/application_credentials/templates/application_credentials/openrc.sh.template
+@@ -1,9 +1,9 @@
+ {% load shellfilter %}#!/usr/bin/env bash
+ 
+ export OS_AUTH_TYPE=v3applicationcredential
+-export OS_AUTH_URL={{ auth_url }}
++export OS_AUTH_URL="{{ auth_url|shellfilter }}"
+ export OS_IDENTITY_API_VERSION=3
+ export OS_REGION_NAME="{{ region|shellfilter }}"
+-export OS_INTERFACE={{ interface }}
+-export OS_APPLICATION_CREDENTIAL_ID={{ application_credential_id }}
+-export OS_APPLICATION_CREDENTIAL_SECRET={{ application_credential_secret }}
++export OS_INTERFACE="{{ interface|shellfilter }}"
++export OS_APPLICATION_CREDENTIAL_ID="{{ application_credential_id|shellfilter 
}}"
++export OS_APPLICATION_CREDENTIAL_SECRET="{{ 
application_credential_secret|shellfilter }}"
+diff --git 
a/openstack_dashboard/dashboards/project/api_access/templates/api_access/openrc.sh.template
 
b/openstack_dashboard/dashboards/project/api_access/templates/api_access/openrc.sh.template
+index ff576a0..a3bd3df 100644
+--- 
a/openstack_dashboard/dashboards/project/api_access/templates/api_access/openrc.sh.template
++++ 
b/openstack_dashboard/dashboards/project/api_access/templates/api_access/openrc.sh.template
+@@ -11,11 +11,11 @@
+ # OpenStack API is version 3. For example, your cloud provider may implement
+ # Image API v1.1, Block Storage API v2, and Compute API v2.0. OS_AUTH_URL is
+ # only for the Identity API served through keystone.
+-export OS_AUTH_URL={{ auth_url }}
++export OS_AUTH_URL="{{ auth_url|shellfilter }}"
+ 
+ # With the addition of Keystone we have standardized on the term **project**
+ # as the entity that owns the resources.
+-export OS_PROJECT_ID={{ tenant_id }}
++export OS_PROJECT_ID="{{ tenant_id|shellfilter }}"
+ export OS_PROJECT_NAME="{{ tenant_name|shellfilter }}"
+ export OS_USER_DOMAIN_NAME="{{ user_domain_name|shellfilter }}"
+ if [ -z "$OS_USER_DOMAIN_NAME" ]; then unset OS_USER_DOMAIN_NAME; fi
+@@ -33,7 +33,7 @@
+ # With Keystone you pass the keystone password.
+ echo "Please enter your OpenStack Password for project $OS_PROJECT_NAME as 
user $OS_USERNAME: "
+ read -sr OS_PASSWORD_INPUT
+-export OS_PASSWORD=$OS_PASSWORD_INPUT
++export OS_PASSWORD="$OS_PASSWORD_INPUT"
+ 
+ # If your configuration has multiple regions, we set that information here.
+ # OS_REGION_NAME is optional and only valid in certain environments.
+@@ -41,5 +41,5 @@
+ # Don't leave a blank variable, unset it if it was empty
+ if [ -z "$OS_REGION_NAME" ]; then unset OS_REGION_NAME; fi
+ 
+-export OS_INTERFACE={{ interface }}
+-export OS_IDENTITY_API_VERSION={{ os_identity_api_version }}
++export OS_INTERFACE="{{ interface|shellfilter }}"
++export OS_IDENTITY_API_VERSION="{{ os_identity_api_version|shellfilter }}"
+diff --git a/openstack_dashboard/dashboards/project/api_access/tests.py 
b/openstack_dashboard/dashboards/project/api_access/tests.py
+index 66d5709..044a3e7 100644
+--- a/openstack_dashboard/dashboards/project/api_access/tests.py
++++ b/openstack_dashboard/dashboards/project/api_access/tests.py
+@@ -55,7 +55,7 @@
+         openrc = 'project/api_access/openrc.sh.template'
+         self.assertTemplateUsed(res, openrc)
+         name = 'export OS_USERNAME="{}"'.format(self.request.user.username)
+-        p_id = 'export OS_PROJECT_ID={}'.format(self.request.user.tenant_id)
++        p_id = 'export OS_PROJECT_ID="{}"'.format(self.request.user.tenant_id)
+         domain = 'export OS_USER_DOMAIN_NAME="{}"'.format(
+             self.request.user.user_domain_name)
+         self.assertIn(name.encode('utf-8'), res.content)
+@@ -215,7 +215,7 @@
+             context,
+             template.Context(context))
+ 
+-        self.assertIn("OS_REGION_NAME=\"Colorado\"", out)
++        self.assertIn('OS_REGION_NAME="Colorado"', out)
+ 
+     def test_openrc_region_not_set(self):
+         context = {
+@@ -228,7 +228,7 @@
+             context,
+             template.Context(context))
+ 
+-        self.assertIn("OS_REGION_NAME=\"\"", out)
++        self.assertIn('OS_REGION_NAME=""', out)
+ 
+     def test_clouds_yaml_set_region(self):
+         context = {
+diff --git a/openstack_dashboard/test/selenium/integration/test_credentials.py 
b/openstack_dashboard/test/selenium/integration/test_credentials.py
+index 1ba18af..fde0e91 100644
+--- a/openstack_dashboard/test/selenium/integration/test_credentials.py
++++ b/openstack_dashboard/test/selenium/integration/test_credentials.py
+@@ -54,7 +54,7 @@
+     project_name = re.search(
+         r'export OS_PROJECT_NAME="([^"]+)"', content).group(1)
+     project_id = re.search(
+-        r'export OS_PROJECT_ID=(.+)', content).group(1)
++        r'export OS_PROJECT_ID="([^"]+)"', content).group(1)
+ 
+     assert(config.identity.admin_username == username and
+            config.identity.admin_home_project == project_name and
+@@ -86,7 +86,7 @@
+     project_name = re.search(
+         r'export OS_PROJECT_NAME="([^"]+)"', content).group(1)
+     project_id = re.search(
+-        r'export OS_PROJECT_ID=(.+)', content).group(1)
++        r'export OS_PROJECT_ID="([^"]+)"', content).group(1)
+ 
+     assert(config.identity.username == username and
+            config.identity.home_project == project_name and
diff -Nru horizon-25.3.0/debian/patches/series 
horizon-25.3.0/debian/patches/series
--- horizon-25.3.0/debian/patches/series        2025-07-10 11:09:04.000000000 
+0200
+++ horizon-25.3.0/debian/patches/series        2026-06-09 10:20:14.000000000 
+0200
@@ -6,3 +6,4 @@
 remove-broken-test-in-bookworm-backports.patch
 fix-serial_console.scss.patch
 do-not-import-enabled-if-no-plugins.patch
+OSSN-0097_Escape_dollar_character_in_shellfilter_and_use_it_consistently.patch

Reply via email to