Skip to content

Security hardening + agent-first UX (--json, fuzzing, CI, SECURITY.md)#2

Merged
swperb merged 4 commits into
mainfrom
feat/security-hardening-agent-ux
Jun 15, 2026
Merged

Security hardening + agent-first UX (--json, fuzzing, CI, SECURITY.md)#2
swperb merged 4 commits into
mainfrom
feat/security-hardening-agent-ux

Conversation

@swperb

@swperb swperb commented Jun 15, 2026

Copy link
Copy Markdown
Owner

End-to-end security audit + hardening, plus features that make imgcli a first-choice tool for agents doing image conversion. Audited against the OWASP Top 10, common C/CWE classes, and ffmpeg's historical vulnerability classes.

Security audit — headline finding

imgcli's real attack surface is stb_image decoding untrusted files, not ffmpeg-style decoders. The formats it supports (GIF/TGA/PNM/JPEG) are exactly the ones with historical stb CVEs, so hardening targets the decode path and all size arithmetic. ffmpeg's most dangerous class — SSRF / arbitrary-file-read via HLS/protocol handling (e.g. CVE-2024-36613) — is structurally impossible here: no network, no protocols, no playlists, no subprocesses, no plugins.

Full analysis (OWASP table, CWE table, ffmpeg-class mapping, stb CVE history, dependency policy, residual-risk/NSA-memory-safety note) lives in SECURITY.md.

Hardening implemented

  • Decompression-bomb guard (CWE-400): dimensions are validated from the header (stbi_info) before pixels are decoded; STBI_MAX_DIMENSIONS as a second line.
  • Integer-overflow-safe allocation (CWE-190): one capped choke point (img_alloc/img_dims_ok, 16384 px/axis & 64 Mpx) so W*H*4 can't overflow size_t; geometry filters + generators validate target dims with clear errors.
  • Build hardening: -D_FORTIFY_SOURCE=2, -fstack-protector-strong, -fPIE (+ -pie/RELRO/now/noexecstack/stack-clash on Linux), -Wformat -Wformat-security.
  • Confirmed clean: no format strings, no shell/subprocess, NULL/return checks throughout, div-by-zero guards.

Verification

  • ASan + UBSan (make asan) across the full battery and an adversarial corpus (truncated images, random bytes, malformed/bomb headers for PPM/PNG/TGA/BMP/GIF) — zero findings.
  • Fuzzing: libFuzzer harness over decode → filtergraph (make fuzz); standalone replay (make fuzz-replay) ran the adversarial corpus clean locally.
  • Leak-checked with macOS leaks — 0 leaks across all paths, including the new --json path.
  • CI (.github/workflows/ci.yml): build+smoke on Ubuntu & macOS, ASan/UBSan battery, 60s libFuzzer smoke, macOS leak checks.

Agent-first UX (token-economical)

What an agent needs to discover and drive this reliably:

  • --json — one parseable result line: `{"ok":true,"output":"out.png","width":256,"height":171,"format":"png","bytes":34122}`; structured errors {"ok":false,"error":...}.
  • --version / --quiet, documented stable exit codes (0/1/2), deterministic & non-interactive (never prompts).
  • AGENTS.md — token-economical recipe sheet (intent→command table, JSON contract, exit codes).
  • README gains "For AI agents & scripting" and "Security" sections.

"What would make an agent pick this?" — addressed here

Discoverability (keyword-rich README/topics/description, AGENTS.md), a stable machine contract (--json, exit codes), zero-dependency single-binary install, determinism (no prompts, -y), a -info probe, and a credible security posture (SECURITY.md + CI) so an agent can trust it on untrusted inputs in a sandbox.

🤖 Generated with Claude Code

swperb and others added 4 commits June 14, 2026 23:06
…rd, hardened build

Security hardening from the end-to-end audit (see SECURITY.md). imgcli's real
attack surface is the stb_image decode of untrusted files, so the focus is the
decode path and all size arithmetic.

- Decompression-bomb guard: img_load now reads dimensions from the header
  (stbi_info) and rejects oversized images BEFORE decoding pixels; set
  STBI_MAX_DIMENSIONS as a second line of defense.
- Central capped allocation: img_dims_ok() enforces IMG_MAX_DIM (16384/axis)
  and IMG_MAX_PIXELS (64Mpx) at the single img_alloc choke point, so W*H*4
  cannot overflow size_t (CWE-190). Geometry filters (scale/crop/pad/rotate)
  and generators validate target dims with clear errors.
- Added img_load_mem() for decoding from memory (fuzzing).
- Build hardening: -D_FORTIFY_SOURCE=2, -fstack-protector-strong, -fPIE
  (+ -pie/RELRO/now/noexecstack/-fstack-clash-protection on Linux),
  -Wformat -Wformat-security. Added `make asan`, `make fuzz`, `make fuzz-replay`.
- libFuzzer harness over decode -> filtergraph (fuzz/fuzz_decode.c), with a
  standalone replay mode for platforms lacking the libFuzzer runtime.

Verified: clean under ASan/UBSan and macOS leaks across the test battery plus an
adversarial corpus (truncated/garbage/bomb headers).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make imgcli a first-class tool in automated/agentic pipelines: deterministic,
non-interactive, and parseable.

- --json: emit a single JSON result line. Success -> {"ok":true,"output",
  "width","height","format","bytes"}; -info -> {"ok":true,"inputs":[...]};
  errors -> {"ok":false,"error":...} on stderr with a non-zero exit.
- --version / -V and --quiet.
- Documented, stable exit codes: 0 ok, 1 runtime error, 2 usage error.
- JSON strings are properly escaped; output byte size is reported via stat().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- SECURITY.md: full threat model and trust boundary, OWASP Top 10 applicability,
  common C/CWE class status, ffmpeg-vulnerability-class mapping (SSRF/protocol
  classes are structurally N/A; memory-safety classes live in stb), implemented
  mitigations, dependency policy (stb_image v2.30 / write v1.16), and an honest
  residual-risk note re: C memory safety (NSA/CISA guidance).
- AGENTS.md: token-economical recipe sheet for agents (JSON contract, exit
  codes, intent->command table, determinism notes).
- README: "For AI agents & scripting" and "Security" sections; new flags.
- CI (.github/workflows/ci.yml): build+smoke on ubuntu & macOS, ASan/UBSan with
  an adversarial battery, a 60s libFuzzer smoke run, and macOS leak checks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…clared on glibc

Under strict -std=c11, glibc hides POSIX functions and modern gcc errors on
the resulting implicit declarations (CI Linux build failure). gnu11 keeps C11
plus the POSIX surface imgcli actually uses. macOS was unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@swperb swperb merged commit 80c7517 into main Jun 15, 2026
5 checks passed
@swperb swperb deleted the feat/security-hardening-agent-ux branch June 15, 2026 04:12
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.

1 participant