Skip to content

fix(docker): refuse to build an image with PHP below 8.4.27 (CVE-2026-109618) - #14

Merged
DGINXREAL merged 2 commits into
mainfrom
fix/php-cve-2026-109618
Oct 21, 2026
Merged

DGINXREAL merged 2 commits into
mainfrom
fix/php-cve-2026-109618

Conversation

@DGINXREAL

Copy link
Copy Markdown
Member

Summary

Fourth repo on the same dunglas/frankenphp:1-php8.4 base — same fix as NoiXdev/marketix#117, NoiXdev/kontorfix#1 and NoiXdev/inventorix#53.

CVE-2026-109618 / CVE-2026-109619 are RCE flaws in PHP's longjmp()-based OOM and fatal-error handling (CVSS 4.0 9.2, exploited in the wild since 21 Oct 2026), fixed in PHP 8.4.27. FrankenPHP is explicitly among the affected SAPIs.

  • Dockerfile — ARG MIN_PHP_VERSION=8.4.27 plus a guard layer right after the FROM. The build fails rather than silently shipping a vulnerable runtime, and clears itself once a patched base is pulled.
  • .github/workflows/release.yml — add pull: true. The docker job in ci.yml already had it (with a comment explaining exactly why); the release job did not — so the pipeline that actually ships could resolve a stale cached base layer.

Why apt cannot do this instead

The apt-get upgrade already in this Dockerfile cannot reach PHP, verified inside the image:

  • dpkg -l | grep -c php → 0. PHP is built from source into /usr/local; no apt package owns /usr/local/bin/php.
  • The base image ships /etc/apt/preferences.d with Package: php* / Pin-Priority: -1, so Debian's own php8.4-cli (which does have 8.4.27 in trixie-security) resolves to Candidate: (none).
  • The vulnerable component is not the CLI binary anyway: ldd $(which frankenphp) → libphp.so => /usr/local/lib/libphp.so, built --enable-embed --enable-zts. An apt-installed PHP would sit unused in /usr/bin and leave libphp.so untouched — while possibly turning a scanner green. The guard therefore checks the running interpreter's PHP_VERSION, not a package list.

Verification

Guard exercised in both directions against the real base image:

  • base PHP 8.4.27, floor 8.4.27 → build proceeds
  • floor raised to 8.4.28 → exit code 1, FATAL: base image ships PHP 8.4.27, need >= 8.4.28 (CVE-2026-109618/109619)

The docker job in CI builds the image on this PR, so the guard is exercised by CI here too.

Note on the published image

The guard only governs future builds — it cannot change an image that already exists. The currently published image was built before PHP 8.4.27 existed and therefore still contains 8.4.26; a docker pull on a server will not change that, because the base image is baked into the layers at build time. A release run is required after this merge for the shipped image to carry the fix.

Out of scope

Kept to the minimum needed for a secure image:

  • The apt layer here has no cache-busting arg, so apt-get upgrade can be served from the BuildKit cache indefinitely. Unrelated to this CVE, same class of staleness.
  • ci.yml has no schedule: trigger, so the Trivy gate only runs when someone pushes.
  • No Trivy gate in the release pipeline before push.

🤖 Generated with Claude Code

CVE-2026-109618 / CVE-2026-109619 are RCE flaws in PHP's longjmp-based OOM
and fatal-error handling (CVSS 9.2, exploited in the wild). They are fixed
in PHP 8.4.27 and affect every SAPI, FrankenPHP included.

PHP is compiled into the FrankenPHP base image rather than installed from
apt — the base image even pins `php*` to Pin-Priority -1 — so the existing
`apt-get upgrade` layer cannot patch it. Only a rebuilt base image can. Add
a MIN_PHP_VERSION floor that fails the build instead of silently shipping a
vulnerable runtime; it clears itself once the build pulls a patched base.

The release workflow also built without `pull: true`, unlike the CI job, so
the pipeline that actually ships to production could resolve a stale cached
base layer — exactly the path that would produce such an image.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@DGINXREAL
DGINXREAL merged commit a904748 into main Oct 21, 2026
2 of 3 checks passed
@DGINXREAL
DGINXREAL deleted the fix/php-cve-2026-109618 branch October 21, 2026 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant