Skip to content
Heist/ feed
Back to feed
CriticalFix available

Mercury: Unauthenticated Remote Code Execution in /api/terminals - access control bypass

Type: advisorySources: OSV

### Summary A missing comma in `mercury_app/block_handler.py`'s `BLOCKED_PATTERNS` list causes the blocklist to fail to cover the `/api/terminals` endpoint. As a result, any unauthenticated attacker can spawn a full, interactive OS-level shell on the host running Mercury. ### Details The blocklist in `mercury_app/block_handler.py` is defined as: ``` BLOCKED_PATTERNS = [ # JupyterLab UI + API r"/lab(?!/(?:extensions/|api/settings(?:/|$)|api/translations(?:/|$)))(?:/.*)?" r"/api/terminals(?:/.*)?", r"/api/shutdown(?:/.*)?", r"/files(?:/.*)?", ] ``` The missing comma between the first and the second line causes Python to merge the rules, therefore the actual list that is blocked looks like this: /lab...(?:/.`*`)?/api/terminals(?:/.`*`)? (the first two lines joined together) /api/shutdown(?:/.`*`)? /files(?:/.`*`)? A direct request to `/api/terminals` does not match any of the 3 blocklist rules and is therefore **not blocked**, reaching Jupyter Server's built-in terminal API, which spawns a real OS shell accessible over a WebSocket connection. No authentication, account, session or pre-existing access to Mercury is required. ### PoC Testing was performed on a local Docker deployment (https://runmercury.com/deploy/dockerfile/), Mercury version 3.2.3, default configuration. **Step 1 - spawn a terminal:** ``` curl -i -X POST http://localhost:8888/api/terminals ``` Response: `{"name": "1", "last_activity": "..."}` **Step 2 - connect to the terminal and execute commands:** ``` python3

Products
mercury
Affected versions
3.0.0, 3.0.1, 3.0.2, 3.1.0, 3.1.1, 3.1.2, 3.1.4, 3.1.5, 3.2.0, 3.2.1, 3.2.2, 3.2.3
Fixed versions
3.2.4
CVEs
CVE-2026-111057
Confirmed fixed CVEs
Not specified
Updated in this feed
31 Oct 2026, 08:21 UTC · Revision 1

Source evidence

Mercury: Unauthenticated Remote Code Execution in /api/terminals - access control bypass

core-osv · Original source

Published 19 Oct 2026, 17:00 UTC

### Summary A missing comma in `mercury_app/block_handler.py`'s `BLOCKED_PATTERNS` list causes the blocklist to fail to cover the `/api/terminals` endpoint. As a result, any unauthenticated attacker can spawn a full, interactive OS-level shell on the host running Mercury. ### Details The blocklist in `mercury_app/block_handler.py` is defined as: ``` BLOCKED_PATTERNS = [ # JupyterLab UI + API r"/lab(?!/(?:extensions/|api/settings(?:/|$)|api/translations(?:/|$)))(?:/.*)?" r"/api/terminals(?:/.*)?", r"/api/shutdown(?:/.*)?", r"/files(?:/.*)?", ] ``` The missing comma between the first and the second line causes Python to merge the rules, therefore the actual list that is blocked looks like this: /lab...(?:/.`*`)?/api/terminals(?:/.`*`)? (the first two lines joined together) /api/shutdown(?:/.`*`)? /files(?:/.`*`)? A direct request to `/api/terminals` does not match any of the 3 blocklist rules and is therefore **not blocked**, reaching Jupyter Server's built-in terminal API, which spawns a real OS shell accessible over a WebSocket connection. No authentication, account, session or pre-existing access to Mercury is required. ### PoC Testing was performed on a local Docker deployment (https://runmercury.com/deploy/dockerfile/), Mercury version 3.2.3, default configuration. **Step 1 - spawn a terminal:** ``` curl -i -X POST http://localhost:8888/api/terminals ``` Response: `{"name": "1", "last_activity": "..."}` **Step 2 - connect to the terminal and execute commands:** ``` python3

osv:GHSA-f8c7-rwf9-fmgm

mercury

Fixed versions: 3.2.4

