chore(security): shrink the ML image's CVE surface, override deepmerge-ts (#1083)

Three Trivy cleanups off the code-scanning tab.

ml/Dockerfile — install no runtime apt packages at all. Neither libgl1
nor libglib2.0-0 is needed: opencv-python-headless 4.14 bundles what it
needs and `ldd .../cv2/cv2*.so` resolves fully on a bare slim base. The
old comment claimed the headless wheel still links libGL, which was
true of much older wheels. libgl1 was dragging in 36 transitive
packages (mesa, LLVM, X11) for a service that never opens a display.

Measured with `trivy image` on locally built variants:

  before:            165 findings — 88 low / 49 med / 19 high / 6 crit
  without libgl1:    133 findings — 58 low / 48 med / 19 high / 5 crit
  without either:    123 findings — 57 low / 46 med / 13 high / 4 crit

42 findings gone, image 1.05GB -> 774MB. Not one of the 165 had an
upstream fix available, so not installing the packages is the only
lever there is.

docker-build.yml — set ignore-unfixed on all four Trivy steps. All 123
remaining ML findings are unfixed base-OS CVEs; Debian has them
resolved in sid and pending backport to trixie, and apt-get upgrade -y
behind CACHEBUST picks each one up automatically. Reporting them buries
anything actionable, and suppressing them is the precondition for ever
setting exit-code: 1.

backend — deepmerge-ts <8.0.0 has a stack-exhaustion advisory
(CVE-2026-40345, high) reached via mailparser -> html-to-text, which
pins ^7.1.5 so npm cannot get there alone. Not reachable in our code:
html-to-text only feeds deepmerge-ts its options object
(html-to-text.mjs:1468, :1442), never parsed email content. npm audit
goes 3 high -> 0.

The lockfile also picks up the version field release-please had left at
3.103.1-beta.0, plus some "peer": true metadata npm 11.6 recomputes.

