fix(security): bump sanitize-html to 2.17.7

Trivy flags the backend image on two sanitize-html advisories, both
fixed upstream:

- CVE-2026-63670 (fixed 2.17.6): a literal solidus after a raw-text end
  tag (`</textarea/>`) is treated as text by htmlparser2 and re-emitted
  unescaped, so disallowed markup passes when textarea or xmp is in
  allowedTags.
- CVE-2026-84371 (fixed 2.17.7): an SVG SMIL animation whose
  attributeName selects href lets the sibling values/from/to/by
  attributes carry URLs past the scheme policy.

2.17.5 -> 2.17.7, exact pin as before. The new version brings its own
htmlparser2 12 / domhandler 6 / domutils 4 / dom-serializer 3 /
entities 8 tree under node_modules/sanitize-html; nothing else in the
lock moves.

That tree is ESM-only, so the backend now needs unflagged require(esm):
Node 20.19+ or 22.12+. The image is node:22-alpine and CI runs 22, but
engines.node still admitted 22.0-22.11, where require('sanitize-html')
throws ERR_REQUIRE_ESM at startup (publicSiteService loads it during
initialisation). engines is now ^20.19.0 || >=22.12.0 and the native
setup script's Node check enforces the same range instead of accepting
any 22.x. On the supported versions the sanitiser behaves identically
to 2.17.5 on the tracker and newsletter fixtures.

Jest 29's CommonJS registry cannot evaluate ESM either, so every suite
importing a route or service that uses the sanitiser would fail at
import. jest.config.js now maps `sanitize-html` to jest.sanitizeHtml.js,
which hands that one module to Node's real loader via
process.getBuiltinModule('module') — a plain require('module') inside
Jest is Jest's wrapper and returns an empty object for this package.
Verified against a real 2.17.7 install: the sanitiser suites and a
settings route suite pass; without the mapper they fail with "Cannot
use import statement outside a module".
This commit is contained in:
Paul Nothaft
2026-09-06 22:57:47 +02:00
parent 83b74e2256
commit 65831785a2
5 changed files with 154 additions and 13 deletions
+20 -5
View File
@@ -15,7 +15,7 @@ readonly SCRIPT_VERSION="2.1.0"
readonly APP_NAME="PicPeak"
readonly REPO_URL="https://github.com/PicPeak/picpeak.git"
readonly NODE_VERSION="20"
readonly NODE_MIN_VERSION="20.19.0" # backend engines: ^20.19.0 || >=22 (sharp 0.35, html-to-text 10)
readonly NODE_MIN_VERSION="20.19.0" # backend engines: ^20.19.0 || >=22.12.0 (sharp 0.35, html-to-text 10; sanitize-html 2.17.7 needs require(esm), unflagged in 20.19 and 22.12)
readonly MIN_RAM_DOCKER=2048
readonly MIN_RAM_NATIVE=1024
readonly MIN_DISK_GB=2
@@ -822,6 +822,19 @@ EOF
# Native Installation
################################################################################
# True when a Node.js version satisfies the backend's engines range
# (^20.19.0 || >=22.12.0). 21.x is out, and so is 22.0-22.11.
node_version_supported() {
local ver="$1" major minor
major="${ver%%.*}"
minor="${ver#*.}"; minor="${minor%%.*}"
[[ "$major" =~ ^[0-9]+$ && "$minor" =~ ^[0-9]+$ ]] || return 1
[[ "$(printf '%s\n' "$NODE_MIN_VERSION" "$ver" | sort -V | head -1)" == "$NODE_MIN_VERSION" ]] || return 1
[[ "$major" == "21" ]] && return 1
[[ "$major" == "22" && "$minor" -lt 12 ]] && return 1
return 0
}
install_nodejs() {
# --update dispatches here before main() runs detect_os, so detect on demand
if [[ -z "$PACKAGE_MANAGER" ]]; then
@@ -830,8 +843,10 @@ install_nodejs() {
local node_ver
node_ver=$(command_exists node && node -v | cut -d'v' -f2 || echo "0")
# backend engines range is ^20.19.0 || >=22 (Node 21 is excluded by the glob/minimatch family)
if [[ "$(printf '%s\n' "$NODE_MIN_VERSION" "$node_ver" | sort -V | head -1)" == "$NODE_MIN_VERSION" && "${node_ver%%.*}" != "21" ]]; then
# backend engines range is ^20.19.0 || >=22.12.0 (Node 21 is excluded by the glob/minimatch
# family; 22.0-22.11 lack unflagged require(esm), which sanitize-html 2.17.7's ESM-only
# htmlparser2 needs — the backend would not start)
if node_version_supported "$node_ver"; then
log_success "Node.js $(node -v) is already installed"
return
fi
@@ -851,8 +866,8 @@ install_nodejs() {
# Package managers won't downgrade a newer Node (e.g. 21), so re-verify before continuing
node_ver=$(command_exists node && node -v | cut -d'v' -f2 || echo "0")
if [[ "$(printf '%s\n' "$NODE_MIN_VERSION" "$node_ver" | sort -V | head -1)" != "$NODE_MIN_VERSION" || "${node_ver%%.*}" == "21" ]]; then
die "Node.js v$node_ver does not satisfy the backend requirement (^$NODE_MIN_VERSION || >=22); remove the current Node.js, install a supported version, then re-run this script"
if ! node_version_supported "$node_ver"; then
die "Node.js v$node_ver does not satisfy the backend requirement (^$NODE_MIN_VERSION || >=22.12.0); remove the current Node.js, install a supported version, then re-run this script"
fi
log_success "Node.js installed: $(node -v)"
}