@@ -1599,7 +1599,7 @@ def layer_export_recipes_csv_view(request, branch, slug):
recipe_fields = [f.name for f in Recipe._meta.get_fields() if not (f.auto_created and f.is_relation)]
for field in fieldlist:
if field not in recipe_fields:
- return HttpResponse('Field %s is invalid' % field)
+ return HttpResponse('Field %s is invalid' % field, content_type='text/plain', status=400)
writer = csv.writer(response)
for recipe in layerbranch.sorted_recipes():
new file mode 100644
@@ -0,0 +1,33 @@
+# layerindex-web - tests for the layer recipes CSV export
+#
+# Licensed under the MIT license, see COPYING.MIT for details
+#
+# SPDX-License-Identifier: MIT
+
+import pytest
+from django.test import TestCase
+from django.urls import reverse
+
+from layerindex.models import Branch, LayerItem, LayerBranch
+
+
+@pytest.mark.django_db
+class TestRecipesCsv(TestCase):
+
+ def setUp(self):
+ branch = Branch.objects.create(name='master', bitbake_branch='master')
+ layer = LayerItem.objects.create(name='meta-test', status='P', layer_type='A',
+ summary='test', description='test',
+ vcs_url='git://example.com/meta-test.git')
+ LayerBranch.objects.create(layer=layer, branch=branch)
+ self.url = reverse('layer_export_recipes_csv', args=('master', 'meta-test'))
+
+ def test_valid_fields(self):
+ response = self.client.get(self.url, {'fields': 'pn,pv'})
+ self.assertEqual(response.status_code, 200)
+ self.assertTrue(response['Content-Type'].startswith('text/csv'))
+
+ def test_invalid_field_not_reflected_as_html(self):
+ response = self.client.get(self.url, {'fields': '<script>alert(1)</script>'})
+ self.assertEqual(response.status_code, 400)
+ self.assertTrue(response['Content-Type'].startswith('text/plain'))
layer_export_recipes_csv_view echoed an invalid name from the "fields" query parameter back in an HttpResponse with the default text/html content type, so a crafted link could run script on the site without the victim being logged in. Return the error as text/plain with status 400 instead. Reported by CodeQL (py/reflective-xss). AI-Generated: Claude Opus 5.5 Signed-off-by: Tim Orling <tim.orling@konsulko.com> --- layerindex/views.py | 2 +- tests/test_recipes_csv.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 tests/test_recipes_csv.py