diff --git a/scanpipe/tests/test_views.py b/scanpipe/tests/test_views.py index 43ae6bd5af..b84fedd795 100644 --- a/scanpipe/tests/test_views.py +++ b/scanpipe/tests/test_views.py @@ -386,6 +386,18 @@ def test_scanpipe_views_project_details_download_output_view(self): response.headers["Content-Disposition"], ) + def test_scanpipe_views_project_details_download_output_view_json_inline(self): + # https://github.com/aboutcode-org/scancode.io/issues/2210 + json_file = self.project1.output_path / "results.json" + json_file.write_text('{"headers": []}') + url = reverse("project_download_output", args=[self.project1.slug, "results.json"]) + response = self.client.get(url) + self.assertEqual("application/json", response.headers["Content-Type"]) + self.assertEqual( + 'inline; filename="results.json"', + response.headers["Content-Disposition"], + ) + def test_scanpipe_views_project_details_delete_input_view(self): random_uuid = str(uuid.uuid4()) url = reverse("project_delete_input", args=[self.project1.slug, random_uuid]) diff --git a/scanpipe/views.py b/scanpipe/views.py index 962fde218c..c1d7732bd6 100644 --- a/scanpipe/views.py +++ b/scanpipe/views.py @@ -1532,7 +1532,11 @@ def download_project_file(request, slug, filename, path_type): if not file_path.exists(): raise Http404(f"{file_path} not found") - return FileResponse(file_path.open("rb"), as_attachment=True) + # JSON and plain text render safely in browsers: serve those inline so + # that e.g. JSON results open directly in the browser instead of always + # forcing a download. + as_attachment = file_path.suffix not in (".json", ".txt") + return FileResponse(file_path.open("rb"), as_attachment=as_attachment) @conditional_login_required