Skip to content

sandbox: shrink image 7.2GB → 3.8GB (cache cleanup, multi-stage Go build, drop ZAP)#474

Merged
0xallam merged 7 commits into
usestrix:mainfrom
seanturner83:sandbox/slim-image
Jul 22, 2026
Merged

sandbox: shrink image 7.2GB → 3.8GB (cache cleanup, multi-stage Go build, drop ZAP)#474
0xallam merged 7 commits into
usestrix:mainfrom
seanturner83:sandbox/slim-image

Conversation

@seanturner83

@seanturner83 seanturner83 commented May 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Three safe reclamations to containers/Dockerfile that shrink the final image by ~2.3GB / ~23% without removing any user-facing tool:

Change Approx saving
go clean -modcache after go install chain ~1.7GB
Purge non-English glibc locales (keep en, en_US, C) ~160MB
Remove /usr/share/{doc,doc-base,man} ~95MB

Measured: 9.78GB → 7.49GB on linux/amd64 build.

Why

The sandbox image is pulled per-scan in CI setups that use Strix, and cold-runner docker pull / docker load times are ~3-5 min, dominating the warm-up envelope for fast scans. Trimming ~2GB directly reduces that latency. No functional change — every binary present before is present after.

Safety

Go modcache. go install -v <pkg>@latest downloads sources into $GOPATH/pkg/mod and produces a statically-linked binary in $GOPATH/bin. The binaries don't reference the module cache at runtime. go clean -modcache removes the source tree; subsequent go install invocations re-download on demand (same behaviour as a fresh container).

Verified locally by running each Go binary after the clean:

httpx             OK
katana            OK
vulnx             OK
gospider          OK
interactsh-client OK

