From 86bfb55e885f0b4757c6a3d45d0c85571deeff43 Mon Sep 17 00:00:00 2001 From: tdruez Date: Mon, 25 May 2026 16:16:29 +0400 Subject: [PATCH 1/4] fix javascript issues when product is locked Signed-off-by: tdruez --- .../templates/product_portfolio/product_details.html | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/product_portfolio/templates/product_portfolio/product_details.html b/product_portfolio/templates/product_portfolio/product_details.html index 6f4f11cd..ee5c147f 100644 --- a/product_portfolio/templates/product_portfolio/product_details.html +++ b/product_portfolio/templates/product_portfolio/product_details.html @@ -346,7 +346,9 @@ {% if purldb_enabled %} - {% endif %} + {# Always render the CSRF token: required by the vulnerability analysis form, even on read-only products #} {% include 'product_portfolio/includes/product_hierarchy.js.html' with relations_feature_grouped=tabsets.Hierarchy.fields.0.1.relations_feature_grouped %} {% if tabsets.Owner.extra %} From 93ad4b5f3ee05bbd61f5a18abf3d1e9f568f91f8 Mon Sep 17 00:00:00 2001 From: tdruez Date: Tue, 26 May 2026 08:55:46 +0400 Subject: [PATCH 3/4] add unit test Signed-off-by: tdruez --- product_portfolio/tests/test_views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/product_portfolio/tests/test_views.py b/product_portfolio/tests/test_views.py index f47da018..6fa19b3d 100644 --- a/product_portfolio/tests/test_views.py +++ b/product_portfolio/tests/test_views.py @@ -1161,6 +1161,7 @@ def test_product_portfolio_detail_view_status_is_locked(self): "This product version is marked as read-only, preventing any modifications " "to its inventory." ) + expected4 = "csrf_header.js" locked_status = make_product_status(self.dataspace, is_locked=True) self.product1.update(configuration_status=locked_status) @@ -1168,6 +1169,7 @@ def test_product_portfolio_detail_view_status_is_locked(self): self.assertContains(response, expected1, html=True) self.assertContains(response, expected2, html=True) self.assertContains(response, expected3, html=True) + self.assertContains(response, expected4, html=True) def test_product_portfolio_list_view_secured_queryset(self): self.client.login(username=self.basic_user.username, password="secret") From a4f1ae23b5a31c5759c6107187ae8c85cb97c48a Mon Sep 17 00:00:00 2001 From: tdruez Date: Tue, 26 May 2026 09:05:37 +0400 Subject: [PATCH 4/4] fix unit test Signed-off-by: tdruez --- .../templates/product_portfolio/product_details.html | 3 ++- product_portfolio/tests/test_views.py | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/product_portfolio/templates/product_portfolio/product_details.html b/product_portfolio/templates/product_portfolio/product_details.html index 28f8e316..bdc5be5f 100644 --- a/product_portfolio/templates/product_portfolio/product_details.html +++ b/product_portfolio/templates/product_portfolio/product_details.html @@ -173,7 +173,8 @@ {% block javascripts %} {{ block.super }} - {# Always render the CSRF token: required by the vulnerability analysis form, even on read-only products #} + {# Always render the CSRF token: required by the vulnerability analysis form, even on read-only products #} + {% include 'product_portfolio/includes/product_hierarchy.js.html' with relations_feature_grouped=tabsets.Hierarchy.fields.0.1.relations_feature_grouped %} {% if tabsets.Owner.extra %} diff --git a/product_portfolio/tests/test_views.py b/product_portfolio/tests/test_views.py index 6fa19b3d..7a81e266 100644 --- a/product_portfolio/tests/test_views.py +++ b/product_portfolio/tests/test_views.py @@ -455,6 +455,13 @@ def test_product_portfolio_detail_view_include_tab_vulnerability_analysis_modal( response = self.client.get(url) self.assertContains(response, modal_id) self.assertContains(response, modal_js) + self.assertContains(response, "csrf_header.js") + + # Ensure the CSRF script is present even if product is locked + locked_status = make_product_status(self.dataspace) + self.product1.update(configuration_status=locked_status) + response = self.client.get(url) + self.assertContains(response, "csrf_header.js") @mock.patch("dejacode_toolkit.vulnerablecode.VulnerableCode.is_configured") def test_product_portfolio_detail_view_tab_vulnerability_label(self, mock_is_configured): @@ -1161,7 +1168,6 @@ def test_product_portfolio_detail_view_status_is_locked(self): "This product version is marked as read-only, preventing any modifications " "to its inventory." ) - expected4 = "csrf_header.js" locked_status = make_product_status(self.dataspace, is_locked=True) self.product1.update(configuration_status=locked_status) @@ -1169,7 +1175,6 @@ def test_product_portfolio_detail_view_status_is_locked(self): self.assertContains(response, expected1, html=True) self.assertContains(response, expected2, html=True) self.assertContains(response, expected3, html=True) - self.assertContains(response, expected4, html=True) def test_product_portfolio_list_view_secured_queryset(self): self.client.login(username=self.basic_user.username, password="secret")