Co-authored-by: Paul Nothaft <[email protected]>
This commit is contained in:
Paul Nothaft
2026-08-19 13:56:01 +02:00
committed by GitHub
co-authored by Paul Nothaft
parent 9f825be01e
commit 74a8f9bf24
4 changed files with 76 additions and 9 deletions
+32
View File
@@ -212,6 +212,14 @@ jobs:
format: 'sarif'
output: 'trivy-backend-${{ env.PLATFORM_PAIR }}.sarif'
severity: 'CRITICAL,HIGH'
# Base-image CVEs with no released fix are not actionable: the
# Dockerfiles already run `apt-get upgrade -y` behind a CACHEBUST,
# so a fix lands in the next build automatically. Reporting them
# buries the findings someone can actually do something about --
# the ML image alone contributed 123 unfixable alerts. Dropping
# them is also the precondition for ever setting exit-code: 1,
# which build-backend's comment flags as a deliberate follow-up.
ignore-unfixed: true
timeout: '10m'
- name: Upload Trivy scan results to GitHub Security tab
@@ -474,6 +482,14 @@ jobs:
format: 'sarif'
output: 'trivy-frontend-${{ env.PLATFORM_PAIR }}.sarif'
severity: 'CRITICAL,HIGH'
# Base-image CVEs with no released fix are not actionable: the
# Dockerfiles already run `apt-get upgrade -y` behind a CACHEBUST,
# so a fix lands in the next build automatically. Reporting them
# buries the findings someone can actually do something about --
# the ML image alone contributed 123 unfixable alerts. Dropping
# them is also the precondition for ever setting exit-code: 1,
# which build-backend's comment flags as a deliberate follow-up.
ignore-unfixed: true
timeout: '10m'
- name: Upload Trivy scan results to GitHub Security tab
@@ -716,6 +732,14 @@ jobs:
format: 'sarif'
output: 'trivy-aio-${{ env.PLATFORM_PAIR }}.sarif'
severity: 'CRITICAL,HIGH'
# Base-image CVEs with no released fix are not actionable: the
# Dockerfiles already run `apt-get upgrade -y` behind a CACHEBUST,
# so a fix lands in the next build automatically. Reporting them
# buries the findings someone can actually do something about --
# the ML image alone contributed 123 unfixable alerts. Dropping
# them is also the precondition for ever setting exit-code: 1,
# which build-backend's comment flags as a deliberate follow-up.
ignore-unfixed: true
timeout: '10m'
- name: Upload Trivy scan results to GitHub Security tab
@@ -1109,6 +1133,14 @@ jobs:
format: 'sarif'
output: 'trivy-ml-${{ env.PLATFORM_PAIR }}.sarif'
severity: 'CRITICAL,HIGH'
# Base-image CVEs with no released fix are not actionable: the
# Dockerfiles already run `apt-get upgrade -y` behind a CACHEBUST,
# so a fix lands in the next build automatically. Reporting them
# buries the findings someone can actually do something about --
# the ML image alone contributed 123 unfixable alerts. Dropping
# them is also the precondition for ever setting exit-code: 1,
# which build-backend's comment flags as a deliberate follow-up.
ignore-unfixed: true
timeout: '10m'
- name: Upload Trivy scan results to GitHub Security tab
+24 -5
View File
@@ -1,12 +1,12 @@
{
"name": "picpeak-backend",
"version": "3.103.1-beta.0",
"version": "3.107.1-beta.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "picpeak-backend",
"version": "3.103.1-beta.0",
"version": "3.107.1-beta.0",
"dependencies": {
"@aws-sdk/client-s3": "^3.850.0",
"@aws-sdk/lib-storage": "^3.850.0",
@@ -324,6 +324,7 @@
"resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1000.0.tgz",
"integrity": "sha512-7kPy33qNGq3NfwHC0412T6LDK1bp4+eiPzetX0sVd9cpTSXuQDKpoOFnB0Njj6uZjJDcLS3n2OeyarwwgkQ0Ow==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@aws-crypto/sha1-browser": "5.2.0",
"@aws-crypto/sha256-browser": "5.2.0",
@@ -1052,6 +1053,7 @@
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.7",
"@babel/generator": "^7.29.7",
@@ -3946,6 +3948,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -4560,6 +4563,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.10.38",
"caniuse-lite": "^1.0.30001799",
@@ -5367,9 +5371,19 @@
}
},
"node_modules/deepmerge-ts": {
"version": "7.1.5",
"resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz",
"integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-8.0.1.tgz",
"integrity": "sha512-szCXE7YLCvLKR9bFPJcvsezOShdalctSvrgN/LM/QGUEPZQajwjmsMObZ6/DuANT5lxzM/wtO8Feubwdkz8myA==",
"funding": [
{
"type": "ko-fi",
"url": "https://ko-fi.com/rebeccastevens"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/deepmerge-ts"
}
],
"license": "BSD-3-Clause",
"engines": {
"node": ">=16.0.0"
@@ -5753,6 +5767,7 @@
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -6000,6 +6015,7 @@
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
"license": "MIT",
"peer": true,
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@@ -6967,6 +6983,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.27.6"
},
@@ -9884,6 +9901,7 @@
"resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.17.2.tgz",
"integrity": "sha512-UnwF5fXy08f0dnp4jchFYAROKMNTaPqb/xgR8GtCzIcqoTnbOqtp3bwKvO4688oHI6vzEEs8Q6vqqEnC5IUELw==",
"license": "MIT",
"peer": true,
"dependencies": {
"crypto-js": "^4.2.0",
"fontkit": "^2.0.4",
@@ -10978,6 +10996,7 @@
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.12.0.tgz",
"integrity": "sha512-b1YMh3+DHZp59DLna3qVwQ5iOla/nrI6mLBNW02XxU77M3046Df6VLkoaJyFz20VsGIG5kkp+FK0kg4K4HnUFw==",
"license": "MIT",
"peer": true,
"dependencies": {
"parseley": "~0.13.1"
},
+2 -1
View File
@@ -96,6 +96,7 @@
"@tootallnate/once": ">=3.0.1",
"ip-address": ">=10.3.1",
"uuid": "^11.1.1",
"nodemailer": "^9.0.1"
"nodemailer": "^9.0.1",
"deepmerge-ts": ">=8.0.1"
}
}
+18 -3
View File
@@ -73,11 +73,26 @@ ARG CACHEBUST=1
RUN echo "cachebust=${CACHEBUST}" \
&& apt-get update \
&& apt-get upgrade -y \
# libGL and libglib are opencv-python-headless's remaining shared-library
# deps. The headless wheel drops the GUI toolkits but still links libGL.
&& apt-get install -y --no-install-recommends libgl1 libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*
# No runtime apt packages at all — deliberately.
#
# opencv-python-headless 4.14 needs neither libgl1 nor libglib2.0-0. The
# wheel bundles what it needs; `ldd .../cv2/cv2*.so` resolves fully on a
# bare python:3.12-slim. Both were installed here on the assumption that
# the headless build still links libGL, which was true of much older
# wheels and is not true of this one.
#
# What they cost: libgl1 alone pulls 36 transitive packages (mesa, LLVM,
# X11) into a service that never opens a display, and libglib2.0-0t64
# carries a critical plus six highs. Together they accounted for 42 of
# this image's Trivy findings — none of which have an upstream fix, so
# not installing them is the only lever that exists.
#
# Before re-adding either, confirm it is actually needed: build without
# it and run `ml/tests` plus a real FaceDetectorYN.detect() call, since
# the API tests stub the pipeline and will pass either way.
WORKDIR /app
COPY requirements.txt .