Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions .trivyignore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,36 @@
# future occurrences of the same CVE in a different location (e.g. if the
# Claude Code CLI later ships its own vulnerable copy).

vulnerabilities: []
# No active ignores. Explicit empty list (not bare `vulnerabilities:`, which
# parses as null) so the key unambiguously represents "no ignores".
# When the last entry is removed, write `vulnerabilities: []`. A bare
# `vulnerabilities:` parses as null, not an empty list.
vulnerabilities:
- id: GHSA-f88m-g3jw-g9cj
paths:
- "app/node_modules/sharp/package.json"
statement: |
sharp 0.34.5 inherits four libvips image-decoding CVEs (CVE-2026-33327,
CVE-2026-33328, CVE-2026-35590, CVE-2026-35591). Not reachable here.
sharp is a transitive dependency of @huggingface/transformers, which this
repo uses 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 passes an image to transformers, so libvips never decodes
attacker-supplied bytes. The module is additionally behind
REVIEW_LEARNINGS_RAG_ENABLED and a dynamic import, so a default deploy
does not even load it.

Not fixed by an override: the fix is sharp 0.35.0, and
@huggingface/transformers 4.2.0 (latest) declares `sharp: ^0.34.5`.
Forcing 0.35.x would break the declared range for an unreachable CVE.
Delete this entry once @huggingface/transformers ships a release that
depends on sharp >= 0.35.0.

