What
The adversarial pass. 0.107.1 shipped six boundaries; an adversary
was then set on that work with one instruction, break it. Three of six
claims fell, and hunting what the first repairs still let through found
four more. Every fix below was proven on the binary before it was
touched, and each carries the mutation that makes it fail.
Two doors that were open
- A permit could name a system root by shouting it.
/root/./x*
closed in the morning;/ROOT/x*did not. macOS ships a
case-insensitive filesystem —/ETCand/etcare the same inode —
so an exact-match root check let a permit name any system root on the
very platform the seatbelt backend serves. The comparison folds case
now, and refusing these on Linux can only ever refuse a path that does
not exist. - A secret was redacted in the trace and printed on stdout. The
redacting sink wraps the event lane, and a run'soutputs:map is not
an event: it ridesRunOutcomestraight to--output json, where the
CLI serialized it verbatim. The static check refuses a declared
egress, but not the side channel this backstop exists for — an exec
catting a file-sourced secret, a tool echoing its input. The map is
scrubbed now, at any depth.
Three guards that judged less than they claimed
- The dangerous-environment floor is proven entry by entry. Forty
names, eleven asserted; deleting the one that makes macOS load
arbitrary code into every dynamically linked child left the suite
green. Both halves are pinned now — every listed name enforced, and a
floor naming what may never leave. - A tainted
nika:notifytarget is judged whatever channel carries
it. One${{ }}inchannel:made the tool unclassifiable and the
re-gate silent: the same payload passed with rc=0 templated and rc=2
spelled out. - The run guard speaks only about runs. An unreadable payload from a
host we do not parse denied every command in the session —lscame
back « nika run blocked ». It now degrades only for bytes that could
have carried a run.
One law, two implementations
- The MCP spawn composes the child environment through the same
function as the exec runner, which its own module doc had promised
all along. The copy was equivalent, and equivalence was exactly what
nothing guaranteed.
Install
brew install supernovae-st/tap/nika # macOS · Linux
curl -LsSf https://nika.sh/install.sh | sh # script install
docker run --rm ghcr.io/supernovae-st/nika:0.107.2 --versionTarballs below: macOS arm64 / x64 · Linux x64 / arm64, plus SHA256SUMS.
Verify: three independent proofs
# 1 · checksum: the bytes you hold are the bytes CI hashed
sha256sum -c SHA256SUMS --ignore-missing # macOS: shasum -a 256 -c
# 2 · attestation: GitHub-signed build provenance for this exact artifact
gh attestation verify nika-<platform>-0.107.2.tar.gz --repo supernovae-st/nika
# 3 · SLSA provenance: the intoto asset, verifiable offline
slsa-verifier verify-artifact nika-<platform>-0.107.2.tar.gz \
--provenance-path multiple.intoto.jsonl \
--source-uri github.com/supernovae-st/nika --source-tag v0.107.2Provenance
Built from tag v0.107.2 by release.yml
on GitHub-hosted runners. Provenance is published twice: GitHub's native
build attestation (proof 2) and the SLSA generator's multiple.intoto.jsonl
release asset (proof 3). The release itself is a claim on the
machine-verified timeline: https://nika.sh/timeline
Full Changelog: v0.107.1...v0.107.2