From a90b79882916b3cb5fd23e7f6f2d66e5d90dcc30 Mon Sep 17 00:00:00 2001 From: devGregA Date: Sat, 26 Sep 2026 21:40:28 -0600 Subject: [PATCH 1/3] build(nginx): collect only runtime files into the static tree Once the Tailwind CSS is built, reinstall components with --production so the CSS toolchain (tailwind cli, lightningcss and their prebuilt native binaries) is not collected, and have collectstatic skip source maps, TypeScript sources and declarations, and the npm lockfiles some packages ship in their tarballs. None of these are loaded by any page. Co-Authored-By: Claude Opus 5.5 --- Dockerfile.nginx-alpine | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Dockerfile.nginx-alpine b/Dockerfile.nginx-alpine index d0f01336f35..7aa8855da8b 100644 --- a/Dockerfile.nginx-alpine +++ b/Dockerfile.nginx-alpine @@ -63,8 +63,14 @@ COPY manage.py ./ COPY dojo/ ./dojo/ # Build Tailwind CSS RUN cd components && yarn build:css +# the CSS is built, so drop the dev-only toolchain (tailwind cli, lightningcss and their native binaries) before static is collected +RUN cd components && yarn install --production --ignore-scripts # always collect static for debug toolbar as we can't make it dependant on env variables or build arguments without breaking docker layer caching -RUN env DD_SECRET_KEY='.' DD_DJANGO_DEBUG_TOOLBAR_ENABLED=True python3 manage.py collectstatic --noinput --verbosity=2 && true +# skip files the pages never load: source maps, typescript sources and declarations, and the npm lockfiles some packages ship +# in their tarballs +RUN env DD_SECRET_KEY='.' DD_DJANGO_DEBUG_TOOLBAR_ENABLED=True python3 manage.py collectstatic --noinput --verbosity=2 \ + --ignore yarn.lock --ignore package-lock.json --ignore npm-shrinkwrap.json --ignore pnpm-lock.yaml \ + --ignore '*.map' --ignore '*.ts' --ignore '*.mts' --ignore '*.cts' && true FROM nginx:1.29.3-alpine3.22@sha256:b3c656d55d7ad751196f21b7fd2e8d4da9cb430e32f646adcf92441b72f82b14 AS release ARG uid=1001 From 96f5f84d1684306109db420648fa66c98916b008 Mon Sep 17 00:00:00 2001 From: devGregA Date: Sat, 26 Sep 2026 21:40:28 -0600 Subject: [PATCH 2/3] chore(components): update nanoid 3.x to 3.3.19 Co-Authored-By: Claude Opus 5.5 --- components/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/yarn.lock b/components/yarn.lock index 2fef196c2e9..2f9414dfbbd 100644 --- a/components/yarn.lock +++ b/components/yarn.lock @@ -2507,9 +2507,9 @@ ms@^2.1.3: integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== nanoid@^3.3.16: - version "3.3.16" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.16.tgz#a04d8ec4b1f10009d2d533947aefe4293737816c" - integrity sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q== + version "3.3.19" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.19.tgz#336d4aa4bcd4fb24d2cddede7ffeae40bec03f0a" + integrity sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug== nanoid@^5.0.7, nanoid@^5.1.6: version "5.1.16" From 1ad615878898e7122ef8446ad85aab8731328ac3 Mon Sep 17 00:00:00 2001 From: devGregA Date: Sat, 26 Sep 2026 21:40:28 -0600 Subject: [PATCH 3/3] chore(api_v3): turn off Scalar's hosted AI assistant on the reference page Scalar enables its "Ask AI" assistant by default, which is a third-party hosted service. Disable it through data-configuration so the page stays self-contained, the same reason the bundle is served from our own static files instead of a CDN. Co-Authored-By: Claude Opus 5.5 --- dojo/api_v3/reference_docs.py | 9 ++++++++- unittests/api_v3/test_apiv3_reference_docs.py | 9 +++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/dojo/api_v3/reference_docs.py b/dojo/api_v3/reference_docs.py index c7e64b710cd..22c7de34fbf 100644 --- a/dojo/api_v3/reference_docs.py +++ b/dojo/api_v3/reference_docs.py @@ -26,6 +26,7 @@ """ from __future__ import annotations +import json from typing import TYPE_CHECKING from django.http import HttpResponse @@ -38,6 +39,11 @@ # Path inside components/node_modules (a STATICFILES_DIRS entry); version pinned in package.json. SCALAR_STATIC_PATH = "@scalar/api-reference/dist/browser/standalone.js" +# Scalar enables its hosted AI assistant ("Ask AI") by default. That is a third-party service, so +# it is turned off to keep the page self-contained, for the same reason the bundle is not loaded +# from a CDN. +SCALAR_CONFIGURATION = json.dumps({"agent": {"disabled": True}}) + _PAGE = """ @@ -49,7 +55,7 @@ - + """ @@ -61,4 +67,5 @@ def scalar_reference(request: HttpRequest) -> HttpResponse: openapi_url=reverse("api_v3:openapi-json"), docs_url=reverse("api_v3:openapi-view"), script_url=static(SCALAR_STATIC_PATH), + configuration=SCALAR_CONFIGURATION, )) diff --git a/unittests/api_v3/test_apiv3_reference_docs.py b/unittests/api_v3/test_apiv3_reference_docs.py index 018ece82488..6910c1651ea 100644 --- a/unittests/api_v3/test_apiv3_reference_docs.py +++ b/unittests/api_v3/test_apiv3_reference_docs.py @@ -12,6 +12,7 @@ from __future__ import annotations import json +import re from pathlib import Path from django.templatetags.static import static @@ -51,6 +52,14 @@ def test_page_points_at_v3_schema_and_swagger_fallback(self): # Swagger (framework-bundled assets) remains linked for noscript. self.assertIn(reverse("api_v3:openapi-view"), html) + def test_hosted_ai_assistant_is_disabled(self): + # Scalar turns its hosted "Ask AI" assistant on by default; the page keeps it off so it + # never talks to a third-party service. + html = self._get().content.decode() + match = re.search(r"data-configuration='([^']*)'", html) + self.assertIsNotNone(match, "the Scalar script tag must carry a data-configuration") + self.assertTrue(json.loads(match.group(1))["agent"]["disabled"]) + def test_reference_page_is_not_an_api_operation(self): # A plain Django view: it must NOT appear in the OpenAPI schema (and therefore places no # obligations on the authz/query completeness gates, which walk the schema).