Stop shipping build-time caches and host identity in published images - #14
Merged
Conversation
An audit of all six release images, built and scanned as filesystems rather than read as Dockerfiles, found no organization data, credentials, or repository content. It did find build-environment residue that becomes public the moment these images are pushed to a public registry. /root/.npm ships in core, portal, auth and web-ui. npm's cacache stores every fetch URL verbatim as the cache key, so log redaction never applies: the core image carried a live GitHub release-asset JWT and Azure SAS signature from resolving @earendil-works/pi-coding-agent over a signed URL. Those particular tokens expire minutes after the build and grant read on an already-public asset, so the exposure is nil, but the mechanism is not: any dependency resolved through an authenticated or signed URL is published verbatim. Cached reqHeaders are empty, so only URL-embedded secrets are affected. Removing the cache also drops core from 4.44 GB to 3.12 GB. sandbox-base baked a fixed /etc/machine-id, generated by systemd's postinst as a chromium dependency, so every container from the published image presented the same 128-bit host identity. Debian ships this empty for the same reason. The gh device-id, generated by the version smoke test, had the same problem. browser-use defaults telemetry and cloud sync on, giving an agent sandbox that browses for users an unconfigured outbound path to eu.i.posthog.com and api.browser-use.com. Disabled by default; operators can re-enable. DEBIAN_FRONTEND was an ENV, so it persisted into the runtime and silently suppressed prompts for anything the agent ran. It is a build concern. Verified by rebuilding all six and re-inspecting: caches absent from all four node images, both machine-id files zero bytes, gh device-id gone, telemetry off, DEBIAN_FRONTEND absent from the runtime environment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
All six release images were built and scanned as filesystems —
docker export, full-tree scan, per-layer whiteout analysis, metadata inspection — rather than audited by reading Dockerfiles.No organization data, credentials, or repository content leaks in any of the six. Zero hits for
ycombinator,quartermaster,work-claw, or the old AWS account, on any image. No.git,.env, SSH keys, or cloud configs. The web-ui client bundle — the only artifact that reaches browsers — has no source maps, no build-machine paths, and no inlined build env.What the audit did find is build-environment residue that becomes public the moment these are pushed to a public registry.
/root/.npmships in core, portal, auth, web-uinpm's cacache stores every fetch URL verbatim as the cache key, so npm's own log redaction never applies. The core image carried a live GitHub release-asset JWT and Azure SAS signature, from resolving
@earendil-works/pi-coding-agentover a signed release URL:Those specific tokens are worthless — 300-second JWT TTL, SAS expires an hour after build, and both grant read on an asset that is already public. The mechanism is the problem: any dependency resolved through an authenticated or signed URL gets published verbatim. Cached
reqHeadersare empty, so header-based auth is unaffected; only URL-embedded secrets are exposed.Removing the cache also takes core from 4.44 GB to 3.12 GB.
sandbox-basebaked a fixed host identity/etc/machine-idand/var/lib/dbus/machine-idboth heldb2dc9eb4b2c34319bb4861e8c984bab3— identical across separatedocker runinvocations. Generated by systemd's postinst, pulled in as a chromium dependency. Every container anyone runs from the published image would present the same 128-bit identity to systemd, D-Bus, and anything keyed on it. Debian's own cloud images ship these empty for exactly this reason. Theghdevice-id, a side effect of the version smoke test, had the same problem.browser-use telemetry was on by default
An agent sandbox that browses on users' behalf shipped with an unconfigured outbound data path to
eu.i.posthog.comandapi.browser-use.com, with exception autocapture enabled. Now off by default; operators can re-enable via env.DEBIAN_FRONTENDwas anENVSo it persisted into the runtime and silently suppressed prompts for anything the agent ran. It is a build-time concern — demoted to
ARG.Verification
Rebuilt all six and re-inspected:
/root/.npmin core/portal/auth/web-ui/tmp/node-compile-cache/etc/machine-id,/var/lib/dbus/machine-idghdevice-idANONYMIZED_TELEMETRY/BROWSER_USE_CLOUD_SYNCfalseDEBIAN_FRONTENDin runtime envDeliberately not in this PR
lru-cache, and chassis. Bloat and vulnerability surface, not exposure: static serving is confined todist-webbehind a traversal guard. Fixing it means restructuring the multi-stage build, which deserves its own change.rm package-lock.jsonin the web-ui Dockerfile does nothing — the file is recoverable byte-identical from the underlyingCOPYlayer. Left alone rather than change what ships inside a hardening PR.release-package.ymlsetsprovenance: falseso it never reaches GHCR — but that is one deleted line away from a private fork publishing its repo URL and commit SHA. Worth making durable separately.Caveats
Images were built on arm64 under emulation; CI builds native amd64 with GHA layer cache. Detection was literal-text regex and
strings, not entropy analysis, so encoded or compressed secrets would not surface. Worth adding gitleaks or trufflehog to CI for standing coverage rather than point-in-time passes.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.