go-tuf improperly validates the configured threshold for delegations
Description
go-tuf is a Go implementation of The Update Framework (TUF). Starting in version 2.0.0 and prior to version 2.3.1, a compromised or misconfigured TUF repository can have the configured value of signature thresholds set to 0, which effectively disables signature verification. This can lead to unauthorized modification to TUF metadata files is possible at rest, or during transit as no integrity checks are made. Version 2.3.1 fixes the issue. As a workaround, always make sure that the TUF metadata roles are configured with a threshold of at least 1.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/theupdateframework/go-tuf/v2Go | < 2.3.1 | 2.3.1 |
Affected products
1- Range: v2.0.0, v2.0.1, v2.0.2, …
Patches
1b38d91fdbc69Verify threshold is valid (#712)
2 files changed · +48 −9
metadata/metadata.go+7 −0 modified@@ -292,6 +292,13 @@ func (meta *Metadata[T]) VerifyDelegate(delegatedRole string, delegatedMetadata if len(roleKeyIDs) == 0 { return &ErrValue{Msg: fmt.Sprintf("no delegation found for %s", delegatedRole)} } + + if roleThreshold < 1 { + return &ErrValue{Msg: fmt.Sprintf("insufficient threshold (%d) configured for %s", + roleThreshold, + delegatedRole)} + } + // loop through each role keyID for _, keyID := range roleKeyIDs { key, ok := keys[keyID]
metadata/metadata_test.go+41 −9 modified@@ -592,26 +592,24 @@ func TestVerifyDelegate(t *testing.T) { }, Roles: []DelegatedRole{ { - Name: "test", - KeyIDs: []string{delegateeKey.ID()}, + Name: "test", + KeyIDs: []string{delegateeKey.ID()}, + Threshold: 1, }, }, } + targets.Signed.Delegations = delegations - err = targets.VerifyDelegate("test", root) - assert.NoError(t, err) + err = targets.VerifyDelegate("root", targets) + assert.Errorf(t, err, "Verifying test failed, not enough signatures, got %d, want %d", 0, 1) err = targets.VerifyDelegate("test", targets) - assert.NoError(t, err) + assert.Errorf(t, err, "Verifying test failed, not enough signatures, got %d, want %d", 0, 1) err = targets.VerifyDelegate("non-existing", root) assert.EqualError(t, err, "value error: no delegation found for non-existing") err = targets.VerifyDelegate("non-existing", targets) assert.EqualError(t, err, "value error: no delegation found for non-existing") - targets.Signed.Delegations.Roles[0].Threshold = 1 - err = targets.VerifyDelegate("test", targets) - assert.Errorf(t, err, "Verifying test failed, not enough signatures, got %d, want %d", 0, 1) - delegations.Keys["incorrectkey"] = delegations.Keys[delegateeKey.ID()] delete(delegations.Keys, delegateeKey.ID()) err = targets.VerifyDelegate("test", root) @@ -626,6 +624,40 @@ func TestVerifyDelegate(t *testing.T) { assert.EqualError(t, err, "type error: call is valid only on delegator metadata (should be either root or targets)") } +func TestVerifyDelegateThreshold(t *testing.T) { + root := Root(fixedExpire) + err := root.VerifyDelegate("test", root) + assert.EqualError(t, err, "value error: no delegation found for test") + + targets := Targets(fixedExpire) + err = targets.VerifyDelegate("test", targets) + assert.EqualError(t, err, "value error: no delegations found") + + key, _, err := ed25519.GenerateKey(nil) + assert.NoError(t, err) + + delegateeKey, _ := KeyFromPublicKey(key) + delegations := &Delegations{ + Keys: map[string]*Key{ + delegateeKey.ID(): delegateeKey, + }, + Roles: []DelegatedRole{ + { + Name: "test", + KeyIDs: []string{delegateeKey.ID()}, + Threshold: 0, + }, + }, + } + targets.Signed.Delegations = delegations + err = targets.VerifyDelegate("test", root) + assert.ErrorIs(t, err, &ErrValue{}) + assert.EqualError(t, err, "value error: insufficient threshold (0) configured for test") + err = targets.VerifyDelegate("test", targets) + assert.ErrorIs(t, err, &ErrValue{}) + assert.EqualError(t, err, "value error: insufficient threshold (0) configured for test") +} + func TestVerifyLengthHashesTargetFiles(t *testing.T) { targetFiles := TargetFile() targetFiles.Hashes = map[string]HexBytes{}
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
5- github.com/advisories/GHSA-fphv-w9fq-2525ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-23992ghsaADVISORY
- github.com/theupdateframework/go-tuf/commit/b38d91fdbc69dfe31fe9230d97dafe527ea854a0ghsax_refsource_MISCWEB
- github.com/theupdateframework/go-tuf/releases/tag/v2.3.1ghsaWEB
- github.com/theupdateframework/go-tuf/security/advisories/GHSA-fphv-w9fq-2525ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.