May 19, 2026·6 min read·4 visits
Turborepo CLI < 2.9.14 lacks state validation in its local authentication callback, enabling attackers to bind a developer's session to an attacker-controlled account via a drive-by request to localhost.
Vercel Turborepo CLI versions prior to 2.9.14 are vulnerable to Cross-Site Request Forgery (CSRF) and Session Fixation during self-hosted remote cache authentication. The local callback server fails to validate the OAuth2 state parameter, allowing malicious websites to inject attacker-controlled tokens and compromise build environments.
Vercel Turborepo utilizes a Command Line Interface (CLI) that supports authentication for accessing remote cache and authorization endpoints. When developers authenticate against self-hosted infrastructure, the CLI initiates a browser-based OAuth2-style flow. This process involves spawning a temporary HTTP server on the localhost loopback interface to receive the authorization token via a callback redirect.
Prior to version 2.9.14, the local callback server implemented this authentication flow without validating a Cross-Site Request Forgery (CSRF) state parameter. The state parameter is an essential component of OAuth2 protocols, designed to cryptographically bind a specific authorization request to the corresponding callback response. The absence of this validation mechanism creates a direct vulnerability (CWE-352) within the authentication lifecycle.
Due to this architectural oversight, the local callback server indiscriminately accepts incoming HTTP requests containing an authorization token. This flaw enables session fixation (CWE-384), where an external entity can force the local CLI to consume a pre-determined authentication context. The vulnerability strictly affects self-hosted remote cache or auth endpoint configurations, while Vercel-hosted login flows utilizing device authorization remain unaffected.
The root cause stems from an incomplete implementation of the OAuth2 protocol standard for native applications. The Turborepo CLI initiates the authentication sequence by directing the default web browser to the self-hosted identity provider. Concurrently, it binds a listening socket to 127.0.0.1 to await the redirection URI containing the session credentials.
Standard security practices dictate that the client application must generate a cryptographically secure, unguessable nonce value known as the state parameter before initiating the authorization request. This nonce is transmitted to the authorization server and subsequently returned to the local callback endpoint. The client application must then verify that the returned state matches the originally generated value.
In vulnerable versions of the Turborepo CLI, this generation and validation sequence is entirely absent. The local HTTP listener strictly parses the query parameters for an access_token or authorization code, completely ignoring the origin or the session context of the incoming request. Because web browsers automatically execute redirects and can issue background requests to loopback addresses, the listener processes any well-formed payload it receives on the designated local port.
The remediation strategy implemented in commit fb8c9aec0f9e83f95783659a5ce9c4478cf62cb9 addresses the core deficiency by enforcing strict state parameter validation. The patch modifies the initial authorization request builder to generate a cryptographically secure random string. This string is stored within the memory space of the CLI process initiating the request.
// Conceptual representation of the vulnerable handler
func handleCallback(w http.ResponseWriter, r *http.Request) {
token := r.URL.Query().Get("token")
if token == "" {
http.Error(w, "Missing token", http.StatusBadRequest)
return
}
// VULNERABILITY: Blindly accepting the token without state validation
saveTokenToConfig(token)
}The patched implementation introduces a mandatory validation step within the callback HTTP handler. When the listener receives a request, it extracts the state query parameter and performs a constant-time string comparison against the value stored in memory. If the values do not match, or if the parameter is entirely absent, the callback server rejects the request and aborts the authentication flow.
// Conceptual representation of the patched handler
func handleCallback(w http.ResponseWriter, r *http.Request, expectedState string) {
receivedState := r.URL.Query().Get("state")
if subtle.ConstantTimeCompare([]byte(receivedState), []byte(expectedState)) != 1 {
http.Error(w, "Invalid state parameter", http.StatusForbidden)
return
}
token := r.URL.Query().Get("token")
saveTokenToConfig(token)
}While the patch successfully mitigates the primary attack vector by introducing state validation, the robustness relies on the entropy of the underlying random number generator. If the pseudo-random number generator utilized for the nonce lacks sufficient entropy, an attacker could predict the value and bypass the validation check. Furthermore, strict validation of the Host and Origin headers provides defense-in-depth against advanced cross-origin exploitation.
Exploitation requires the attacker to position themselves between the developer's execution of the turbo login command and the subsequent authentication completion. The attacker must operate a malicious web server and entice the targeted developer to navigate to an attacker-controlled page during this specific timeframe. This sequence constitutes a Drive-by Compromise.
The malicious webpage contains JavaScript designed to issue a cross-origin background request to the loopback interface on the specific port utilized by the Turborepo CLI. This request payload contains an access token corresponding to an attacker-controlled account on the self-hosted remote cache infrastructure. Advanced implementations of this exploit script can iterate through common local port ranges to locate the active listener.
When the background request reaches the developer's local callback server, the vulnerable CLI processes the payload immediately. The CLI terminates the local listener and persists the attacker's token into the local configuration file. The developer's environment is now cryptographically bound to the attacker's session context without any visible indication of failure within the terminal interface.
Successful exploitation compromises the integrity and confidentiality of the developer's local build environment. By binding the Turborepo CLI to an attacker-controlled session, the attacker gains indirect control over the remote cache interactions. Subsequent executions of turbo build or turbo run utilize the injected credentials to communicate with external infrastructure.
The primary consequence is the unauthorized exfiltration of sensitive build artifacts. When the developer compiles proprietary source code, the CLI uploads the resulting cached outputs to the attacker's server instead of the legitimate corporate infrastructure. This data exposure often includes compiled binaries, intermediate assets, and embedded environment variables.
A secondary impact vector involves remote cache poisoning. The attacker can place malicious build artifacts into their controlled remote cache. If the developer's local build process relies on the cache, the CLI will download and execute these compromised artifacts during the compilation phase. This mechanism facilitates supply chain attacks by injecting malicious code directly into the developer's output binaries.
The vendor addressed the vulnerability in Turborepo version 2.9.14 by properly implementing state parameter validation within the OAuth callback server. System administrators and developers must upgrade the global Turborepo binary to version 2.9.14 or later to apply the fix. Package managers such as npm or yarn provide the standard update mechanism for this utility.
Merely upgrading the binary does not immediately secure environments that have already been compromised. Developers must manually flush any persistent authentication tokens by executing the turbo logout command. Following this step, developers should initiate a new session via turbo login to establish a clean authentication state bound to the patched local server implementation.
Security teams managing self-hosted authentication infrastructure should implement defense in depth measures. Identity providers handling the CLI authentication requests should strictly mandate the inclusion of the state parameter from clients. Enforcing Proof Key for Code Exchange (PKCE) for public clients provides an additional layer of cryptographic verification, significantly reducing the viability of authorization code interception.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Turborepo Vercel | < 2.9.14 | 2.9.14 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-352, CWE-384 |
| Attack Vector | Network (Loopback) |
| CVSS | 6.5 |
| EPSS | 0.00023 |
| Exploit Status | Proof of Concept |
| KEV Status | Not Listed |
The application does not adequately verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.