From cb3b74222dc3043b778d16ce9e87355ab8e75ed1 Mon Sep 17 00:00:00 2001 From: ibuilder <308683+ibuilder@users.noreply.github.com> Date: Wed, 22 Jul 2026 15:40:32 +0000 Subject: [PATCH 1/2] chore(sec): pin Dockerfile base images by digest (SEC F4) Pin every FROM line in the API, web, and converter Dockerfiles to the exact current multi-platform @sha256 index digest instead of mutable tags, for supply-chain integrity. Digests resolved from the Docker Hub registry (OCI image index, equivalent to buildx imagetools inspect): node:20-slim sha256:2cf067cf...febfc0 python:3.12-slim sha256:57cd7c3a...710de nginx:alpine sha256:4a73073b...c1752 Add a docker ecosystem to Dependabot (per-Dockerfile directory) so these digests auto-bump weekly. Co-Authored-By: Claude Opus 4.7 --- .github/dependabot.yml | 13 +++++++++++++ apps/web/Dockerfile | 4 ++-- services/api/Dockerfile | 6 +++--- services/converter/Dockerfile | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 155d609f..3c07a165 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -25,3 +25,16 @@ updates: directory: / schedule: { interval: weekly } open-pull-requests-limit: 3 + # Container base images: auto-bump the @sha256 digests pinned in each Dockerfile FROM line. + - package-ecosystem: docker + directory: /services/api + schedule: { interval: weekly } + open-pull-requests-limit: 3 + - package-ecosystem: docker + directory: /apps/web + schedule: { interval: weekly } + open-pull-requests-limit: 3 + - package-ecosystem: docker + directory: /services/converter + schedule: { interval: weekly } + open-pull-requests-limit: 3 diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index bd10b9d9..fb97153d 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -1,6 +1,6 @@ # Build the Vite app, then serve the static bundle with nginx. # Build context = repo root (this is an npm workspace: the only lockfile is the root package-lock.json). -FROM node:20-slim AS build +FROM node:20-slim@sha256:2cf067cfed83d5ea958367df9f966191a942351a2df77d6f0193e162b5febfc0 AS build WORKDIR /repo # Manifests + lockfile first, so `npm ci` is a reproducible, cache-friendly layer that only re-runs when # deps change — not on every source edit. `npm ci` (vs `npm install`) installs EXACTLY the locked tree @@ -11,7 +11,7 @@ RUN npm ci COPY apps/web/ apps/web/ RUN npm run build --workspace apps/web # prebuild copies web-ifc WASM into public/wasm/ -FROM nginx:alpine +FROM nginx:alpine@sha256:4a73073bd557c65b759505da037898b61f1be6cbcc3c2c3aeac22d2a470c1752 COPY apps/web/nginx.conf /etc/nginx/conf.d/default.conf COPY --from=build /repo/apps/web/dist /usr/share/nginx/html EXPOSE 80 diff --git a/services/api/Dockerfile b/services/api/Dockerfile index 07157946..7407140e 100644 --- a/services/api/Dockerfile +++ b/services/api/Dockerfile @@ -5,7 +5,7 @@ # Pin the fragments/web-ifc pair to the SAME versions as the client parser (apps/web/package.json). # CI enforces this (scripts/check-fragments-version.mjs) — a mismatch means the server emits .frag the # browser can't parse. -FROM node:20-slim AS converter +FROM node:20-slim@sha256:2cf067cfed83d5ea958367df9f966191a942351a2df77d6f0193e162b5febfc0 AS converter WORKDIR /conv ARG FRAGMENTS_VERSION=3.4.5 ARG WEBIFC_VERSION=0.0.77 @@ -15,7 +15,7 @@ RUN npm init -y >/dev/null 2>&1 && npm install --no-audit --no-fund \ # --- python build stage (B3): a full toolchain compiles any source-only wheel HERE, then we lift only # the installed packages into the runtime image below. gcc + headers never ship to production, so the # runtime image is smaller and carries no compiler in its attack surface. --- -FROM python:3.12-slim AS pybuild +FROM python:3.12-slim@sha256:57cd7c3a7a273101a6485ba99423ee568157882804b1124b4dd04266317710de AS pybuild # build toolchain + the same shared libs, so wheels that need to compile can link during install RUN apt-get update && apt-get install -y --no-install-recommends \ libgl1 libglib2.0-0 build-essential python3-dev \ @@ -29,7 +29,7 @@ COPY services/api/requirements.lock /tmp/req/requirements.lock # wholesale into the runtime stage. RUN pip install --prefix=/install --require-hashes -r /tmp/req/requirements.lock -FROM python:3.12-slim +FROM python:3.12-slim@sha256:57cd7c3a7a273101a6485ba99423ee568157882804b1124b4dd04266317710de WORKDIR /app ENV PYTHONUNBUFFERED=1 \ diff --git a/services/converter/Dockerfile b/services/converter/Dockerfile index 4f7ab32f..e658d552 100644 --- a/services/converter/Dockerfile +++ b/services/converter/Dockerfile @@ -1,5 +1,5 @@ # IFC→Fragments converter (Node). Build context = repo root. -FROM node:20-slim +FROM node:20-slim@sha256:2cf067cfed83d5ea958367df9f966191a942351a2df77d6f0193e162b5febfc0 WORKDIR /app # install just what the converter needs; web-ifc ships its own WASM. Pin to the SAME versions as the From 3a96fa654f274e6ce085e3f131bd67f8b42b7a47 Mon Sep 17 00:00:00 2001 From: ibuilder <308683+ibuilder@users.noreply.github.com> Date: Wed, 22 Jul 2026 15:47:22 +0000 Subject: [PATCH 2/2] =?UTF-8?q?chore(sec):=20npm=20audit=20fix=20dev/docs?= =?UTF-8?q?=20chain=20=E2=80=94=20bump=20fast-uri=20(SEC=20F6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve the high-severity fast-uri advisories (GHSA-v2hh-gcrm-f6hx, GHSA-4c8g-83qw-93j6) pulled in transitively via vite-plugin-pwa -> workbox-build -> ajv -> fast-uri. `npm audit fix` bumps the nested fast-uri to 3.1.4. Lock regenerated with npm's nested install strategy (npm install --package-lock-only --install-strategy=nested) to keep the tree correct; package.json is unchanged. Deferred: the nested js-yaml (GHSA-52cp-r559-cp3m) via @redocly/openapi-core is NOT fixable without a breaking major. The fix lives in @redocly/openapi-core 2.x, but openapi-typescript@7.13.0 (the latest release) pins @redocly/openapi-core ^1.34.6, whose newest 1.x (1.34.17) hard-pins js-yaml 4.2.0. Left per spec (no breaking bumps). npm audit --omit=dev stays at 0 vulnerabilities. Co-Authored-By: Claude Opus 4.7 --- package-lock.json | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index af310703..8e499ee1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ }, "apps/web": { "name": "@aec/web", - "version": "0.3.560", + "version": "0.3.584", "dependencies": { "@mkkellogg/gaussian-splats-3d": "^0.4.7", "@tauri-apps/plugin-dialog": "^2.7.1", @@ -4297,6 +4297,23 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv/node_modules/fast-uri": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -5630,22 +5647,6 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ] - }, "node_modules/fast-xml-builder": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz",