Skip to content

fix(deps): clear the orchestrator image's HIGH CVE findings - #295

Merged
chrisleekr merged 3 commits into
mainfrom
fix/orchestrator-high-cves
Sep 4, 2026
Merged

fix(deps): clear the orchestrator image's HIGH CVE findings#295
chrisleekr merged 3 commits into
mainfrom
fix/orchestrator-high-cves

Conversation

@chrisleekr

Copy link
Copy Markdown
Owner

Problem

Trivy flagged 33 CRITICAL/HIGH findings on chrisleekr/github-app:1.17.0-orchestrator under the CI flag set (--severity CRITICAL,HIGH --ignore-unfixed). Four distinct causes, four different correct fixes.

Finding Count Fix
util-linux family, CVE-2026-53612/53613/53614 27 Base-image --only-upgrade
js-yaml 4.1.1, CVE-2026-59869 + GHSA-5p4m-2wfm-xmqj 2 Override → 4.3.1
ip-address 10.1.0/10.2.0, CVE-2026-69192 2 Override → 10.7.0
@anthropic-ai/claude-code 2.1.114, CVE-2026-55607 1 Renovate-managed ARG → 2.1.260
sharp 0.34.5, GHSA-f88m-g3jw-g9cj 1 Documented ignore, not an override

The claude-code CVE had a structural cause

The bump is the symptom. The cause: the pin was written inline as npm install -g @anthropic-ai/claude-code@2.1.114, and renovate.json's custom regex manager only matches ARG *_VERSION= lines carrying a # renovate: marker. That pin was invisible to every dependency manager and drifted 146 patch releases behind into a known arbitrary-code-execution advisory.

Converting it to a renovate-managed ARG is the real fix, so it cannot silently rot again. Both Dockerfiles get the identical change (check:dockerfile-base-sync enforces the shared prefix byte-for-byte).

If you'd rather land the minimum fix version (2.1.163) and let Renovate walk it forward, say so and I'll change it. This is the agent runtime, and 2.1.114 → 2.1.260 is a wide jump.

Why sharp is ignored rather than bumped

The fix is sharp 0.35.0, but @huggingface/transformers@4.2.0 (latest, no newer release) declares sharp: ^0.34.5. An override would force a semver-incompatible version.

I checked reachability instead. The four inherited CVEs are libvips image-decoding paths. This repo uses transformers for exactly one thing: the text feature-extraction pipeline in src/orchestrator/embedding.ts (Xenova/bge-small-en-v1.5, 384-dim). No code path hands an image to libvips, and the module sits behind REVIEW_LEARNINGS_RAG_ENABLED plus a dynamic import, so a default deploy never loads it.

Recorded in .trivyignore.yaml with a written reachability statement and expired_at: 2026-12-04, exactly as that file's own POLICY block requires. Scoped with paths: to the one file Trivy flagged, so a future vulnerable copy elsewhere still trips the gate.

util-linux: why all nine packages

Nine binary packages built from one source, all at 2.41-5 in oven/bun:1.3.14, all fixed at 2.41.5-0+deb13u1. They carry tight inter-package version constraints, so upgrading a subset leaves apt unable to resolve. login is in the list because Debian ships it from the util-linux source under a +really epoch. Added to the existing targeted --only-upgrade block rather than a blanket apt-get upgrade, keeping the build reproducible and hadolint DL3005 quiet.

Verification

Built Dockerfile.orchestrator --target production locally and scanned with the exact CI flag set:

  • 0 CRITICAL/HIGH, down from 33
  • Without the ignorefile, exactly one finding remains: the documented sharp entry. Nothing else is masked.
  • Confirmed inside the built image: js-yaml 4.3.1, ip-address 10.7.0 (single copy — the nested socks/node_modules/ip-address collapsed), util-linux family all 2.41.5-0+deb13u1, claude --version2.1.260 exit 0.

Gates: typecheck 0 errors, lint 0 errors (621 pre-existing warnings), audit:ci 0 advisories, Prettier clean, check:dockerfile-base-sync / check:docs-versions / check:docs-citations / check:no-em-dashes all OK.

Test suite with local Postgres + Valkey up: 211 files passed, 0 assertion failures. The 2 non-passing files (test/integration/repo-knowledge, test/integration/review-learnings) are skipIf(sql === null) environment skips; I verified they skip identically on unmodified main (13 skip / 17 skip, 0 fail), so they are pre-existing and unrelated.

Note on the npm postinstall warning

The build logs npm warn install-scripts @anthropic-ai/claude-code@2.1.260 (postinstall: node install.cjs) — npm 11 blocks global install scripts by default. This is pre-existing (identical on the shipped 1.17.0 image) and benign: claude --version works in both. Flagging it because it is visible in the build output, not because this PR changed it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KUPpJPtxAaHWrBsjytRGyM

Trivy flagged 33 CRITICAL/HIGH findings on
chrisleekr/github-app:1.17.0-orchestrator under the CI flag set. Four
distinct causes, four different correct fixes.

