sic swallows a bare --, breaking every nested command that needs one
#1
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
sicconsumes a bare--as its own argument separator instead of passing it through to the remote argv. Any nested tool that itself requires--(incus exec,docker exec,kubectl exec,env,git) is therefore unusable throughsicin its natural form.This matters more than it sounds, because the fleet's normal shape is jump-host → container runtime → container, and that middle hop is exactly where
--lives.Reproductions (both hit during one session, 2026-07-22)
1. Silently runs the wrong command on the wrong host
sicate the--, so everything after it became the command to run on boltzmann rather than being handed toincus exec. Note the failure mode: it does not error on the malformed invocation, it cheerfully runs something else.2. Same class, different victim — command runs outside the container
This looks like a permissions problem and is not:
systemctlran on dcw2, not insidepica, because the--vanished. Diagnosing this cost real time precisely because the error message points somewhere else entirely.Why the workaround is not acceptable
The fix in both cases was:
which reintroduces full shell quoting for every hop after the first — the exact problem
sicexists to eliminate. A 4-hop call (noether -> sic boltzmann -> incus exec memory -> docker exec stash -> /stash remember 'TEXT') ends up with one protected hop and three unprotected ones, and the payload quoted through all of them.Suggested fix
Either of:
--through to the remote argv. Ifsicneeds its own separator, require an explicit escape (sic host --sic-- cmd ...) rather than claiming the common one.sic boltzmann/memory <argv>orsic boltzmann/memory/stash-stash-1 <argv>, withsicconstructing each runtime invocation itself and re-encoding argv as netstrings at every hop. This makes the bug moot by construction, since nothing ever hands a shell a string to parse. Needs a per-host runtime resolver (incus on boltzmann/hertz/dcw2/pve1, docker nested inmemory,pcton data) or an explicit scheme likehost/pct:107.(2) is the more useful shape and is tracked as an idea in stash
/ideas; (1) is the minimal correctness fix.Related, separate bug
sicdoes not forward stdin, and does not say so.sic hertz tee /path <<EOF ... EOFproduced a zero-byte file with no error and no non-zero exit — content silently lost. Every heredoc/pipe use since has had to be replaced by scp-then-move. Worth its own issue; at minimum it should fail loudly rather than write an empty file.Fixed in v0.2.0 (v2 wire protocol). Argv is carried end-to-end as length-framed netstrings — no layer re-parses or interprets it — so a bare
--survives into the innermost argv untouched, and nestedincus exec … --/docker exec/pct exec …chains work in their natural form. Covered byTestBareDoubleDashPayloadIsOpaqueplus the argv-boundary tests, and smoke-verified live through a real 2-hop container chain. Released and rolled out fleet-wide.