No mirrored entry in scripts/audit-ci.ts: `bun run audit:ci` on this
branch reports total=0, so bun's advisory source does not surface this
GHSA today. The asymmetry is deliberate, not an oversight. If a future
advisory-DB sync turns audit:ci red on it, mirror this entry there with
the same expiry, and note that that allowlist matches by GHSA id alone
with no `paths` scoping, so it suppresses more broadly than this does.
expired_at: 2026-12-04
#
# Retired 2026-06-30 (all expired AND fixed upstream, per each entry's own
# "delete when fixed" instruction):
Expand Down
38 changes: 35 additions & 3 deletions Dockerfile.daemon
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,50 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
# by the Trivy gate. This block lives in the shared base so both images carry
# the patched versions. Targeted --only-upgrade (not a blanket apt-get upgrade)
# keeps the build reproducible and avoids hadolint DL3005.
#
# The util-linux family (2.41-5 -> 2.41.5-0+deb13u1) covers CVE-2026-53612,
# CVE-2026-53613 and CVE-2026-53614. These nine are every util-linux binary
# package present in a debian:trixie rootfs, which oven/bun is built FROM (the
# source builds 33, the rest are not installed here). Keep the list complete:
# --only-upgrade silently IGNORES a name that is not installed and does not
# error on a name that is omitted, so dropping one leaves that package at the
# vulnerable version with no build-time signal and only Trivy notices later.
# `login` is here because trixie builds it from the util-linux source under a
# `+really` version suffix, keeping shadow's epoch so it still sorts above the
# old shadow-built package (1:4.16.0-2+really2.41.5-0+deb13u1).
RUN apt-get update && \
apt-get install --only-upgrade -y --no-install-recommends \
openssl libssl3 \
python3.13 python3.13-minimal libpython3.13-stdlib libpython3.13-minimal \
libsystemd0 libudev1 libnghttp2-14 libcap2 \
libgssapi-krb5-2 libk5crypto3 libkrb5-3 libkrb5support0 && \
libgssapi-krb5-2 libk5crypto3 libkrb5-3 libkrb5support0 \
util-linux mount login bsdutils libblkid1 liblastlog2-2 libmount1 \
libsmartcols1 libuuid1 && \
rm -rf /var/lib/apt/lists/*

# Claude Code CLI required by @anthropic-ai/claude-agent-sdk. Declared as an
# ARG so renovate.json's custom regex manager keeps it current: the previous
# inline `@2.1.114` pin was invisible to every manager and drifted far enough
# behind to ship CVE-2026-55607 (arbitrary code execution via git directory
# confusion, fixed in 2.1.163).
#
# Value tracks the vendor's `stable` dist-tag, not `latest`/`next`. This CLI
# runs under bypassPermissions holding a GitHub installation token, so it takes
# the conservative channel. A hand-picked version must also clear the 7-day
# soak renovate.json enforces via minimumReleaseAge (that setting only governs
# Renovate-raised PRs, not a manual edit), and must be >= 2.1.163.
# renovate: datasource=npm depName=@anthropic-ai/claude-code
ARG CLAUDE_CODE_VERSION=2.1.236

# Tracks 11.x major (unlike the exact pins elsewhere).
# The version is validated before it reaches npm: `--build-arg
# CLAUDE_CODE_VERSION=` would otherwise expand to `...@`, which npm-package-arg
# resolves to the `*` range and silently installs whatever is newest, dropping
# the pin with no build failure.
RUN npm install -g npm@11 && \
# Claude Code CLI required by @anthropic-ai/claude-agent-sdk.
npm install -g @anthropic-ai/claude-code@2.1.114
printf '%s' "${CLAUDE_CODE_VERSION:?CLAUDE_CODE_VERSION must not be empty}" \
| grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' && \
npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}"

# Stage: Build: install all deps and bundle the main app.
FROM base AS development
Expand Down
38 changes: 35 additions & 3 deletions Dockerfile.orchestrator
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,50 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
# by the Trivy gate. This block lives in the shared base so both images carry
# the patched versions. Targeted --only-upgrade (not a blanket apt-get upgrade)
# keeps the build reproducible and avoids hadolint DL3005.
#
# The util-linux family (2.41-5 -> 2.41.5-0+deb13u1) covers CVE-2026-53612,
# CVE-2026-53613 and CVE-2026-53614. These nine are every util-linux binary
# package present in a debian:trixie rootfs, which oven/bun is built FROM (the
# source builds 33, the rest are not installed here). Keep the list complete:
# --only-upgrade silently IGNORES a name that is not installed and does not
# error on a name that is omitted, so dropping one leaves that package at the
# vulnerable version with no build-time signal and only Trivy notices later.
# `login` is here because trixie builds it from the util-linux source under a
# `+really` version suffix, keeping shadow's epoch so it still sorts above the
# old shadow-built package (1:4.16.0-2+really2.41.5-0+deb13u1).
RUN apt-get update && \
apt-get install --only-upgrade -y --no-install-recommends \
openssl libssl3 \
python3.13 python3.13-minimal libpython3.13-stdlib libpython3.13-minimal \
libsystemd0 libudev1 libnghttp2-14 libcap2 \
libgssapi-krb5-2 libk5crypto3 libkrb5-3 libkrb5support0 && \
libgssapi-krb5-2 libk5crypto3 libkrb5-3 libkrb5support0 \
util-linux mount login bsdutils libblkid1 liblastlog2-2 libmount1 \
libsmartcols1 libuuid1 && \
rm -rf /var/lib/apt/lists/*

# Claude Code CLI required by @anthropic-ai/claude-agent-sdk. Declared as an
# ARG so renovate.json's custom regex manager keeps it current: the previous
# inline `@2.1.114` pin was invisible to every manager and drifted far enough
# behind to ship CVE-2026-55607 (arbitrary code execution via git directory
# confusion, fixed in 2.1.163).
#
# Value tracks the vendor's `stable` dist-tag, not `latest`/`next`. This CLI
# runs under bypassPermissions holding a GitHub installation token, so it takes
# the conservative channel. A hand-picked version must also clear the 7-day
# soak renovate.json enforces via minimumReleaseAge (that setting only governs
# Renovate-raised PRs, not a manual edit), and must be >= 2.1.163.
# renovate: datasource=npm depName=@anthropic-ai/claude-code
ARG CLAUDE_CODE_VERSION=2.1.236

# Tracks 11.x major (unlike the exact pins elsewhere).
# The version is validated before it reaches npm: `--build-arg
# CLAUDE_CODE_VERSION=` would otherwise expand to `...@`, which npm-package-arg
# resolves to the `*` range and silently installs whatever is newest, dropping
# the pin with no build failure.
RUN npm install -g npm@11 && \
# Claude Code CLI required by @anthropic-ai/claude-agent-sdk.
npm install -g @anthropic-ai/claude-code@2.1.114
printf '%s' "${CLAUDE_CODE_VERSION:?CLAUDE_CODE_VERSION must not be empty}" \
| grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' && \
npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}"

# Stage: Build: install all deps and bundle the main app.
FROM base AS development
Expand Down
8 changes: 4 additions & 4 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 21 additions & 9 deletions docs/operate/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ The two images intentionally diverge after the shared base because their cost an

### Shared base stages

| Stage | Base | Purpose |
| ------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `base` | `oven/bun:1.3.14` | Installs Node.js 20 (for the Claude Code CLI), npm 11, `curl`, `git`, `@anthropic-ai/claude-code` globally, plus targeted openssl CVE upgrades. |
| `development` | `base` | `bun install` (all deps) + `bun run build` → `dist/` (app, daemon, workflow runner, process-boundary probe, MCP stdio servers). |
| `deps` | `base` | `bun install --production --ignore-scripts` (runtime deps only). |
| Stage | Base | Purpose |
| ------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `base` | `oven/bun:1.3.14` | Installs Node.js from NodeSource (major pinned in the Dockerfile, currently 25) for the Claude Code CLI, npm 11, `curl`, `git`, `@anthropic-ai/claude-code` globally (pinned via the `CLAUDE_CODE_VERSION` ARG), plus targeted OS CVE upgrades. |
| `development` | `base` | `bun install` (all deps) + `bun run build` → `dist/` (app, daemon, workflow runner, process-boundary probe, MCP stdio servers). |
| `deps` | `base` | `bun install --production --ignore-scripts` (runtime deps only). |

### Orchestrator-only stage

Expand Down Expand Up @@ -50,10 +50,11 @@ The daemon image compiles a native preload guard that sets `PR_SET_DUMPABLE=0` b

### Build arguments

| Argument | Default | Purpose |
| ----------------- | ------------- | ------------------------------------------------------------ |
| `PACKAGE_VERSION` | `untagged` | Stored as Docker label `com.chrisleekr.bot.package-version`. |
| `GIT_HASH` | `unspecified` | Stored as Docker label `com.chrisleekr.bot.git-hash`. |
| Argument | Default | Purpose |
| --------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PACKAGE_VERSION` | `untagged` | Stored as Docker label `com.chrisleekr.bot.package-version`. |
| `GIT_HASH` | `unspecified` | Stored as Docker label `com.chrisleekr.bot.git-hash`. |
| `CLAUDE_CODE_VERSION` | pinned in the Dockerfile, never passed by CI | Global `@anthropic-ai/claude-code` CLI pin, tracking the vendor `stable` dist-tag. Carries a `# renovate:` marker so `renovate.json`'s custom regex manager bumps it. Deliberately not restated as a literal here: Renovate moves it and no CI gate compares this page against the Dockerfile. |

Daemon-only:

Expand All @@ -71,6 +72,17 @@ docker build -f Dockerfile.orchestrator \
.
```

### Dependency overrides

`package.json`'s `overrides` block force-pins transitive dependencies past what their parent declared, almost always to clear a CVE the parent has not yet picked up. JSON carries no comments, so each pin's reason and its removal condition live here. Drop an entry once its condition is met, otherwise it silently holds a package back after the reason expires.

| Override | Pinned | Reason | Remove when |
| ------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| `js-yaml` | `4.3.2` | CVE-2026-59869 (fixed 4.3.0), GHSA-5p4m-2wfm-xmqj (fixed 4.3.1), GHSA-2883-xcg3-v3hh (fixed 4.3.2). Reached via `@kubernetes/client-node` and `cosmiconfig`, both declaring `^4.1.0`. | `@kubernetes/client-node` declares `js-yaml >= 4.3.2`. |
| `ip-address` | `10.7.0` | CVE-2026-69192 SSRF (fixed 10.3.1). Reached via `express-rate-limit` (`^10.2.0`) and `socks` (`^10.0.1`), which resolved two separate copies before the pin. | Both parents declare `ip-address >= 10.3.1`. |

The remaining entries predate this table; add a row when you touch one.

### Verifying image attestations

> **Attestations are currently disabled.** The `provenance: mode=max` / `sbom: true` inputs, the `attestations: write` permission, and the `gh attestation verify` gate in `.github/workflows/docker-build.yml` are all commented out because the SBOM exceeded GitHub's 16MB attestation limit. The GitLab builds pass `--provenance false`. **No published tag carries an attestation today**, so the `gh attestation verify` commands below will fail until the inputs are restored. The section is retained because the workflow code is retained in place for re-enablement.
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@
"flatted": "3.4.4",
"form-data": "4.0.6",
"hono": "4.12.29",
"ip-address": "10.7.0",
"js-yaml": "4.3.2",
"path-to-regexp": "8.4.2",
"picomatch": "4.0.5",
"protobufjs": "7.6.5",
Expand Down
Loading