VYPR
High severity7.3GHSA Advisory· Published May 5, 2026· Updated May 11, 2026

CVE-2026-40110

CVE-2026-40110

Description

Jupyter Server is the backend for Jupyter web applications. In versions 2.17.0 and earlier, the Origin header validation uses Python's re.match() to check incoming origins against the allow_origin_pat configuration value. Because re.match() only anchors at the start of the string and does not require a full match, a pattern intended to match only a trusted domain (e.g., trusted.example.com) will also match any origin that begins with that domain followed by additional characters (e.g., trusted.example.com.evil.com). An attacker who controls such a domain can bypass the CORS origin restriction and make cross-origin requests to the Jupyter Server API from an untrusted site. This issue has been fixed in version 2.18.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
jupyter-serverPyPI
< 2.18.02.18.0

Affected products

2

Patches

3
49b34392feaa

Move check origin into a util function and add it to websocket (#1630)

6 files changed · +136 40
  • jupyter_server/auth/login.py+2 1 modified
    @@ -10,6 +10,7 @@
     from tornado.escape import url_escape
     
     from ..base.handlers import JupyterHandler
    +from ..utils import origin_matches_pat
     from .decorator import allow_unauthenticated
     from .security import passwd_check, set_password
     
    @@ -73,7 +74,7 @@ def _redirect_safe(self, url, default=None):
                     if self.allow_origin:
                         allow = self.allow_origin == origin
                     elif self.allow_origin_pat:
    -                    allow = self._origin_matches_pat(origin)
    +                    allow = origin_matches_pat(self.allow_origin_pat, origin)
                 if not allow:
                     # not allowed, use default
                     self.log.warning("Not allowing login redirect to %r" % url)
    
  • jupyter_server/base/handlers.py+4 24 modified
    @@ -36,6 +36,7 @@
     from jupyter_server.utils import (
         ensure_async,
         filefind,
    +    origin_matches_pat,
         url_escape,
         url_is_absolute,
         url_path_join,
    @@ -383,27 +384,6 @@ def allow_credentials(self) -> bool:
             """Whether to set Access-Control-Allow-Credentials"""
             return cast("bool", self.settings.get("allow_credentials", False))
     
    -    def _origin_matches_pat(self, origin: str) -> bool:
    -        """Check whether origin matches ``allow_origin_pat`` using full-string matching.
    -
    -        Uses ``re.fullmatch`` so the pattern must cover the entire origin string,
    -        preventing prefix-bypass attacks (GHSA-24qx-w28j-9m6p/CVE-2026-40110).
    -        Emits a warning in case a user relied on prefix-style patterns.
    -        """
    -        if not self.allow_origin_pat:
    -            return False
    -        if re.fullmatch(self.allow_origin_pat, origin):
    -            return True
    -        if re.match(self.allow_origin_pat, origin):
    -            warnings.warn(
    -                f"allow_origin_pat {self.allow_origin_pat!r} only matched the request origin as a prefix. "
    -                "This has been replaced with a full string match. "
    -                "Update your pattern if you need to prefix-match the origin (e.g. append '.*')",
    -                UserWarning,
    -                stacklevel=2,
    -            )
    -        return False
    -
         def set_default_headers(self) -> None:
             """Add CORS headers, if defined"""
             super().set_default_headers()
    @@ -418,7 +398,7 @@ def set_cors_headers(self) -> None:
                 self.set_header("Access-Control-Allow-Origin", self.allow_origin)
             elif self.allow_origin_pat:
                 origin = self.get_origin()
    -            if origin and self._origin_matches_pat(origin):
    +            if origin and origin_matches_pat(self.allow_origin_pat, origin):
                     self.set_header("Access-Control-Allow-Origin", origin)
             elif self.token_authenticated and "Access-Control-Allow-Origin" not in self.settings.get(
                 "headers", {}
    @@ -488,7 +468,7 @@ def check_origin(self, origin_to_satisfy_tornado: str = "") -> bool:
             if self.allow_origin:
                 allow = bool(self.allow_origin == origin)
             elif self.allow_origin_pat:
    -            allow = self._origin_matches_pat(origin)
    +            allow = origin_matches_pat(self.allow_origin_pat, origin)
             else:
                 # No CORS headers deny the request
                 allow = False
    @@ -533,7 +513,7 @@ def check_referer(self) -> bool:
             if self.allow_origin:
                 allow = self.allow_origin == origin
             elif self.allow_origin_pat:
    -            allow = self._origin_matches_pat(origin)
    +            allow = origin_matches_pat(self.allow_origin_pat, origin)
             else:
                 # No CORS settings, deny the request
                 allow = False
    
  • jupyter_server/base/websocket.py+2 2 modified
    @@ -8,7 +8,7 @@
     from tornado.iostream import IOStream
     
     from jupyter_server.base.handlers import JupyterHandler
    -from jupyter_server.utils import JupyterServerAuthWarning
    +from jupyter_server.utils import JupyterServerAuthWarning, origin_matches_pat
     
     # ping interval for keeping websockets alive (30 seconds)
     WS_PING_INTERVAL = 30000
    @@ -71,7 +71,7 @@ def check_origin(self, origin: Optional[str] = None) -> bool:
             if self.allow_origin:
                 allow = self.allow_origin == origin
             elif self.allow_origin_pat:
    -            allow = self._origin_matches_pat(origin)
    +            allow = origin_matches_pat(self.allow_origin_pat, origin)
             else:
                 # No CORS headers deny the request
                 allow = False
    
  • jupyter_server/utils.py+23 0 modified
    @@ -7,6 +7,7 @@
     import errno
     import importlib.util
     import os
    +import re
     import socket
     import sys
     import warnings
    @@ -42,6 +43,28 @@
     ensure_async = _ensure_async
     
     
    +def origin_matches_pat(allow_origin_pat: str, origin: str) -> bool:
    +    """Check whether origin matches ``allow_origin_pat`` using full-string matching.
    +
    +    Uses ``re.fullmatch`` so the pattern must cover the entire origin string,
    +    preventing prefix-bypass attacks (GHSA-24qx-w28j-9m6p/CVE-2026-40110).
    +    Emits a warning in case a user relied on prefix-style patterns.
    +    """
    +    if not allow_origin_pat:
    +        return False
    +    if re.fullmatch(allow_origin_pat, origin):
    +        return True
    +    if re.match(allow_origin_pat, origin):
    +        warnings.warn(
    +            f"allow_origin_pat {allow_origin_pat!r} only matched the request origin as a prefix. "
    +            "This has been replaced with a full string match. "
    +            "Update your pattern if you need to prefix-match the origin (e.g. append '.*')",
    +            UserWarning,
    +            stacklevel=3,
    +        )
    +    return False
    +
    +
     def url_path_join(*pieces: str) -> str:
         """Join components of url into a relative url
     
    
  • tests/base/test_allow_origin_pat.py+19 13 modified
    @@ -7,6 +7,7 @@
     to match only "https://trusted.example.com".
     """
     
    +import warnings
     from unittest.mock import MagicMock
     
     import pytest
    @@ -45,7 +46,8 @@ def _make_handler(jp_serverapp, origin=None, host="localhost:8888", referer=None
     
     @pytest.mark.parametrize("origin", BYPASS_ORIGINS)
     def test_check_origin_rejects_bypass(jp_serverapp, origin):
    -    assert not _make_handler(jp_serverapp, origin=origin, host="other.host:8888").check_origin()
    +    with pytest.warns(UserWarning, match="allow_origin_pat.*"):
    +        assert not _make_handler(jp_serverapp, origin=origin, host="other.host:8888").check_origin()
     
     
     def test_check_origin_allows_trusted(jp_serverapp):
    @@ -55,29 +57,32 @@ def test_check_origin_allows_trusted(jp_serverapp):
     @pytest.mark.parametrize("origin", BYPASS_ORIGINS)
     def test_check_referer_rejects_bypass(jp_serverapp, origin):
         handler = _make_handler(jp_serverapp, host="other.host:8888", referer=f"{origin}/page")
    -    assert not handler.check_referer()
    +    with pytest.warns(UserWarning, match="allow_origin_pat.*"):
    +        assert not handler.check_referer()
     
     
     @pytest.mark.parametrize("origin", BYPASS_ORIGINS)
     def test_set_cors_headers_rejects_bypass(jp_serverapp, origin):
    -    handler = _make_handler(jp_serverapp, origin=origin)
    -    handler.set_cors_headers()
    -    assert handler._headers.get("Access-Control-Allow-Origin") != origin
    +    with pytest.warns(UserWarning, match="allow_origin_pat.*"):
    +        handler = _make_handler(jp_serverapp, origin=origin)
    +        handler.set_cors_headers()
    +        assert handler._headers.get("Access-Control-Allow-Origin") != origin
     
     
     @pytest.mark.parametrize("attacker_origin", BYPASS_ORIGINS)
     def test_login_redirect_safe_rejects_bypass(jp_serverapp, attacker_origin):
         request = HTTPRequest("GET", headers=HTTPHeaders({"Host": "localhost:8888"}))
         request.connection = MagicMock()
    -    handler = LoginHandler(jp_serverapp.web_app, request)
    -    handler.settings["allow_origin"] = ""
    -    handler.settings["allow_origin_pat"] = TRUSTED_PAT
    +    with pytest.warns(UserWarning, match="allow_origin_pat.*"):
    +        handler = LoginHandler(jp_serverapp.web_app, request)
    +        handler.settings["allow_origin"] = ""
    +        handler.settings["allow_origin_pat"] = TRUSTED_PAT
     
    -    redirected_to = []
    -    handler.redirect = lambda url, *a, **kw: redirected_to.append(url)
    -    handler._redirect_safe(f"{attacker_origin}/capture", default=jp_serverapp.base_url)
    +        redirected_to = []
    +        handler.redirect = lambda url, *a, **kw: redirected_to.append(url)
    +        handler._redirect_safe(f"{attacker_origin}/capture", default=jp_serverapp.base_url)
     
    -    assert redirected_to[0] != f"{attacker_origin}/capture"
    +        assert redirected_to[0] != f"{attacker_origin}/capture"
     
     
     @pytest.mark.parametrize("attacker_origin", BYPASS_ORIGINS)
    @@ -96,7 +101,8 @@ class TestWSHandler(WebSocketMixin, JupyterHandler):
         handler.settings["allow_origin_pat"] = TRUSTED_PAT
         handler.skip_check_origin = lambda: False
     
    -    assert not handler.check_origin(attacker_origin)
    +    with pytest.warns(UserWarning, match="allow_origin_pat.*"):
    +        assert not handler.check_origin(attacker_origin)
     
     
     def test_truncated_pattern_warns_and_blocks(jp_serverapp):
    
  • tests/test_utils.py+86 0 modified
    @@ -15,6 +15,7 @@
         check_version,
         filefind,
         is_namespace_package,
    +    origin_matches_pat,
         path2url,
         run_sync_in_loop,
         samefile_simple,
    @@ -164,3 +165,88 @@ def test_filefind(tmp_path, filename, result):
         else:
             with pytest.raises(result):
                 filefind(filename, [str(a), str(b)])
    +
    +
    +TRUSTED_PAT = r"https://trusted\.example\.com"
    +
    +
    +@pytest.mark.parametrize(
    +    "origin",
    +    [
    +        "https://trusted.example.com",
    +        # pattern is the full origin string
    +    ],
    +)
    +def test_origin_matches_pat_accepts_exact(origin):
    +    assert origin_matches_pat(TRUSTED_PAT, origin) is True
    +
    +
    +@pytest.mark.parametrize(
    +    "origin",
    +    [
    +        # suffix-bypass: pre-CVE-2026-40110 prefix matching would have allowed these
    +        "https://trusted.example.com.evil.com",
    +        "https://trusted.example.comedy",
    +        "https://trusted.example.com:9999",
    +        "https://trusted.example.com/path",
    +        # newline injection — must not be allowed via $-anchor or otherwise
    +        "https://trusted.example.com\nhttps://evil.com",
    +    ],
    +)
    +def test_origin_matches_pat_rejects_full_match_failures(origin):
    +    # These all match `re.match` (prefix) but NOT `re.fullmatch`, so the helper
    +    # warns and returns False.
    +    with pytest.warns(UserWarning, match="only matched the request origin as a prefix"):
    +        assert origin_matches_pat(TRUSTED_PAT, origin) is False
    +
    +
    +@pytest.mark.parametrize(
    +    "origin",
    +    [
    +        "http://trusted.example.com",
    +        "https://other.example.com",
    +        "https://trusted.example.co",
    +        "",
    +    ],
    +)
    +def test_origin_matches_pat_rejects_non_match(origin):
    +    # No prefix match either, so no warning.
    +    with warnings.catch_warnings():
    +        warnings.simplefilter("error")
    +        assert origin_matches_pat(TRUSTED_PAT, origin) is False
    +
    +
    +def test_origin_matches_pat_empty_pattern_rejects_all():
    +    # Empty pattern must never allow any origin (empty-string regex would otherwise
    +    # fullmatch only the empty string, but the early-return guard makes this explicit).
    +    assert origin_matches_pat("", "") is False
    +    assert origin_matches_pat("", "https://anything") is False
    +
    +
    +def test_origin_matches_pat_respects_user_anchors():
    +    # Patterns that already include ^ and $ should still work (fullmatch is idempotent
    +    # with explicit anchors).
    +    assert origin_matches_pat(r"^https://trusted\.example\.com$", "https://trusted.example.com")
    +    assert not origin_matches_pat(
    +        r"^https://trusted\.example\.com$", "https://trusted.example.com.evil"
    +    )
    +
    +
    +def test_origin_matches_pat_alternation():
    +    pat = r"https://a\.com|https://b\.com"
    +    assert origin_matches_pat(pat, "https://a.com") is True
    +    assert origin_matches_pat(pat, "https://b.com") is True
    +    # alternation must not be exploitable as a prefix:
    +    with pytest.warns(UserWarning, match="only matched the request origin as a prefix"):
    +        assert origin_matches_pat(pat, "https://a.comevil") is False
    +
    +
    +def test_origin_matches_pat_unescaped_dot_is_a_footgun():
    +    # Documented footgun: an operator who forgets to escape `.` writes a regex that
    +    # treats it as a wildcard. The helper has no way to detect this — it is the
    +    # caller's responsibility to escape literal characters in `allow_origin_pat`.
    +    # This test pins the current behavior so a future change that auto-escapes or
    +    # warns about unescaped dots is a deliberate, reviewed decision.
    +    bad_pattern = r"https://trusted.example.com"  # dots NOT escaped
    +    assert origin_matches_pat(bad_pattern, "https://trustedxexamplexcom") is True
    +    assert origin_matches_pat(bad_pattern, "https://trusted-example-com") is True
    
057869a327c4

Fix allow_origin_pat to do full matching instead of prefix matching

https://github.com/jupyter-server/jupyter_serverYann PellegriniApr 9, 2026via ghsa
4 files changed · +138 6
  • jupyter_server/auth/login.py+1 1 modified
    @@ -68,7 +68,7 @@ def _redirect_safe(self, url, default=None):
                     if self.allow_origin:
                         allow = self.allow_origin == origin
                     elif self.allow_origin_pat:
    -                    allow = bool(re.match(self.allow_origin_pat, origin))
    +                    allow = self._origin_matches_pat(origin)
                 if not allow:
                     # not allowed, use default
                     self.log.warning("Not allowing login redirect to %r" % url)
    
  • jupyter_server/base/handlers.py+24 3 modified
    @@ -383,6 +383,27 @@ def allow_credentials(self) -> bool:
             """Whether to set Access-Control-Allow-Credentials"""
             return cast("bool", self.settings.get("allow_credentials", False))
     
    +    def _origin_matches_pat(self, origin: str) -> bool:
    +        """Check whether origin matches ``allow_origin_pat`` using full-string matching.
    +
    +        Uses ``re.fullmatch`` so the pattern must cover the entire origin string,
    +        preventing prefix-bypass attacks (GHSA-24qx-w28j-9m6p/CVE-2026-40110).
    +        Emits a warning in case a user relied on prefix-style patterns.
    +        """
    +        if not self.allow_origin_pat:
    +            return False
    +        if re.fullmatch(self.allow_origin_pat, origin):
    +            return True
    +        if re.match(self.allow_origin_pat, origin):
    +            warnings.warn(
    +                f"allow_origin_pat {self.allow_origin_pat!r} only matched the request origin as a prefix. "
    +                "This has been replaced with a full string match. "
    +                "Update your pattern if you need to prefix-match the origin (e.g. append '.*')",
    +                UserWarning,
    +                stacklevel=2,
    +            )
    +        return False
    +
         def set_default_headers(self) -> None:
             """Add CORS headers, if defined"""
             super().set_default_headers()
    @@ -397,7 +418,7 @@ def set_cors_headers(self) -> None:
                 self.set_header("Access-Control-Allow-Origin", self.allow_origin)
             elif self.allow_origin_pat:
                 origin = self.get_origin()
    -            if origin and re.match(self.allow_origin_pat, origin):
    +            if origin and self._origin_matches_pat(origin):
                     self.set_header("Access-Control-Allow-Origin", origin)
             elif self.token_authenticated and "Access-Control-Allow-Origin" not in self.settings.get(
                 "headers", {}
    @@ -467,7 +488,7 @@ def check_origin(self, origin_to_satisfy_tornado: str = "") -> bool:
             if self.allow_origin:
                 allow = bool(self.allow_origin == origin)
             elif self.allow_origin_pat:
    -            allow = bool(re.match(self.allow_origin_pat, origin))
    +            allow = self._origin_matches_pat(origin)
             else:
                 # No CORS headers deny the request
                 allow = False
    @@ -512,7 +533,7 @@ def check_referer(self) -> bool:
             if self.allow_origin:
                 allow = self.allow_origin == origin
             elif self.allow_origin_pat:
    -            allow = bool(re.match(self.allow_origin_pat, origin))
    +            allow = self._origin_matches_pat(origin)
             else:
                 # No CORS settings, deny the request
                 allow = False
    
  • jupyter_server/base/websocket.py+1 2 modified
    @@ -1,6 +1,5 @@
     """Base websocket classes."""
     
    -import re
     import warnings
     from typing import Optional, no_type_check
     from urllib.parse import urlparse
    @@ -72,7 +71,7 @@ def check_origin(self, origin: Optional[str] = None) -> bool:
             if self.allow_origin:
                 allow = self.allow_origin == origin
             elif self.allow_origin_pat:
    -            allow = bool(re.match(self.allow_origin_pat, origin))
    +            allow = self._origin_matches_pat(origin)
             else:
                 # No CORS headers deny the request
                 allow = False
    
  • tests/base/test_allow_origin_pat.py+112 0 added
    @@ -0,0 +1,112 @@
    +"""Tests for allow_origin_pat origin validation using re.fullmatch()
    +
    +(GHSA-24qx-w28j-9m6p and CVE-2026-40110)
    +
    +allow_origin_pat must match the full origin string. re.match() only anchors at the
    +start, allowing "https://trusted.example.com.evil.com" to bypass a pattern intended
    +to match only "https://trusted.example.com".
    +"""
    +
    +from unittest.mock import MagicMock
    +
    +import pytest
    +from tornado.httpserver import HTTPRequest
    +from tornado.httputil import HTTPHeaders
    +
    +from jupyter_server.auth.login import LoginHandler
    +from jupyter_server.base.handlers import JupyterHandler
    +from jupyter_server.base.websocket import WebSocketMixin
    +
    +TRUSTED_PAT = r"https://trusted\.example\.com"
    +TRUSTED_ORIGIN = "https://trusted.example.com"
    +
    +BYPASS_ORIGINS = [
    +    "https://trusted.example.com.evil.com",
    +    "https://trusted.example.comedy",
    +    "https://trusted.example.com:9999",
    +]
    +
    +
    +def _make_handler(jp_serverapp, origin=None, host="localhost:8888", referer=None):
    +    headers = {"Host": host}
    +    if origin:
    +        headers["Origin"] = origin
    +    if referer:
    +        headers["Referer"] = referer
    +    request = HTTPRequest("GET", headers=HTTPHeaders(headers))
    +    request.connection = MagicMock()
    +    handler = JupyterHandler(jp_serverapp.web_app, request)
    +    handler.settings["allow_origin"] = ""
    +    handler.settings["allow_origin_pat"] = TRUSTED_PAT
    +    # skip_check_origin() requires async auth context; bypass it to reach pattern matching
    +    handler.skip_check_origin = lambda: False
    +    return handler
    +
    +
    +@pytest.mark.parametrize("origin", BYPASS_ORIGINS)
    +def test_check_origin_rejects_bypass(jp_serverapp, origin):
    +    assert not _make_handler(jp_serverapp, origin=origin, host="other.host:8888").check_origin()
    +
    +
    +def test_check_origin_allows_trusted(jp_serverapp):
    +    assert _make_handler(jp_serverapp, origin=TRUSTED_ORIGIN, host="other.host:8888").check_origin()
    +
    +
    +@pytest.mark.parametrize("origin", BYPASS_ORIGINS)
    +def test_check_referer_rejects_bypass(jp_serverapp, origin):
    +    handler = _make_handler(jp_serverapp, host="other.host:8888", referer=f"{origin}/page")
    +    assert not handler.check_referer()
    +
    +
    +@pytest.mark.parametrize("origin", BYPASS_ORIGINS)
    +def test_set_cors_headers_rejects_bypass(jp_serverapp, origin):
    +    handler = _make_handler(jp_serverapp, origin=origin)
    +    handler.set_cors_headers()
    +    assert handler._headers.get("Access-Control-Allow-Origin") != origin
    +
    +
    +@pytest.mark.parametrize("attacker_origin", BYPASS_ORIGINS)
    +def test_login_redirect_safe_rejects_bypass(jp_serverapp, attacker_origin):
    +    request = HTTPRequest("GET", headers=HTTPHeaders({"Host": "localhost:8888"}))
    +    request.connection = MagicMock()
    +    handler = LoginHandler(jp_serverapp.web_app, request)
    +    handler.settings["allow_origin"] = ""
    +    handler.settings["allow_origin_pat"] = TRUSTED_PAT
    +
    +    redirected_to = []
    +    handler.redirect = lambda url, *a, **kw: redirected_to.append(url)
    +    handler._redirect_safe(f"{attacker_origin}/capture", default=jp_serverapp.base_url)
    +
    +    assert redirected_to[0] != f"{attacker_origin}/capture"
    +
    +
    +@pytest.mark.parametrize("attacker_origin", BYPASS_ORIGINS)
    +def test_websocket_check_origin_rejects_bypass(jp_serverapp, attacker_origin):
    +    request = HTTPRequest(
    +        "GET",
    +        headers=HTTPHeaders({"Origin": attacker_origin, "Host": "other.host:8888"}),
    +    )
    +    request.connection = MagicMock()
    +
    +    class TestWSHandler(WebSocketMixin, JupyterHandler):
    +        pass
    +
    +    handler = TestWSHandler(jp_serverapp.web_app, request)
    +    handler.settings["allow_origin"] = ""
    +    handler.settings["allow_origin_pat"] = TRUSTED_PAT
    +    handler.skip_check_origin = lambda: False
    +
    +    assert not handler.check_origin(attacker_origin)
    +
    +
    +def test_truncated_pattern_warns_and_blocks(jp_serverapp):
    +    """Prefix-only pattern (e.g. 'https://trusted') emits UserWarning and blocks the request."""
    +    handler = _make_handler(
    +        jp_serverapp, origin="https://trusted.example.com", host="other.host:8888"
    +    )
    +    handler.settings["allow_origin_pat"] = r"https://trusted"
    +
    +    with pytest.warns(UserWarning, match="only matched the request origin as a prefix"):
    +        result = handler.check_origin()
    +
    +    assert not result
    
3ecb4e0184b7

Fix allow_origin_pat property to properly parse regex (#603)

https://github.com/jupyter-server/jupyter_serverBrian CherinkaNov 2, 2021via nvd-ref
3 files changed · +6 5
  • jupyter_server/auth/login.py+1 1 modified
    @@ -53,7 +53,7 @@ def _redirect_safe(self, url, default=None):
                     if self.allow_origin:
                         allow = self.allow_origin == origin
                     elif self.allow_origin_pat:
    -                    allow = bool(self.allow_origin_pat.match(origin))
    +                    allow = bool(re.match(self.allow_origin_pat, origin))
                 if not allow:
                     # not allowed, use default
                     self.log.warning("Not allowing login redirect to %r" % url)
    
  • jupyter_server/base/handlers.py+3 3 modified
    @@ -309,7 +309,7 @@ def set_default_headers(self):
                 self.set_header("Access-Control-Allow-Origin", self.allow_origin)
             elif self.allow_origin_pat:
                 origin = self.get_origin()
    -            if origin and self.allow_origin_pat.match(origin):
    +            if origin and re.match(self.allow_origin_pat, origin):
                     self.set_header("Access-Control-Allow-Origin", origin)
             elif self.token_authenticated and "Access-Control-Allow-Origin" not in self.settings.get(
                 "headers", {}
    @@ -382,7 +382,7 @@ def check_origin(self, origin_to_satisfy_tornado=""):
             if self.allow_origin:
                 allow = self.allow_origin == origin
             elif self.allow_origin_pat:
    -            allow = bool(self.allow_origin_pat.match(origin))
    +            allow = bool(re.match(self.allow_origin_pat, origin))
             else:
                 # No CORS headers deny the request
                 allow = False
    @@ -427,7 +427,7 @@ def check_referer(self):
             if self.allow_origin:
                 allow = self.allow_origin == origin
             elif self.allow_origin_pat:
    -            allow = bool(self.allow_origin_pat.match(origin))
    +            allow = bool(re.match(self.allow_origin_pat, origin))
             else:
                 # No CORS settings, deny the request
                 allow = False
    
  • jupyter_server/base/zmqhandlers.py+2 1 modified
    @@ -3,6 +3,7 @@
     # Copyright (c) Jupyter Development Team.
     # Distributed under the terms of the Modified BSD License.
     import json
    +import re
     import struct
     import sys
     from urllib.parse import urlparse
    @@ -139,7 +140,7 @@ def check_origin(self, origin=None):
             if self.allow_origin:
                 allow = self.allow_origin == origin
             elif self.allow_origin_pat:
    -            allow = bool(self.allow_origin_pat.match(origin))
    +            allow = bool(re.match(self.allow_origin_pat, origin))
             else:
                 # No CORS headers deny the request
                 allow = False
    

Vulnerability mechanics

AI mechanics synthesis has not run for this CVE yet.

References

6

News mentions

0

No linked articles in our index yet.