smash moves untrusted bytes between systems. Its security posture is built on one idea: the payload is inert data, never instructions, and never touches the terminal.
- No terminal bleed. Source content is never written to stdout/stderr. Every dynamic string smash prints (filenames, API error bodies) is stripped of control/escape bytes first, so a hostile filename or provider response cannot inject ANSI/OSC sequences (title rewrites, OSC-52 clipboard writes, cursor games) into your terminal. Verified: zero ESC bytes across captured stdout+stderr while encoding files whose names and contents contain OSC/CSI sequences.
- Inert, non-executable artifacts. smash contains no
eval; it never sources or executes payload bytes. Artifacts and restored files are written mode0600. The manifest states this contract inside every artifact. - Pure-ASCII artifacts. Whatever the source bytes were — binary, encrypted, escape-laden — the artifact is printable ASCII (manifest + base64), safe for cat, clipboards, chat, and LLM/agent file readers.
- Traversal-safe restore.
.dtarextraction refuses archive members with absolute paths or..components beforetar xruns. - Typo guard over silent fallback. A mostly-real file list with one missing name dies naming it; nothing is silently re-interpreted as prose.
- Secret hygiene (
--ai-api). API keys are passed tocurlvia-K tmpfileand request bodies via-d @tmpfile— never on the command line whereps(1)would expose them. Temp files (payload, curl auth config, stdin copies) live in$TMPDIR(mode-700 per-user on macOS) and are removed on exit and on interrupt (trap on EXIT/INT/HUP/TERM). Verified: after SIGINT mid-request, zero smash temp files and no key residue remain. - Provider error bodies are surfaced to the user but pass through the same
control-byte sanitizer; the API key is never echoed. Verified: a sentinel
key never appears in any stdout, artifact, or temp file across the full
--ai-apimatrix.
- argv-only execution. The server calls the
smashbinary with an argument vector; there is no shell, no string interpolation, nosh -c. The model cannot inject flags or commands. - Typed tool surface. The model can encode/decode/verify/inspect — it cannot pass arbitrary smash flags or choose arbitrary behavior.
- Path containment. In HTTP mode (and stdio with
-roots), every input path and output dir is resolved throughfilepath.EvalSymlinksand must land inside an approved root;..traversal and symlink escapes are rejected. - No content dumps. Results are paths, sizes, hashes, and manifests — never raw file bytes. Verified: payload text never appears in the protocol channel.
- No payload logging. Request bodies are consumed but never logged; the only log stream is startup/status on stderr.
- Sanitized structured errors. Error strings are stripped of control bytes and length-capped, so a hostile filename cannot escape through an error message.
- HTTP hardening. Loopback-only by default (non-loopback refused unless
-allow-remoteand TLS); bearer auth required; constant-time token comparison over SHA-256 digests (length not leaked); CORS disabled; request-size cap; concurrency cap; fixed-window rate limit; per-op timeouts;GET→405. Verified: 401 on missing/wrong token, 405 on GET, no CORS header, non-loopback bind refused, supplied token not logged.
- All local. Encode/decode run entirely in the browser via native
CompressionStream/DecompressionStream. Nothing is uploaded. There is no provider key in client code; provider-backed semantic compression is left to the CLI or a user-run local helper. - SRI + strict CSP. The production
dist/index.htmlloadsapp.min.jsvia Subresource Integrity (sha384-…) under a strict CSP (default-src 'none';script-src 'self'; no inline script; noeval). Verified: a one-byte change toapp.min.jscauses the browser to refuse the script (page renders empty); the unmodified bundle runs. - Service worker fails closed. At install the SW fetches every cached asset, computes SHA-256, and compares to a pinned map; any mismatch throws so the SW never activates — offline use can't be silently poisoned. The pinned map is verified to equal the real asset hashes. Runtime activation must be confirmed in a GUI browser; headless Chrome in the build environment rejects all SW registration.
- No
innerHTMLwith untrusted data. All untrusted values (filenames, content, manifests) are inserted viatextContent;innerHTMLis used only with static/numeric strings. - Minification is not a security boundary.
app.min.jsis compacted for size; the integrity boundary is SRI + the SW hash check, not obscurity. The readable source (smash-web.app.js) is kept out ofdist/.
- Secrets in Keychain only. API keys are stored as Keychain generic
passwords (
kSecAttrAccessibleWhenUnlockedThisDeviceOnly), never inUserDefaults/plist, logs, argv, or shell history. - argv execution. The app runs
smashwith an argument array; no shell. - User-level helper only. The optional MCP "network layer" installs a user-level binary and registers it with the AI client; no system daemon, no platform-server change.
- Code signing. The bundle is signed (ad-hoc in the build environment; Developer ID where the cert is present). Notarization is not performed here — see BUILD.md for the honest status. A self-hash alone is not treated as anti-tamper; OS code signing is the boundary.
- It is not encryption.
--ai-apisends content to whatever provider you configure; that is an explicit, user-chosen network action. - Minification/obfuscation are not security.
- A generated file existing is not proof it works; see the evidence report.