@@ -6,6 +6,7 @@
#
# SPDX-License-Identifier: MIT
+import logging
import os
import sys
import re
@@ -63,6 +64,8 @@ from layerindex.models import (BBAppend, BBClass, BBClassGlobal, BBClassRecipe,
from . import tasks, utils
+logger = logging.getLogger(__name__)
+
def edit_layernote_view(request, template_name, slug, pk=None):
layeritem = get_object_or_404(LayerItem, name=slug)
if layeritem.comparison:
@@ -347,12 +350,11 @@ def bulk_change_patch_view(request, pk):
return HttpResponse('No patch data generated', content_type='text/plain')
except Exception as e:
output = getattr(e, 'output', None)
- if output:
- if 'timeout' in output:
- return HttpResponse('Failed to generate patches: timed out waiting for lock. Please try again shortly.', content_type='text/plain')
- else:
- return HttpResponse('Failed to generate patches: %s' % output, content_type='text/plain')
- return HttpResponse('Failed to generate patches: %s' % e, content_type='text/plain')
+ if output and 'timeout' in output:
+ return HttpResponse('Failed to generate patches: timed out waiting for lock. Please try again shortly.', content_type='text/plain')
+ # Details can include paths and bitbake output, so keep them in the server log
+ logger.exception('Failed to generate patches for bulk change %s: %s', pk, output)
+ return HttpResponse('Failed to generate patches, please contact the site administrator.', content_type='text/plain', status=500)
# FIXME better error handling
@@ -51,3 +51,14 @@ class TestBulkChangeAccess(TestCase):
for name in ('bulk_change_edit', 'bulk_change_review'):
response = self.client.get(self._url(name))
self.assertEqual(response.status_code, 200, name)
+
+ def test_patch_error_details_not_exposed(self):
+ import subprocess
+ from unittest import mock
+ self.client.force_login(self.owner)
+ err = subprocess.CalledProcessError(1, 'bulkchange.py', output='Traceback: /opt/secret/path')
+ with mock.patch('layerindex.views.utils.runcmd', side_effect=err), \
+ self.assertLogs('layerindex.views', 'ERROR'):
+ response = self.client.get(self._url('bulk_change_patches'))
+ self.assertEqual(response.status_code, 500)
+ self.assertNotIn(b'/opt/secret/path', response.content)
bulk_change_patch_view returned the output of the failing bulkchange.py run, or the exception text, in the HTTP response. That can include tracebacks, server paths and bitbake output. Log the details instead and return a generic error with status 500. The lock timeout message is kept, since it tells the user what to do and reveals nothing. Reported by CodeQL (py/stack-trace-exposure). AI-Generated: Claude Opus 5.5 Signed-off-by: Tim Orling <tim.orling@konsulko.com> --- layerindex/views.py | 14 ++++++++------ tests/test_bulkchange_access.py | 11 +++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-)