Locales. The image runs with LANG=C.UTF-8 implicitly (no explicit LANG= set, no locales package pulled in with a locale-gen step). glibc falls back to C when requested locales are absent. Purging /usr/share/locale/* except en, en_US, C changes nothing for any process running in the sandbox.

Docs/man. Tool runtime doesn't call man or read /usr/share/doc/*. Package postinst scripts reference these during install, not at runtime.

All three reclamations ride inside the existing apt-get autoremove cleanup layer, so no new layer is introduced.

Smoke test (manual, linux/amd64)

docker build -f containers/Dockerfile -t strix-sandbox:slim .
docker run --rm --entrypoint /bin/bash strix-sandbox:slim -c '
for t in semgrep bandit nuclei httpx katana trufflehog trivy zaproxy nmap \
         sqlmap ffuf subfinder naabu gitleaks wapiti ast-grep eslint retire \
         jshint tree-sitter arjun dirsearch wafw00f js-beautify; do
  command -v $t >/dev/null && echo "OK  $t" || echo "MISSING $t"
done
'

All tools report OK on the slim image. Size confirmed via docker images.

Non-goals

  • Not removing any tool
  • Not changing any pinned version
  • Not touching the CA setup, docker-entrypoint.sh, Caido proxy, or Python/Node runtime
  • Not adding CI to build the container (separate conversation — there's no .github/workflows/* that builds the sandbox today, so this PR has been tested manually)

Update: additional cache reclamation (+~1.1GB)

Follow-up commit sandbox: also clear go build cache and npm cache extends the same "safe, same-layer, no tool removal" approach:

Change Approx saving
go clean -cache (added alongside the existing -modcache) ~1.1GB
npm cache clean --force after the npm install -g chain ~79MB

Why: go clean -modcache only removes the module source cache ($GOPATH/pkg/mod); the Go build cache ($GOCACHE, ~/.cache/go-build) was still shipping in the image at ~1.1GB. It only speeds up recompilation and is never read at runtime, so clearing it is safe. Likewise the npm cache (~/.npm) is build-time only.

Measured (linux/amd64, all off the same main base):

Image Size
main (base) 7.24GB
this PR, before follow-up 5.85GB
this PR, with follow-up 4.75GB

Net vs base: −2.49GB (~34%).

Verified on the trimmed image: all 30 tools present and runnable; every Go binary executes after the cache clean; tree-sitter parse works; httpx live-probes a target (200); $GOCACHE down to ~12K. A full strix scan boots the sandbox and runs httpx/nuclei from the trimmed image with no functional change.


Update 2: multi-stage build + drop ZAP & C/C++ build chain (−~1GB more)

Structural trims on top of the cache work. Same "no user-facing tool loss" philosophy, with the one intentional exception of ZAP (removed on request).

Change Approx saving
Multi-stage Go build — the Go toolchain (golang-go, ~225MB) + module/build caches now live only in a gobuilder stage; just the 5 static binaries (httpx, katana, vulnx, gospider, interactsh-client) are COPY --from'd into runtime ~225MB + caches
Remove OWASP ZAP + its JRE ~500MB
Drop the heavy C/C++ build chain (build-essential, pkg-config, libpcap-dev, libssl-dev, python3-dev) ~220MB
De-dupe ast-grep@ast-grep/cli ships two byte-identical binaries (ast-grep and sg); symlink sg → ast-grep ~52MB

Kept: gcc + libc6-dev (tree-sitter compiles grammar .sos on demand at runtime — dropping the C compiler breaks tree-sitter parse), plus trivy, wapiti, caido, uv, and vim. Also removed the two zaproxy references and the stale Go/go install mention from the agent system prompt (the Go scanners are prebuilt and on PATH).

Measured (linux/amd64): 7.24GB → 3.77GB (−3.47GB / −48% vs base).

Verified on the final image: all tools present & runnable except the intentionally-removed ZAP (httpx/katana/vulnx/gospider/interactsh-client, trivy, wapiti, caido-cli, nuclei/subfinder/naabu/ffuf/sqlmap, semgrep/bandit/trufflehog/gitleaks, uv, vim); sg run and tree-sitter parse work (the latter compiles python.so via the retained gcc); go and zaproxy are gone as intended.

Link to Devin session: https://app.devin.ai/sessions/7bfd32c02ecd423fa3654576b9352c04
Requested by: @0xallam

@greptile-apps

greptile-apps Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR substantially reduces the sandbox image size while keeping the remaining tools available. The main changes are:

  • Builds Go-based scanners in a separate builder stage.
  • Removes ZAP and unneeded runtime build packages.
  • Clears Go, npm, APT, locale, documentation, and manual-page data.
  • Deduplicates the ast-grep executables.
  • Updates the agent prompt to match the runtime toolset.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the updated code.

Important Files Changed

Filename Overview
containers/Dockerfile Adds the multi-stage Go build and reduces the runtime image through package removal, cache cleanup, and tool-install changes.
strix/agents/prompts/system_prompt.jinja Removes ZAP and Go toolchain claims so the agent prompt matches the trimmed image.

Reviews (5): Last reviewed commit: "sandbox: keep trufflehog self-update wor..." | Re-trigger Greptile

@0xallam
0xallam force-pushed the main branch 4 times, most recently from 44e87ca to daf39a2 Compare July 13, 2026 23:50
Three safe reclamations to containers/Dockerfile that trim the final
image from ~9.8GB to ~7.5GB without removing any user-facing tool:

- `go clean -modcache` after the ProjectDiscovery installs removes
  ~1.7GB of Go module sources. The resulting binaries in
  /home/pentester/go/bin are self-contained and don't need the
  module cache at runtime.
- Purge non-English glibc locales (~160MB). Sandbox runs with
  LANG=C.UTF-8 implicitly; no locale beyond en/en_US/C is used.
- Remove /usr/share/doc, /usr/share/doc-base, and /usr/share/man
  (~95MB). Runtime doesn't call man or read package docs.

Verified by building + smoke-testing the slim image: all path-visible
tools (semgrep, nuclei, httpx, katana, trivy, zaproxy, trufflehog,
gitleaks, sqlmap, ffuf, subfinder, naabu, bandit, arjun, dirsearch,
wafw00f, tree-sitter, ast-grep, eslint, retire, jshint, js-beautify,
wapiti) report version / usage cleanly on the slim image.
man-db is vestigial once the man page tree is removed in the cleanup
layer — the man binary has nothing to serve. Dropping it from the
install list recovers a few more MB and avoids shipping a no-op
package + its dependencies (groff-base, libpipeline1, etc).

Raised by greptile on the slim-image PR.
@bearsyankees

Copy link
Copy Markdown
Collaborator

@greptile

@devin-ai-integration

Copy link
Copy Markdown
Contributor

@greptile

@devin-ai-integration devin-ai-integration Bot changed the title sandbox: shrink image by ~2GB (go modcache, locales, docs) sandbox: shrink image 7.2GB → 3.8GB (cache cleanup, multi-stage Go build, drop ZAP) Jul 22, 2026
@devin-ai-integration

devin-ai-integration Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

E2E test results — slimmed sandbox image (updated)

Tested by building the image locally and exercising it with Docker + real Strix scans (shell-only; no UI). Final image 3.8 GB vs base 7.24 GB (−48%).

Every tool present & functional (full system-prompt TOOLS list + Dockerfile utilities)

Recon/scan: nmap 7.99 · ncat · ndiff · subfinder · naabu 2.6.1 · httpx · gospider v1.1.6
Vuln: nuclei v3.10.0 · sqlmap 1.10.6 · trivy 0.72.0 (+trivy fs) · wapiti
Fuzz/discovery: ffuf 2.1.0 · dirsearch · katana v1.6.1 · arjun · vulnx
JS: retire 5.4.3 · eslint v10.7.0 · jshint 2.13.6 · js-beautify 2.0.3 · JS-Snooper · jsniper.sh
Code analysis: semgrep 1.170.1 · bandit 1.9.4 · ast-grep 0.44.1 · sg→dedupe symlink · tree-sitter 0.26.11 · trufflehog 3.95.9 · gitleaks 8.30.1
Specialized: jwt_tool v2.3.0 · wafw00f · interactsh-client
Proxy/runtime: caido-cli 0.56.0 · python3 + venv libs + caido_api · uv 0.11.31 · node v24 · npm · pipx · chromium · agent-browser
Utilities: git · vim · nano · jq · rg · gdb · certutil · dig · whois · … all present

Real network runs (with NET_ADMIN/NET_RAW): httpx→[200], katana/gospider crawled, naabu→scanme.nmap.org:80 open, nmap→80/tcp open (no "Operation not permitted"), ffuf→3 hits.

SAST chain detects seeded vulns

semgrep (3 findings) · bandit (B608/B602/B105) · gitleaks (github-pat) · trufflehog (Github) · ast-grep (matched sinks) · tree-sitter (parsed → compiles grammar via the retained gcc, proving that keep-decision).

trufflehog self-update — fixed (commit 873909d)

The root-owned binary couldn't be overwritten by the non-root pentester user, so trufflehog's self-update failed (cannot move binary) and aborted scans. Now installed to /home/pentester/.local/bin (pentester-owned, on PATH), initial version pinned 3.95.9. Verified in the final image: seeded v3.95.8 → ran without --no-updateself-updated to v3.95.9 with no error, secret still detected; no root-owned leftover.

Real Strix scan on the final image + removals

strix --target https://example.com -n -m quick on the final image: boots (image=strix-sandbox:pr474th, caps applied), bootstraps Caido, spawns agents, loads httpx skill, finish_scan: completed scan with 0 vulnerability report(s).
One of 3 runs ended agent.status=failed on an Invalid HTTPQL query (agent/LLM wrote a malformed filter) — did not reproduce, and a base-image control run behaves identically → LLM nondeterminism, not the image.

Removals verified in-image: go, zaproxy, g++/cc1plus, pkg-config absent; gcc retained; sg→ast-grep dedupe symlink; prompt has 0 zaproxy / 0 go-install, uv still listed.
nuclei templates dir ships empty on both this image and base (runtime fetch) — not a trim regression.

Session: https://app.devin.ai/sessions/7bfd32c02ecd423fa3654576b9352c04

The trufflehog binary is root-owned but the sandbox runs as non-root
pentester, so its self-update fails ('cannot move binary') and aborts
the scan. Pin the version and wrap the binary to force --no-update
(guarded so a caller-supplied --no-update doesn't duplicate the flag).
@devin-ai-integration

Copy link
Copy Markdown
Contributor

@greptile

…ned dir)

Instead of disabling self-update, install trufflehog under
/home/pentester/.local/bin (on PATH, owned by the non-root runtime user)
so its in-place binary swap succeeds. This fixes the 'cannot move binary'
abort while preserving runtime detector freshness. Initial version pinned
for reproducible builds.
@devin-ai-integration

Copy link
Copy Markdown
Contributor

@greptile

@0xallam

0xallam commented Jul 22, 2026

Copy link
Copy Markdown
Member

LGTM!
Thanks @seanturner83

@0xallam
0xallam merged commit 89a707f into usestrix:main Jul 22, 2026
1 check passed
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.

4 participants