Repository navigation
Rebuild base-php from the PHP 7.4 backport for CVE-2026-109618/109619 - #1856
Conversation
Both CVEs are unauthenticated RCE in PHP, highest risk on Apache mod_php, which is what Language Forge runs. PHP 7.4 is EOL so there is no fixed release and the official php:7.4-apache image will not be rebuilt. The PHP security team published a minimal source backport branch (PHP-7.4-CVE-2026-109618-109619); this recompiles PHP from that branch, pinned to commit 712e60f9, in place on top of the official image using the same configure flags, so the apache module, ini layout and extension tooling are unchanged. Also needed to make the image buildable at all today: - Debian 11 moved to archive.debian.org, so apt sources are rewritten. - The mongodb extension is pinned to 1.19.0 (the version in the published image); unpinned it resolves to 1.20, whose ReadPreference deprecation floods the test log and OOMs PHPUnit's JUnit writer. Defence in depth: session.upload_progress.enabled=Off, a confirmed CVE-2026-109619 path that Language Forge does not use. docs/RELEASE.md explains that the base PHP image must be published via the manual workflow before merging changes to docker/base-php. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Unit Test Results362 tests 362 ✅ 9s ⏱️ Results for commit ae18dd7. |
rmunn
left a comment
There was a problem hiding this comment.
Looks like this should work. Let's merge it and let the proof of the pudding be in the eating.
| \ | ||
| # remove build dependencies again, same approach as the official image | ||
| apt-mark auto '.*' > /dev/null; \ | ||
| [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \ |
There was a problem hiding this comment.
It's possible for this to fail if the $savedAptMark variable is extremely long. In theory, to be safe, it should be piped through xargs, which would split the command into two (or more) commands if the argument length is too long. But it would take thousands of manually-installed packages to exceed the default 2 MB limit for command line length, so that's not a serious concern here.
This can stand as-is.
| find /usr/local -type f -executable -exec ldd '{}' ';' \ | ||
| | awk '/=>/ { print $(NF-1) }' \ | ||
| | sort -u \ | ||
| | xargs -r dpkg-query --search \ | ||
| | cut -d: -f1 \ | ||
| | sort -u \ | ||
| | xargs -r apt-mark manual \ | ||
| ; \ | ||
| apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ |
There was a problem hiding this comment.
This is attempting to trim the volume down to the minimally-required set of packages. Normally I would say this is worth doing, but it's possible that this might end up removing some Debian packages that we assume are there. I would actually remove this find command and the apt-get purge (and then the savedAptMark and apt-mark auto business can go away too), and just remove the packages that were explicitly installed by the script, such as git and the various *-dev packages. If the purpose is to reduce the available attack surface, then only removing git might actually make more sense.
However, that can also wait for later. For now, this should work, so let's let this also stand as-is. If it turns out that this ends up removing something that LfMerge relied on, we can switch to just uninstalling git and nothing else.
There was a problem hiding this comment.
Note that LfMerge runs in its own container, so removing things from the app container shouldn't affect it. So I should have said "something that Language Forge code relied on". Which we'll find out when we try to test this.
fixes CI which is currently broken
---
<details>
<summary>AI summary</summary>
## Summary
The deploy job ran `kubectl set image` on a self-hosted runner labelled
`languageforge` that no longer exists. Every staging deploy since May
has queued on it until GitHub cancelled the job after 24 hours. This
replaces it with the LexBox pattern: commit new image tags to the fleet
repo and let Rancher Fleet roll the cluster.
```diff
integrate-and-deploy.yml
inputs: image-tag-app, image-tag-proxy, image-tag-next-app
+ k8s-environment (staging | production), deploy-domain
- secrets: kube-context
+ secrets: fleet-repo-ssh-key
integrate
+ outputs.TAG_LFMERGE # so deploy can pin lfmerge too
deploy
- runs-on: [self-hosted, languageforge]
- kubectl set image deployment/{app,next-proxy,next-app,lfmerge}
+ runs-on: ubuntu-latest, concurrency per environment, GitHub environment + url
+ checkout ${{ vars.FLEET_REPO }}@main with FLEET_REPO_SSH_KEY -> fleet/
+ yq: set newTag for the 4 images in fleet/languageforge/<env>/kustomize/kustomization.yaml
+ add-and-commit + push: "[Language Forge] Update <env> images to <tag>"
staging.yml + k8s-environment: staging, deploy-domain: staging.languageforge.org
production.yml + k8s-environment: production, deploy-domain: languageforge.org
docs/RELEASE.md describes the fleet flow and manual deploy (edit the kustomization, push main)
```
Unlike LexBox, the fleet repo already holds the full Language Forge
manifests under `languageforge/<env>/kustomize/`, so nothing is copied
from this repo; only the four `newTag` values change. The
`docker/deployment` folder here is now unused by CI.
Actions are SHA-pinned to the same versions LexBox uses.
`docker/login-action` moved from v2 to v3 because actionlint rejects v2
as too old to run.
## Evidence
- **Before:** staging run 39717933967 (merge of #1856): integrate and
all e2e shards green, `deploy` queued forever. Run 25669681222 from May:
`deploy` cancelled after exactly 24 h.
**After:** `actionlint -shellcheck= -pyflakes= .github/workflows/*.yml`
exits 0. The yq expression was run against a copy of the real staging
kustomization and updated all four `newTag` values with the rest of the
file byte-identical.
Not tested: an actual workflow run. It cannot run until the repository
variable and secret below exist.
## Merge Danger
**Door:** two-way
Revert the workflow; nothing in the cluster changes until a deploy runs.
**Blast Radius:** deploys only
Needs, before the first run on `develop`:
- Repository variable `FLEET_REPO` = `sillsdev/languagedepot-deploy`
(same as LexBox).
- Repository secret `FLEET_REPO_SSH_KEY` = a deploy key with write
access on that repo (LexBox's can be reused or a new one added).
- Environments `staging` and `production` are created automatically on
first use; add required reviewers on `production` if you want an
approval gate like LexBox.
`staging.yml` calls the reusable workflow `@develop`, so the new deploy
job is live for staging as soon as this merges to `develop`. Production
picks it up once `master` has it.
</details>
## Test plan
- [x] Add `FLEET_REPO` variable and `FLEET_REPO_SSH_KEY` secret
- [ ] Merge to `develop`; confirm the `deploy` job pushes a commit to
`languagedepot-deploy` and Fleet updates `staging.languageforge.org`
- [ ] Check `staging.languageforge.org` serves PHP 7.4.34-dev (`php -v`
in the app pod)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Both CVEs are unauthenticated RCE in PHP, highest risk on Apache mod_php, which is what Language Forge runs. PHP 7.4 is EOL so there is no fixed release and the official php:7.4-apache image will not be rebuilt. The PHP security team published a minimal source backport branch (PHP-7.4-CVE-2026-109618-109619); this recompiles PHP from that branch, pinned to commit 712e60f9, in place on top of the official image using the same configure flags, so the apache module, ini layout and extension tooling are unchanged.
Also needed to make the image buildable at all today:
Defence in depth: session.upload_progress.enabled=Off, a confirmed CVE-2026-109619 path that Language Forge does not use.
docs/RELEASE.md explains that the base PHP image must be published via the manual workflow before merging changes to docker/base-php.
Fixes #issue
Description
A summary of the change including motivation and context.
Screenshots
Demonstrate any UI / behavioral changes with screenshots or animations.
Checklist
Testing
Testers, use the following instructions against our staging environment. Post your findings as a comment and include any meaningful screenshots, etc.
Describe how to verify your changes and provide any necessary test data.