Skip to content

fix(python-sdk): percent-encode git credentials in with_credentials#1507

Draft
anxkhn wants to merge 1 commit into
e2b-dev:mainfrom
anxkhn:loop/e2b__002
Draft

fix(python-sdk): percent-encode git credentials in with_credentials#1507
anxkhn wants to merge 1 commit into
e2b-dev:mainfrom
anxkhn:loop/e2b__002

Conversation

@anxkhn

@anxkhn anxkhn commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What

Sandbox.git.clone and Sandbox.git.pull embed HTTP(S) credentials by passing them
through with_credentials (packages/python-sdk/e2b/sandbox/_git/auth.py), which built
the URL netloc by raw string interpolation:

netloc = f"{username}:{password}@{parsed.netloc}"

A username, password, or token that contains URL-reserved characters (@ : / # ?) is
spliced straight into the netloc, so the resulting URL is corrupted and git auth fails.
Common GitHub PATs are alphanumeric and unaffected, but GitLab/Bitbucket app passwords
and many tokens do contain reserved characters.

The JavaScript SDK does not have this problem: withCredentials
(packages/js-sdk/src/sandbox/git/utils.ts) assigns parsed.username / parsed.password
through the WHATWG URL setters, which percent-encode automatically. So the two SDKs
disagree on identical input.

password Python (before) JS / Python (after)
p@ss/w:rd user:p@ss/w:rd@github.com/... user:p%40ss%2Fw%3Ard@github.com/...
x#y?z user:x#y?z@github.com/... user:x%23y%3Fz@github.com/...
ghp_AbC123 user:ghp_AbC123@... (ok) user:ghp_AbC123@... (unchanged)

Fix

Percent-encode both parts with quote(safe='') before building the netloc, matching the
JS SDK byte-for-byte. Alphanumeric tokens are unchanged.

netloc = f"{quote(username, safe='')}:{quote(password, safe='')}@{parsed.netloc}"

This is the only behavior change. Sync and async share this single helper, so both clone
and pull are fixed by the one-line edit.

Usage example (now works)

from e2b import Sandbox

sbx = Sandbox()
# token with reserved characters no longer corrupts the URL
sbx.git.clone(
    "https://github.com/org/private.git",
    username="user",
    password="p@ss/w:rd",
)

Tests

  • packages/python-sdk/tests/shared/git/test_auth.py (new, 5 offline cases): reserved
    chars encode, alnum token unchanged, no-cred passthrough, partial creds raise,
    non-http(s) raises. Runs without a sandbox (imports e2b.sandbox._git).
  • packages/js-sdk/tests/sandbox/git/validation.test.ts (+1 case): locks the JS encoded
    output so the two SDKs stay in parity.

Changeset: @e2b/python-sdk patch.

@cla-bot

cla-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

We require contributors to sign our Contributor License Agreement, and we don't have @anxkhn on file. You can sign our CLA at https://e2b.dev/docs/cla . Once you've signed, post a comment here that says '@cla-bot check'

@changeset-bot

changeset-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 432ec57

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@e2b/python-sdk Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@anxkhn

anxkhn commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@cla-bot check

@cla-bot cla-bot Bot added the cla-signed label Jul 1, 2026
@cla-bot

cla-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

The cla-bot has been summoned, and re-checked this pull request!

mishushakov pushed a commit that referenced this pull request Jul 14, 2026
The JSDoc `@example` on the public `Sandbox.getHost()` method calls
`sandbox.commands.exec(...)`, but the `Commands` class has no `exec`
method. It
exposes `run`. Copy-pasting the documented snippet therefore throws:

```
TypeError: sandbox.commands.exec is not a function
```

### Where

`packages/js-sdk/src/sandbox/index.ts`, in the `getHost()` doc comment:

```ts
/**
 * ...
 * @example
 * ```ts
 * const sandbox = await Sandbox.create()
 * // Start an HTTP server
 * await sandbox.commands.exec('python3 -m http.server 3000')  // <- no such method
 * // Get the hostname of the HTTP server
 * const serverURL = sandbox.getHost(3000)
 * ```
 */
```

The `Commands` class (`packages/js-sdk/src/sandbox/commands/index.ts`)
exposes
`list`, `sendStdin`, `closeStdin`, `kill`, `connect`, and `run` (four
`run`
overloads), plus a private `start`. There is no `exec`. The correct
method here
is `run`, which is what every other example already uses, including the
sibling
`@example` in this same file (the `commands.run(...)` snippet a few
methods up)
and both Python SDK mirrors (`get_host` in `sandbox_sync/main.py` and
`sandbox_async/main.py` already use `commands.run`).

### Fix

One token, `exec` -> `run`:

```ts
- await sandbox.commands.exec('python3 -m http.server 3000')
+ await sandbox.commands.run('python3 -m http.server 3000')
```

Documentation only. No behavior or type change.

### Parity with the Python SDK

The repo guidelines ask that SDK changes be mirrored across the JS and
Python
SDKs. Here the Python `get_host` examples already use `commands.run`
correctly,
so this defect exists only in the JS SDK doc comment and no Python
change is
needed to reach parity.

### Tests

This is a JSDoc `@example` correction with no runtime code path to
exercise, so
it adds no test, matching the repo's existing precedent for
documentation-only
fixes (e.g. `.changeset/sandbox-list-docstring.md`, and merged doc-fix
PRs such
as #1511 / #1500 / #1260, none of which added a regression test).
Correctness is
that the example now names the real public API: after the change,
`commands.exec`
no longer appears anywhere in the SDK source, and `commands.run` matches
the
`Commands` class and the sibling examples.

Offline gates run locally (Node 20, pnpm 9.15.5):

```
pnpm --filter e2b run lint        # oxlint, clean
pnpm --filter e2b run typecheck   # tsc --noEmit, clean
pnpm --filter e2b run build       # tsc + tsup, ESM/CJS/DTS built
prettier --check src/sandbox/index.ts  # clean
```

A changeset (`e2b`, patch) is included.

---

## Linked issues

- None. There is no existing GitHub issue for this; it is a self-evident
public
doc-example defect (the documented snippet throws at runtime). Not
filing a
  separate issue for a one-token doc fix.

## Pre-flight checklist (repo AGENTS.md / CLAUDE.md gates)

- [x] `pnpm run format` - `prettier --check` clean on the changed file
- [x] `pnpm run lint` - oxlint clean (exit 0)
- [x] `pnpm run typecheck` - tsc --noEmit clean (exit 0)
- [x] `pnpm run build` - tsc + tsup clean
- [x] Changeset generated - `.changeset/fix-gethost-example-command.md`
(`e2b`: patch)
- [x] Conventional Commit message (`fix(js-sdk): ...`, reuses `js-sdk`
scope)
- [ ] Test added - not applicable (doc-only `@example`; see Tests
section for precedent)
- [ ] DCO / CLA - no sign-off required by this repo; CLA is signed via
`@cla-bot`
      on the PR after opening (as on prior PRs #1518 / #1519 / #1507)

Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
git.clone and git.pull embed HTTP(S) credentials via with_credentials,
which interpolated the username and password straight into the URL netloc.
A user/token containing URL-reserved characters (@ : / # ?) produced a
corrupted git URL and broke authentication.

Percent-encode both parts with quote(safe=''), matching the JS SDK which
already encodes via the WHATWG URL setters. Alphanumeric tokens are
unchanged. Add offline unit tests for the Python helper and a JS parity
test to lock the encoded output.

Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
@anxkhn

anxkhn commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Pushed a revision addressing the latest review/CI feedback.

@mishushakov

Copy link
Copy Markdown
Member

We're holding off contributions to the Git integration for a bit. Thanks!

@mishushakov mishushakov marked this pull request as draft July 15, 2026 09:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants