Sep 15, 2026·6 min read·12 visits
Multiple critical and high severity flaws in @zereight/mcp-gitlab allow unauthenticated remote access, GraphQL authorization bypasses, and denial of service.
A constellation of five distinct security flaws (F1 through F5) in `@zereight/mcp-gitlab` prior to version 2.1.30 allows unauthenticated remote access, read-only policy bypasses, DNS rebinding, and denial-of-service via session exhaustion.
The @zereight/mcp-gitlab package implements a Model Context Protocol (MCP) server designed to interface large language model (LLM) agents with GitLab APIs. By presenting GitLab functionalities as semantic tools, the server allows automated agents to perform actions such as retrieving pipeline traces, writing issues, or querying data through GraphQL.
In versions prior to 2.1.30, a collection of five distinct security flaws (designated F1 through F5) compromises the authorization, confidentiality, and availability guarantees of this integration. The most severe defects allow malicious actors, or prompt-injected LLM agents, to execute unauthorized write operations and bypass the configured read-only mode boundaries.
These vulnerabilities expand the attack surface from typical local execution to remote unauthorized action orchestration, session exhaustion, and DNS rebinding. The combination of local state maintenance and weak validation in HTTP and Server-Sent Events (SSE) channels exposes the system to active exploitation under specific configurations.
The F1 vulnerability is an authorization bypass located within utils/graphql-query.ts and the "execute_graphql" tool handler in index.ts. The implementation attempts to enforce a read-only environment, controlled by the GITLAB_READ_ONLY_MODE configuration parameter, by parsing and rejecting any incoming query containing write operations.
The normalization function stripGraphQLCommentsAndStrings strips comments and strings from the GraphQL query but fails to sanitize commas. Under the GraphQL syntax specification, commas are treated as insignificant whitespace and are skipped by parsers. The server attempts to detect mutations or subscriptions using the regular expression /(?:^|[};]\s*)(mutation|subscription)\b/ on the normalized query.
By prefixing a write operation with a leading comma (for example, ,mutation { ... }), an attacker or compromised agent prevents the regex from matching because the write keyword is preceded by a comma rather than the start of the string (^), a closing brace (}), or a semicolon (;). The downstream GitLab GraphQL engine ignores the comma and executes the mutation, bypassing safety controls. Furthermore, the "execute_graphql" tool handler fails to call rejectIfProjectScopedDeployment(), allowing operations on projects outside the defined GITLAB_ALLOWED_PROJECT_IDS allow-list.
The second flaw, F2, is a deployment-conditional authentication bypass on the /mcp HTTP endpoint. When STREAMABLE_HTTP=true is set, the server only checks if MCP-layer authentication is configured when initialized with a Personal Access Token or job token. If initialized via OAuth device flow or cookie-jar pathways, the validation is skipped, exposing /mcp to unauthenticated network actors while signing requests with the server's valid credentials.
Flaw F3 involves the Server-Sent Events (SSE) transport protocol, which lacks origin and host-header verification by default and does not enforce authentication unless SSE_AUTH_TOKEN is defined. Because the server typically binds to loopback interfaces, this omission permits DNS rebinding attacks. An external site visited by the host user can issue commands to the local SSE endpoint, invoking GitLab actions on behalf of the user.
Flaw F4 represents an unauthenticated transport-exhaustion denial-of-service vulnerability. The validateToken function only performs structural checks on tokens without validating their active status upstream. An unauthenticated attacker can flood /mcp with structurally correct garbage tokens, allocating sessions up to the MAX_SESSIONS limit (1000). These dead sessions persist for one hour by default, denying access to legitimate requests.
The remediation implemented in version 2.1.30 addresses these issues across multiple files. In utils/graphql-query.ts, the regular expression was modified to match leading commas only when they occur at the very beginning of the document string, ensuring that nested fields containing matching substrings are not falsely classified as write operations while blocking prefix evasion.
// Before patch:
// return /(?:^|[};]\s*)(mutation|subscription)\b/.test(normalized);
// After patch in v2.1.30:
return /(?:^(?:,\s*)?|[};]\s*)(mutation|subscription)\b/.test(normalized);To remediate the DNS rebinding vulnerability (F3) on the SSE server, a validation middleware was introduced. This middleware checks both the Host and Origin headers against a strictly defined allow-list containing localhost and configured server URLs. If the headers do not match, the connection is closed immediately with a 403 Forbidden status.
const allowedHosts = new Set([
`${HOST}:${PORT}`,
`127.0.0.1:${PORT}`,
`localhost:${PORT}`,
`[::1]:${PORT}`,
]);
// Middleware rejects requests whose headers do not match allowedHostsFor the session exhaustion vulnerability (F4), the patch replaces local syntactical token validation with active upstream verification. The server now attempts to authenticate against the GitLab /api/v4/user endpoint before allocating an internal session object, neutralizing unauthenticated DoS attempts.
Exploitation of these vulnerabilities varies based on transport configuration. For the F1 GraphQL bypass, an attacker uses a prompt injection to force the LLM agent to submit a comma-prefixed GraphQL payload. Since the agent's safety middleware evaluates this as read-only, the request is permitted, and the backend processes the write command.
// Local validation bypass replication
const vulnerableRegex = /(?:^|[};]\s*)(mutation|subscription)\b/;
const bypassPayload = ",mutation { deleteProject(input: { id: 1 }) { errors } }";
const isBlocked = vulnerableRegex.test(bypassPayload);
console.log("Is Blocked:", isBlocked); // Outputs: falseIn the case of F4, session exhaustion is accomplished by issuing rapid HTTP requests containing synthetic tokens to the /mcp initialization endpoint. Because the server fails to check these tokens upstream prior to session creation, it populates the active session pool to its maximum capacity, causing service denial.
# Denial of service payload execution
for i in $(seq 1 1000); do
curl -s -o /dev/null http://127.0.0.1:3002/mcp \
-H 'Content-Type: application/json' \
-H 'Private-Token: dummytokenstringlongenough' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05"}}'
doneThe collective impact of these vulnerabilities is classified as High, with an advisory CVSS v3.1 score of 8.1. The vector string is CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N. This score reflects the ability of network-adjacent or remote actors to perform high-privilege actions on the GitLab platform.
The integration of LLM agents introduces a unique attack vector through flaw F5. Because the server returns raw pipeline log traces to the LLM agent, an attacker can embed malicious instructions inside public CI job traces. When the agent processes these traces, it interprets the embedded commands as system-level instructions, potentially leveraging F1 to delete repositories or exfiltrate private code.
Furthermore, the lack of host and origin header validation on the SSE server exposes internal developer environments to cross-site request forgery and DNS rebinding attacks. This allows untrusted web origins to issue arbitrary MCP commands, effectively bridging the host's browser context with the local MCP-GitLab service instance.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
@zereight/mcp-gitlab @zereight | < 2.1.30 | 2.1.30 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-863 |
| Attack Vector | Network |
| CVSS | 8.1 |
| Impact | Authorization Bypass & Denial of Service |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
The software performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.
An OS command injection and argument injection vulnerability exists in the Nx build system and its official Maven (@nx/maven) and Gradle (@nx/gradle) plugins. When constructing the project dependency graph, the plugins invoke underlying Java build wrappers via subshells while interpolating unsanitized plugin options directly from nx.json into the command arguments. An attacker who supplies a crafted nx.json file via a repository or pull request can execute arbitrary shell commands with the privileges of the user or CI/CD runner invoking any Nx command that computes the project graph.
AdminNeo versions 4.6.2 through 5.4.1 contain an improper input validation vulnerability (CWE-20) in the version verification endpoint (?script=version). An unauthenticated remote attacker can submit a crafted HTTP POST request containing an array parameter for 'version', which is serialized into a shared server-side temporary cache file without type checking or authentication. On subsequent requests, the application deserializes this cached array and passes it to cookie() and rawurlencode(), triggering an uncaught fatal TypeError in PHP 8.x environments and causing a persistent 24-hour Denial of Service (DoS) for all users.
Charmbracelet Soft Serve versions 0.5.0 through 0.12.2 contain an authorization bypass and observable response discrepancy vulnerability in the HTTP Git server. Remote attackers can determine the existence, exact names, and clone URLs of private repositories by issuing crafted Go remote import path discovery requests (?go-get=1) or by observing differential HTTP status codes across Git Smart HTTP, Dumb HTTP, and Git LFS endpoints.
A Time-of-Check to Time-of-Use (TOCTOU) race condition in Charmbracelet Soft Serve versions 0.6.0 through 0.12.2 allows unauthenticated users to retain read access to repositories after their visibility is changed from public to private. The flaw stems from premature cache invalidation in the SetPrivate method prior to committing the visibility update to the underlying database. Concurrent read requests arriving during the database transaction window repopulate the non-expiring in-memory LRU cache with stale public metadata, bypassing access controls across HTTP, SSH, and Git protocols until the server process is restarted.
A resource consumption vulnerability exists in the Authlib joserfc Python library prior to version 1.7.5. When processing JSON Web Encryption (JWE) objects formatted using General JSON Serialization, the library fails to enforce an upper bound on the number of elements within the recipients array. An unauthenticated attacker can supply a JWE containing thousands of recipient entries targeting an RSA key management algorithm, forcing the server to perform an equivalent number of computationally expensive RSA private-key decryption operations per request and exhausting available CPU resources.
CVE-2026-119284 (GHSA-9pg5-8c7x-5hfx) is an uncontrolled resource consumption vulnerability (CWE-400) in the compact serialization deserialization routines of joserfc prior to version 1.7.6. The library enforced individual segment size limits only after invoking Python's bytes.split(b'.') on untrusted input tokens. By sending oversized compact JWS or JWE tokens, an unauthenticated remote attacker can force the Python interpreter to duplicate and allocate memory for every segment before size validation executes, resulting in memory exhaustion and an Endpoint Denial of Service (DoS).