CVE-2026-24137
Description
sigstore framework is a common go library shared across sigstore services and clients. In versions 1.10.3 and below, the legacy TUF client (pkg/tuf/client.go) supports caching target files to disk. It constructs a filesystem path by joining a cache base directory with a target name sourced from signed target metadata; however, it does not validate that the resulting path stays within the cache base directory. A malicious TUF repository can trigger arbitrary file overwriting, limited to the permissions that the calling process has. Note that this should only affect clients that are directly using the TUF client in sigstore/sigstore or are using an older version of Cosign. Public Sigstore deployment users are unaffected, as TUF metadata is validated by a quorum of trusted collaborators. This issue has been fixed in version 1.10.4. As a workaround, users can disable disk caching for the legacy client by setting SIGSTORE_NO_CACHE=true in the environment, migrate to https://github.com/sigstore/sigstore-go/tree/main/pkg/tuf, or upgrade to the latest sigstore/sigstore release.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/sigstore/sigstoreGo | < 1.10.4 | 1.10.4 |
Affected products
1Patches
18ec410a2993eEscape target name - GHSA-fcv2-xgw5-pqxf (#2265)
2 files changed · +96 −2
pkg/tuf/client.go+6 −2 modified@@ -671,12 +671,16 @@ type diskCache struct { memory *memoryCache } +func (d *diskCache) safePath(p string) string { + return filepath.FromSlash(filepath.Join(d.base, url.PathEscape(p))) +} + func (d *diskCache) Get(p string) ([]byte, error) { // Read from the in-memory cache first. if b, err := d.memory.Get(p); err == nil { return b, nil } - fp := filepath.FromSlash(filepath.Join(d.base, p)) + fp := d.safePath(p) return os.ReadFile(fp) } @@ -685,7 +689,7 @@ func (d *diskCache) Set(p string, b []byte) error { return err } - fp := filepath.FromSlash(filepath.Join(d.base, p)) + fp := d.safePath(p) if err := os.MkdirAll(filepath.Dir(fp), 0o700); err != nil { return fmt.Errorf("creating targets dir: %w", err) }
pkg/tuf/client_test.go+90 −0 modified@@ -894,3 +894,93 @@ func Test_remoteFromMirror(t *testing.T) { t.Fatalf("unexpected error with GCS mirror: %v", err) } } + +func TestDiskCache_safePath(t *testing.T) { + tests := []struct { + name string + base string + input string + expected string + }{ + { + name: "simple filename", + base: "/tmp/cache", + input: "target.json", + expected: filepath.Join("/tmp/cache", "target.json"), + }, + { + name: "path with subdirectory", + base: "/tmp/cache", + input: "subdir/target.json", + expected: filepath.Join("/tmp/cache", "subdir%2Ftarget.json"), + }, + { + name: "path traversal attempt with ..", + base: "/tmp/cache", + input: "../../../etc/passwd", + expected: filepath.Join("/tmp/cache", "..%2F..%2F..%2Fetc%2Fpasswd"), + }, + { + name: "path with leading slash", + base: "/tmp/cache", + input: "/etc/passwd", + expected: filepath.Join("/tmp/cache", "%2Fetc%2Fpasswd"), + }, + { + name: "path with special characters", + base: "/tmp/cache", + input: "file with spaces.json", + expected: filepath.Join("/tmp/cache", "file%20with%20spaces.json"), + }, + { + name: "path with URL-encoded characters", + base: "/tmp/cache", + input: "file%2Fname.json", + expected: filepath.Join("/tmp/cache", "file%252Fname.json"), + }, + { + name: "empty input", + base: "/tmp/cache", + input: "", + expected: filepath.Join("/tmp/cache", ""), + }, + { + name: "path with backslash", + base: "/tmp/cache", + input: "..\\..\\etc\\passwd", + expected: filepath.Join("/tmp/cache", "..%5C..%5Cetc%5Cpasswd"), + }, + { + name: "path with null byte", + base: "/tmp/cache", + input: "file\x00.json", + expected: filepath.Join("/tmp/cache", "file%00.json"), + }, + { + name: "deeply nested path traversal", + base: "/tmp/cache", + input: "a/../b/../c/../../../etc/passwd", + expected: filepath.Join("/tmp/cache", "a%2F..%2Fb%2F..%2Fc%2F..%2F..%2F..%2Fetc%2Fpasswd"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := &diskCache{ + base: tt.base, + memory: &memoryCache{}, + } + + result := d.safePath(tt.input) + if result != tt.expected { + t.Errorf("safePath(%q) = %q, want %q", tt.input, result, tt.expected) + } + + // Verify the result doesn't escape the base directory + // by checking if the result starts with the base path + if !strings.HasPrefix(result, tt.base) { + t.Errorf("safePath(%q) = %q escapes base directory %q", tt.input, result, tt.base) + } + }) + } +}
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-fcv2-xgw5-pqxfghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-24137ghsaADVISORY
- github.com/sigstore/sigstore/commit/8ec410a2993ea78083aecf0e473a85453039496envdWEB
- github.com/sigstore/sigstore/releases/tag/v1.10.4nvdWEB
- github.com/sigstore/sigstore/security/advisories/GHSA-fcv2-xgw5-pqxfnvdWEB
- pkg.go.dev/vuln/GO-2026-4358ghsaWEB
News mentions
0No linked articles in our index yet.