util-linux (27 findings, CVE-2026-53612/53613/53614)
  Nine binary packages from one source, all at 2.41-5 in the oven/bun
  base, all fixed in Debian at 2.41.5-0+deb13u1. Added to the existing
  targeted --only-upgrade block rather than a blanket apt-get upgrade, so
  the build stays reproducible and hadolint DL3005 stays quiet. All nine
  go together: they carry tight inter-package version constraints, so a
  partial list leaves apt unable to resolve.

js-yaml 4.1.1 -> 4.3.1 (CVE-2026-59869, GHSA-5p4m-2wfm-xmqj)
ip-address 10.1.0/10.2.0 -> 10.7.0 (CVE-2026-69192)
  Both transitive, both in range for every declaring consumer, so a
  plain override collapses them. ip-address had two copies in the image
  (app/node_modules and app/node_modules/socks/node_modules); the
  override resolves the lockfile to a single 10.7.0 entry.

@anthropic-ai/claude-code 2.1.114 -> 2.1.260 (CVE-2026-55607, arbitrary
code execution via git directory confusion, fixed in 2.1.163)
  The version bump is the symptom. The cause is that the pin was written
  inline as `npm install -g @anthropic-ai/claude-code@2.1.114`, and
  renovate.json's custom regex manager only matches `ARG *_VERSION=`
  lines carrying a `# renovate:` marker. So this pin was invisible to
  every manager and drifted 146 patch releases behind into a known RCE.
  Converted to a renovate-managed ARG so it cannot silently rot again.

sharp 0.34.5 (GHSA-f88m-g3jw-g9cj): ignored, not bumped
  Deliberately NOT an override. The fix is sharp 0.35.0 and
  @huggingface/transformers 4.2.0 (latest) declares `sharp: ^0.34.5`, so
  forcing 0.35.x would break the declared range. The four inherited
  libvips CVEs are image-decoding paths, and this repo uses transformers
  for exactly one thing: the text feature-extraction pipeline in
  src/orchestrator/embedding.ts. No code path hands an image to libvips,
  and the module sits behind REVIEW_LEARNINGS_RAG_ENABLED plus a dynamic
  import. Recorded in .trivyignore.yaml with a reachability statement and
  expired_at 2026-12-04, per that file's stated policy.

Verified by building Dockerfile.orchestrator --target production locally
and scanning the result with the exact CI flag set: 0 CRITICAL/HIGH,
down from 33. Without the ignorefile exactly one finding remains, the
documented sharp entry, so nothing else is masked. Confirmed inside the
image: js-yaml 4.3.1, ip-address 10.7.0 (single copy), util-linux family
2.41.5-0+deb13u1, and `claude --version` reporting 2.1.260 exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KUPpJPtxAaHWrBsjytRGyM
@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 19 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: fe5624de-dc11-493b-a683-98f1510f7f8f

📥 Commits

Reviewing files that changed from the base of the PR and between 6965322 and 9347c01.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • .trivyignore.yaml
  • Dockerfile.daemon
  • Dockerfile.orchestrator
  • docs/operate/deployment.md
  • package.json

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Senior review panel findings (CodeRabbit was rate-limited on this PR, so the
panel was the only review gate). Five fixes, two verified against primary
sources before acting.

js-yaml 4.3.1 -> 4.3.2
  4.3.1 is one patch short of clean. GHSA-2883-xcg3-v3hh (HIGH,
  maxTotalMergeKeys does not limit CPU use for empty merge sources) affects
  >= 4.0.0 < 4.3.2 and is patched in 4.3.2, confirmed against GitHub's
  security-advisories API for nodeca/js-yaml. Pinning 4.3.1 would have
  re-opened the exact orchestrator HIGH gate this PR closes as soon as the
  advisory syncs into the global DB. 4.3.2 still satisfies both declaring
  consumers (@kubernetes/client-node and cosmiconfig, each ^4.1.0).

claude-code 2.1.260 -> 2.1.236
  2.1.260 was published the same day it was pinned. renovate.json enforces a
  7-day minimumReleaseAge on patch releases specifically "to catch bad
  releases before automerge", and that setting governs only Renovate-raised
  PRs, not a hand-edited pin. This CLI runs under bypassPermissions holding a
  GitHub installation token, so it is the wrong package to take a same-day
  release on. 2.1.236 is the vendor's own `stable` dist-tag, 15 days old, and
  above the 2.1.163 CVE-2026-55607 fix floor.

Empty-build-arg guard
  `--build-arg CLAUDE_CODE_VERSION=` would expand to
  `npm install -g "@anthropic-ai/claude-code@"`, and npm-package-arg resolves
  an empty spec to the `*` range, installing whatever is newest with no build
  failure. The pin would vanish silently. Now validated for exact semver
  before it reaches npm. Not currently reachable (docker-build.yml passes
  three fixed build args and no workflow input flows into them), so this is
  hardening.