Complete source record
{
  "id": "0d489217c5256704b8125623737ef391b2e65dae92494613388f3641519433ae",
  "url": "https://osv.dev/vulnerability/GHSA-f8c7-rwf9-fmgm",
  "method": "structured",
  "payload": {
    "kind": "advisory",
    "title": "Mercury: Unauthenticated Remote Code Execution in /api/terminals - access control bypass",
    "summary": "### Summary A missing comma in `mercury_app/block_handler.py`'s `BLOCKED_PATTERNS` list causes the blocklist to fail to cover the `/api/terminals` endpoint. As a result, any unauthenticated attacker can spawn a full, interactive OS-level shell on the host running Mercury. ### Details The blocklist in `mercury_app/block_handler.py` is defined as: ``` BLOCKED_PATTERNS = [ # JupyterLab UI + API r\"/lab(?!/(?:extensions/|api/settings(?:/|$)|api/translations(?:/|$)))(?:/.*)?\" r\"/api/terminals(?:/.*)?\", r\"/api/shutdown(?:/.*)?\", r\"/files(?:/.*)?\", ] ``` The missing comma between the first and the second line causes Python to merge the rules, therefore the actual list that is blocked looks like this: /lab...(?:/.`*`)?/api/terminals(?:/.`*`)? (the first two lines joined together) /api/shutdown(?:/.`*`)? /files(?:/.`*`)? A direct request to `/api/terminals` does not match any of the 3 blocklist rules and is therefore **not blocked**, reaching Jupyter Server's built-in terminal API, which spawns a real OS shell accessible over a WebSocket connection. No authentication, account, session or pre-existing access to Mercury is required. ### PoC Testing was performed on a local Docker deployment (https://runmercury.com/deploy/dockerfile/), Mercury version 3.2.3, default configuration. **Step 1 - spawn a terminal:** ``` curl -i -X POST http://localhost:8888/api/terminals ``` Response: `{\"name\": \"1\", \"last_activity\": \"...\"}` **Step 2 - connect to the terminal and execute commands:** ``` python3",
    "evidence": "osv:GHSA-f8c7-rwf9-fmgm",
    "products": [
      {
        "name": {
          "value": "mercury",
          "evidence": "/affected/0/package/name"
        },
        "prerequisites": [],
        "fixed_versions": [
          {
            "value": "3.2.4",
            "evidence": "/affected/0/ranges"
          }
        ],
        "affected_versions": [
          {
            "value": "3.0.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "3.0.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "3.0.2",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "3.1.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "3.1.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "3.1.2",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "3.1.4",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "3.1.5",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "3.2.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "3.2.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "3.2.2",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "3.2.3",
            "evidence": "/affected/0/versions"
          }
        ]
      }
    ],
    "severity": "critical",
    "structured": {
      "id": "GHSA-f8c7-rwf9-fmgm",
      "aliases": [
        "CVE-2026-111057",
        "GHSA-f8c7-rwf9-fmgm"
      ],
      "metrics": [
        {
          "type": "CVSS_V3",
          "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H"
        }
      ],
      "affected": [
        {
          "ranges": [
            {
              "type": "ECOSYSTEM",
              "events": [
                {
                  "introduced": "3.0.0"
                },
                {
                  "fixed": "3.2.4"
                }
              ]
            }
          ],
          "package": {
            "name": "mercury",
            "purl": "pkg:pypi/mercury",
            "ecosystem": "PyPI"
          },
          "versions": [
            "3.0.0",
            "3.0.1",
            "3.0.2",
            "3.1.0",
            "3.1.1",
            "3.1.2",
            "3.1.4",
            "3.1.5",
            "3.2.0",
            "3.2.1",
            "3.2.2",
            "3.2.3"
          ],
          "database_specific": {
            "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-f8c7-rwf9-fmgm/GHSA-f8c7-rwf9-fmgm.json",
            "last_known_affected_version_range": "<= 3.2.3"
          }
        }
      ],
      "modified": "2026-10-19T17:15:04.105175540Z",
      "provider": "osv",
      "references": [
        "https://github.com/mljar/mercury/security/advisories/GHSA-f8c7-rwf9-fmgm",
        "https://github.com/mljar/mercury/commit/8c467129037ee9df269d543ac217ffd33b97bf3f",
        "https://github.com/mljar/mercury",
        "https://github.com/mljar/mercury/releases/tag/v3.2.4"
      ],
      "provider_metadata": {
        "cwe_ids": [
          "CWE-185",
          "CWE-284"
        ],
        "severity": "CRITICAL",
        "github_reviewed": true,
        "nvd_published_at": null,
        "github_reviewed_at": "2026-10-19T17:00:54Z"
      }
    },
    "related_cves": [
      {
        "value": "CVE-2026-111057",
        "evidence": "/aliases"
      }
    ],
    "security_relevant": true,
    "severity_evidence": "/database_specific/severity"
  },
  "source_id": "core-osv",
  "document_id": 375519,
  "published_at": "2026-10-19T17:00:54+00:00"
}