Feb 15, 2026·6 min read·37 visits
The go-tuf library attempts to parse untrusted JSON metadata using unsafe Go type assertions. A malicious mirror can send syntactically valid but structurally incorrect JSON (e.g., wrong types for known fields), causing the client to panic and crash immediately. This creates a Denial of Service condition that prevents updates, reachable without authentication or signing keys.
A critical Denial of Service vulnerability exists in the go-tuf library, the Go implementation of The Update Framework (TUF). By serving malformed metadata JSON, a compromised repository mirror or Man-in-the-Middle attacker can trigger a runtime panic in the client. Crucially, this crash occurs during the parsing phase, before cryptographic signatures are verified, allowing an attacker without signing keys to disable the update mechanism across a fleet of devices.
The Update Framework (TUF) is the gold standard for software supply chain security. It’s the architectural equivalent of a paranoid bank vault, designed specifically to operate in hostile environments where repository mirrors might be compromised. The entire premise of TUF is: "We don't trust the mirror to tell the truth; we only trust the cryptographic signatures on the metadata."
But here is the irony: before you can verify a signature, you have to read the message. And if reading the message causes your brain to explode, the signature doesn't matter. That is exactly what happened in go-tuf.
This vulnerability isn't about stealing keys or injecting malware directly; it's about the fragility of code handling untrusted input. By exploiting a quirk in how Go handles interfaces, an attacker can turn a standard update check into a SIGABRT, effectively freezing a fleet of clients in time, unable to ever update again.
To understand this bug, you need to understand Go's type system. Go is statically typed, but it offers the interface{} (or any) type to handle dynamic data like JSON. When you unmarshal an arbitrary JSON blob, you often get a map[string]any.
To use that data, you have to tell Go what it is. "Trust me, compiler, this field is a string." In code, that looks like value.(string).
Here is the catch: If you are wrong—if value is actually an integer, or nil—Go doesn't just return an error. It panics. It flips the table and terminates the process immediately. This is a "Reachable Assertion" (CWE-617).
The developers of go-tuf wrote code that assumed the JSON structure coming from a mirror would always match the TUF specification. They chained these assertions together, walking through the JSON tree without a safety net. If a mirror sends a JSON object where the signed field is a string instead of a map, the application crashes hard.
The vulnerability lived in metadata/metadata.go. The code was trying to determine the type of metadata (e.g., root, targets, snapshot) by inspecting the _type field inside the signed object.
Here is the smoking gun from the vulnerable version:
// The Vulnerable Code
// Assumption 1: "signed" exists and is a map
// Assumption 2: "_type" exists inside that map and is a string
signedType := m["signed"].(map[string]any)["_type"].(string)Do you see the problem? It is a one-liner of doom. If m["signed"] is present but is actually a float, the first .(...) panics. If it is a map but _type is missing or is a boolean, the second .(...) panics.
The fix, introduced in version 2.3.1, replaces this cowboy coding with the "comma-ok" idiom, which is the standard, safe way to do type assertions in Go:
// The Fixed Code
// Check if "signed" is a map
signed, ok := m["signed"].(map[string]any)
if !ok {
return &ErrValue{Msg: "metadata 'signed' field is missing or not an object"}
}
// Check if "_type" is a string
signedType, ok := signed["_type"].(string)
if !ok {
return &ErrValue{Msg: "no _type found in signed"}
}This change transforms a fatal crash into a manageable error that the client can log and ignore, allowing it to perhaps try a different mirror.
Exploiting this does not require a genius-level IQ or supercomputers. It requires a text editor. The attack vector is the repository mirror. In a real-world scenario, an attacker might compromise a CDN node, a mirror server, or perform a Man-in-the-Middle attack on HTTP traffic (since TUF is designed to work over plain HTTP, relying on signatures for security).
Here is the attack flow:
root.json from the mirror.{
"signatures": [],
"signed": "This should be a map, but I made it a string just to watch you cry."
}go-tuf client receives this. It successfully unmarshals the JSON (because it is valid JSON). It then attempts m["signed"].(map[string]any). Since "signed" is a string, the runtime panics.Because this check happens before signature verification, the attacker does not need any private keys. They just need to be in the network path.
At first glance, a crash sounds annoying but not catastrophic. In the context of an update framework, however, Availability is a security property.
If an attacker can crash the update client at will, they can enforce a "version freeze." Imagine a fleet of IoT devices with a known critical vulnerability (say, a remote root exploit). The vendor releases a patch. The devices wake up to download the patch.
The attacker, controlling a mirror or a specific network segment, serves the malformed JSON. Every single device crashes the moment it tries to update. The devices remain vulnerable to the root exploit indefinitely.
Furthermore, because the panic crashes the entire Go process, if the TUF client is integrated directly into a larger application (e.g., a microservice or a daemon) rather than running as a separate CLI tool, the entire application goes down. This escalates the issue from "cannot update" to "service outage."
The remediation is straightforward: update the library. The patch was released in version 2.3.1.
To patch your Go project:
go get github.com/theupdateframework/go-tuf@v2.3.1
go mod tidyIf you are a developer using go-tuf, you should also audit your own code for similar patterns. Search your codebase for direct type assertions (.(type)) on data that originates from external sources (user input, files, network requests). Always use the two-value assignment form (val, ok := x.(T)) to handle unexpected types gracefully without crashing the runtime.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
go-tuf theupdateframework | >= 2.0.0, < 2.3.1 | 2.3.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-617 (Reachable Assertion) |
| Attack Vector | Network (Mirror/MitM) |
| CVSS v3.1 | 5.9 (Medium) |
| EPSS Score | 0.02% |
| Impact | Denial of Service (DoS) |
| Exploit Status | Trivial (PoC available in unit tests) |
| Language | Go |
Reachable Assertion
An uncontrolled resource consumption vulnerability exists in the Scala-based http4s-blaze-server package of the http4s/blaze library. The vulnerability allows remote, unauthenticated attackers to cause an Out of Memory Error (OOM) and JVM crash by streaming a continuous sequence of small or empty WebSocket continuation frames with the FIN bit set to 0. This bypasses typical payload size checks because of the JVM's per-object allocation overhead, leading to rapid heap exhaustion with minimal network bandwidth.
A critical path traversal vulnerability has been identified in the OpenList Go-based backend package. The vulnerability exists within the batch rename handler because the application does not validate the source filename parameter before constructing filesystems paths. This omission allows authenticated users to escape their designated directory and rename files in sibling paths.
OpenList version 4.2.3 and prior is vulnerable to an authorization bypass and metadata leakage. When configured with the Bleve search engine backend, OpenList fails to perform separator-aware path matching when validating tenant containment. This allows authenticated users to access sibling directories sharing similar name prefixes. Furthermore, the search backend returns unfiltered global result counts, leaking existence verification data of unauthorized files via side-channel analysis.
An authorization bypass vulnerability in OpenList version 4.2.3 and below allows authenticated users to read arbitrary files outside of their designated base directories due to an insecure path prefix check using Go's standard strings.HasPrefix function.
A security policy bypass vulnerability exists in the AWS API MCP Server (awslabs-aws-api-mcp-server) from version 0.2.13 through 1.3.46. When the server fails to load the read-only operations index during startup (due to transient network failures, file permission issues, or other exceptions), it logs a warning but continues running in an insecure, degraded state. Under this condition, the security policy engine fails open, silently skipping all subsequent security checks and consent prompts for the lifetime of the process. This permits unauthorized mutating AWS CLI commands to execute via indirect prompt injection attacks.
An incomplete escaping vulnerability in the npm package 'shescape' allows unauthenticated users to trigger dynamic shell expansions, absolute path disclosure, and command block break-outs on Unix and Windows systems.