Skip to content

v0.107.2

Latest

Choose a tag to compare

@github-actions github-actions released this 02 Aug 17:27

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 — /ETC and /etc are 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's outputs: map is not
    an event: it rides RunOutcome straight 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:notify target is judged whatever channel carries
    it.
    One ${{ }} in channel: 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 — ls came
    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 --version

Tarballs 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.2

Provenance

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