CVE-2023-34233 (Python, published 2023-06-08T21:15Z)
Description: value: The Snowflake Connector for Python provides an interface for developing Python applications that can connect to Snowflake and perform all standard operations. Versions prior to 3.0.2 are vulnerable to command injection via single sign-on(SSO) browser URL authentication. In order to exploit the potential for command injection, an attacker would need to be successful in (1) establishing a malicious resource and (2) redirecting users to utilize the resource. The attacker could set up a malicious, publicly accessible server which responds to the SSO URL with an attack payload. If the attacker then tricked a user into visiting the maliciously crafted connection URL, the user’s local machine would render the malicious payload, leading to a remote code execution. This attack scenario can be mitigated through URL whitelisting as well as common anti-phishing resources. Version 3.0.2 contains a patch for this issue.
Severity: nan (CVSS v3 base score: 8.8)
Weakness: CWE-77 \u2014 Improper Neutralization of Special Elements used in a Command ('Command Injection')
The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component.
Repository: https://github.com/snowflakedb/snowflake-connector-python
Files changed:
.pre-commit-config.yaml
DESCRIPTION.md
setup.py
src/snowflake/connector/auth/webbrowser.py
src/snowflake/connector/snow_logging.py
src/snowflake/connector/url_util.py
test/unit/test_auth_webbrowser.py
test/unit/test_url_util.py
Commit message: SNOW-761004 Added URL Validator and URL escaping of strings (#1480)



Vulnerable code:
    warnings.warn("Cannot compile native C code, because of a missing build dependency")


            logger.debug("step 1: query GS to obtain SSO url")
            sso_url = self._get_sso_url(
                conn, authenticator, service_name, account, callback_port, user
            )

            2,

Fixed code:
                | url_util
  - Validate SSO URL before opening it in the browser for External browser authenticator.
    warnings.warn(
        "Cannot compile native C code, because of a missing build dependency",
        stacklevel=1,
    )
    ER_INVALID_VALUE,
from ..url_util import is_valid_url
            logger.debug("step 1: query GS to obtain SSO url")
            sso_url = self._get_sso_url(
                conn, authenticator, service_name, account, callback_port, user
            )

            logger.debug("Validate SSO URL")
            if not is_valid_url(sso_url):
                self._handle_failure(
                    conn=conn,
                    ret={
                        "code": ER_INVALID_VALUE,
                        "message": (f"The SSO URL provided {sso_url} is invalid"),
                    },
                )
                return

            stacklevel=2,
#
# Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
#

from __future__ import annotations

import re
import urllib.parse
from logging import getLogger

logger = getLogger(__name__)


URL_VALIDATOR = re.compile(
    "^http(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z@:])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\&\\(\\)\\/\\\\\\+&%\\$#_=@]*)?$"
)


def is_valid_url(url: str) -> bool:
    """Confirms if the provided URL is a valid HTTP/ HTTPs URL

    Args:
        url: the URL that needs to be validated

    Returns:
        true/ false depending on whether the URL is valid or not
    """
    return bool(URL_VALIDATOR.match(url))


def url_encode_str(target: str | None) -> str:
    """Converts a target string into escaped URL safe string

    Args:
        target: string to be URL encoded

    Returns:
        URL encoded string
    """
    if target is None:
        logger.debug("The string to be URL encoded is None")
        return ""
    return urllib.parse.quote_plus(target, safe="")
INVALID_SSO_URL = "this is an invalid URL"


def test_auth_webbrowser_invalid_sso(monkeypatch):
    """Authentication by WebBrowser with failed to start web browser case."""
    rest = _init_rest(INVALID_SSO_URL, REF_PROOF_KEY)

    # mock webbrowser
    mock_webbrowser = MagicMock()
    mock_webbrowser.open_new.return_value = False

    # mock socket
    mock_socket_instance = MagicMock()
    mock_socket_instance.getsockname.return_value = [None, 12345]

    mock_socket_client = MagicMock()
    mock_socket_client.recv.return_value = (
        "\r\n".join(["GET /?token=MOCK_TOKEN HTTP/1.1", "User-Agent: snowflake-agent"])
    ).encode("utf-8")
    mock_socket_instance.accept.return_value = (mock_socket_client, None)
    mock_socket = Mock(return_value=mock_socket_instance)

    auth = AuthByWebBrowser(
        application=APPLICATION,
        webbrowser_pkg=mock_webbrowser,
        socket_pkg=mock_socket,
    )
    auth.prepare(
        conn=rest._connection,
        authenticator=AUTHENTICATOR,
        service_name=SERVICE_NAME,
        account=ACCOUNT,
        user=USER,
        password=PASSWORD,
    )
    assert rest._connection.errorhandler.called  # an error
    assert auth.assertion_content is None
#
# Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
#

try:
    from snowflake.connector.url_util import is_valid_url, url_encode_str
except ImportError:

    def is_valid_url(s):
        return False


def test_url_validator():
    assert is_valid_url("https://ssoTestURL.okta.com")
    assert is_valid_url("https://ssoTestURL.okta.com:8080")
    assert is_valid_url("https://ssoTestURL.okta.com/testpathvalue")

    assert not is_valid_url("-a Calculator")
    assert not is_valid_url("This is a random text")
    assert not is_valid_url("file://TestForFile")


def test_encoder():
    assert url_encode_str("Hello @World") == "Hello+%40World"
    assert url_encode_str("Test//String") == "Test%2F%2FString"
    assert url_encode_str(None) == ""