util-linux comment rationale corrected
  The old comment claimed a partial list "leaves apt unable to satisfy the
  tight inter-package version constraints". That is not how --only-upgrade
  behaves: apt IGNORES names that are not installed and does not error on an
  omitted one, so trimming the list would silently leave a vulnerable package
  with no build-time signal. Also corrected "epoch" (the epoch is the `1:`
  prefix; `+really` is an upstream-version suffix) and the implication that
  the source builds only nine packages (it builds 33; nine are present in a
  debian:trixie rootfs).

Docs
  deployment.md said "Node.js 20"; the Dockerfiles add NodeSource node_25.x.
  Added a Dependency overrides table recording each pin's reason and removal
  condition, since JSON cannot carry comments. Recorded why no mirrored entry
  exists in audit-ci.ts (bun audit reports total=0 for this GHSA today) so the
  asymmetry is deliberate. Restored the null-vs-empty-list note that was lost
  when .trivyignore.yaml gained its first live entry.

Rebuilt and rescanned: 0 CRITICAL/HIGH under the CI flag set. Confirmed in the
image: claude 2.1.236, js-yaml 4.3.2, ip-address 10.7.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KUPpJPtxAaHWrBsjytRGyM
@chrisleekr

Copy link
Copy Markdown
Owner Author

Senior review panel round applied (3224d34)

CodeRabbit was rate-limited on this PR, so the three-lens review panel was the only review gate. It caught two things worth calling out, both verified against primary sources before acting:

1. js-yaml 4.3.1 was one patch short of clean. GHSA-2883-xcg3-v3hh (HIGH, maxTotalMergeKeys CPU exhaustion) affects >= 4.0.0 < 4.3.2, patched in 4.3.2 — confirmed via GitHub's security-advisories API, not taken on the lens's word. Pinning 4.3.1 would have re-opened the exact orchestrator HIGH gate this PR exists to close, the moment that advisory syncs into the global DB. Now 4.3.2, still in range for @kubernetes/client-node and cosmiconfig (both ^4.1.0).

2. The claude-code pin violated this repo's own supply-chain policy. renovate.json:63-66 mandates a 7-day minimumReleaseAge on patch releases, explicitly "to catch bad releases before automerge". 2.1.260 was published the same day I pinned it, and minimumReleaseAge only governs Renovate-raised PRs, not a hand-edited pin. That is the wrong package to take a same-day release on: the CLI runs under bypassPermissions holding a GitHub installation token.

Now pinned to 2.1.236 — the vendor's own stable dist-tag, 15 days old, above the 2.1.163 fix floor for CVE-2026-55607. That satisfies both the soak requirement and the release-channel question the panel raised (latest/next are both 2.1.260; stable is the conservative channel).

Also fixed:

  • Empty --build-arg CLAUDE_CODE_VERSION= would expand to ...claude-code@, which npm-package-arg resolves to the * range — installing whatever is newest, pin silently gone, no build failure. Now validated as exact semver before it reaches npm. Not currently reachable (no workflow input flows into build args), so this is hardening.
  • The --only-upgrade comment claimed a partial package list makes apt fail. It does not: apt ignores names that are not installed, so trimming the list would silently leave a vulnerable package with no build-time signal. Corrected, along with "epoch" (the epoch is the 1: prefix; +really is a version suffix) and the "nine packages from that source" implication (the source builds 33; nine are present in a debian:trixie rootfs).
  • Docs said "Node.js 20"; the Dockerfiles add NodeSource node_25.x.
  • Added a Dependency overrides table to deployment.md recording each pin's reason and removal condition, since JSON carries no comments.
  • Recorded why there is no mirrored entry in scripts/audit-ci.ts: bun run audit:ci reports total=0 for this GHSA today, so the asymmetry is deliberate rather than an oversight.
  • Restored the null-vs-empty-list note lost when .trivyignore.yaml gained its first live entry.

Re-verified after the changes: rebuilt Dockerfile.orchestrator --target production and rescanned with the CI flag set → 0 CRITICAL/HIGH. Confirmed inside the image: claude 2.1.236, js-yaml 4.3.2, ip-address 10.7.0.

Two findings declined, with reasons:

  • A CI guard enforcing the .trivyignore.yaml policy (new check:trivyignore script + test). Legitimate gap — the policy is prose in a comment and this PR adds the first live entry, so it goes from vacuously true to load-bearing. But a new guard script is scope creep in a CVE-remediation PR. Worth a follow-up issue.
  • docs/build/conventions.md:7 also says "Node.js 20". Same factual error, but that file is not in this PR's changed set and ci: decouple Trivy CVE scan from the release path #294 already touches it. Flagging rather than reaching across PRs.

@chrisleekr
chrisleekr merged commit f4b8f03 into main Sep 4, 2026
13 of 15 checks passed
@chrisleekr
chrisleekr deleted the fix/orchestrator-high-cves branch September 4, 2026 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant