diff --git a/.changeset/git-credentials-url-encode.md b/.changeset/git-credentials-url-encode.md new file mode 100644 index 0000000000..287e083150 --- /dev/null +++ b/.changeset/git-credentials-url-encode.md @@ -0,0 +1,5 @@ +--- +"@e2b/python-sdk": patch +--- + +Percent-encode the username and password when embedding HTTP(S) Git credentials into a clone URL. Previously they were inserted raw, so a username or token containing URL-significant characters (`@`, `/`, `:`, spaces, etc.) produced a malformed URL that git could not use. This now matches the JS SDK, which already encodes the credentials. diff --git a/packages/python-sdk/e2b/sandbox/_git/auth.py b/packages/python-sdk/e2b/sandbox/_git/auth.py index a93511f48d..d668eba8f0 100644 --- a/packages/python-sdk/e2b/sandbox/_git/auth.py +++ b/packages/python-sdk/e2b/sandbox/_git/auth.py @@ -1,5 +1,5 @@ from typing import Optional -from urllib.parse import urlparse, urlunparse +from urllib.parse import quote, urlparse, urlunparse from e2b.exceptions import InvalidArgumentException from e2b.sandbox.commands.command_handle import CommandExitException @@ -27,7 +27,10 @@ def with_credentials(url: str, username: Optional[str], password: Optional[str]) "Only http(s) Git URLs support username/password credentials." ) - netloc = f"{username}:{password}@{parsed.netloc}" + # Percent-encode credentials so URL-significant characters (e.g. "@", "/", + # ":") in a username/token don't corrupt the URL. Matches the JS SDK, which + # encodes via the URL username/password setters. + netloc = f"{quote(username, safe='')}:{quote(password, safe='')}@{parsed.netloc}" return urlunparse(parsed._replace(netloc=netloc)) diff --git a/packages/python-sdk/tests/shared/git/test_auth.py b/packages/python-sdk/tests/shared/git/test_auth.py new file mode 100644 index 0000000000..c0d21b2cc6 --- /dev/null +++ b/packages/python-sdk/tests/shared/git/test_auth.py @@ -0,0 +1,26 @@ +from e2b.sandbox._git.auth import strip_credentials, with_credentials + + +def test_with_credentials_percent_encodes_special_characters(): + # URL-significant characters in the username/token must be percent-encoded + # so they don't corrupt the URL (git would otherwise mis-parse the host). + assert ( + with_credentials("https://github.com/o/r.git", "user", "p@ss") + == "https://user:p%40ss@github.com/o/r.git" + ) + assert ( + with_credentials("https://github.com/o/r.git", "us er", "a/b:c") + == "https://us%20er:a%2Fb%3Ac@github.com/o/r.git" + ) + + +def test_with_credentials_round_trips_through_strip(): + url = with_credentials("https://github.com/o/r.git", "user", "p@ss") + assert strip_credentials(url) == "https://github.com/o/r.git" + + +def test_with_credentials_without_credentials_returns_url_unchanged(): + assert ( + with_credentials("https://github.com/o/r.git", None, None) + == "https://github.com/o/r.git" + )