Skip to content

Harden git extension auth proxy#802

Open
ghostwriternr wants to merge 1 commit into
ext/gitfrom
fix/git-extension-proxy-leases
Open

Harden git extension auth proxy#802
ghostwriternr wants to merge 1 commit into
ext/gitfrom
fix/git-extension-proxy-leases

Conversation

@ghostwriternr

@ghostwriternr ghostwriternr commented Jul 1, 2026

Copy link
Copy Markdown
Member

Harden git extension auth proxy

This keeps the git extension extraction while replacing the git-specific core auth hook with a generic extension HTTP proxy lease. Authenticated HTTPS clones are rewritten to an operation-scoped internal proxy URL, credentials stay in Worker-side proxy state, and the cloned repository origin is reset back to the sanitized public URL after checkout.

The lease lifecycle now rejects duplicate operation IDs, rolls back failed registration, and keeps disposal retryable until outbound proxy configuration is refreshed. This avoids stale credential routes and prevents public-host interception such as github.com from overriding user egress policy.

Also updates stale standalone-binary docs and restores the public code-interpreter example image.

Validation:

  • npm run check
  • npm test -w @cloudflare/sandbox

Note: I also tried root npm test, but this machine cannot connect to the Docker daemon needed by @repo/sandbox-execution (Cannot connect to the Docker daemon at unix:///Users/naresh/.colima/default/docker.sock).


Open in Devin Review
@changeset-bot

changeset-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: dba6ca2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@pkg-pr-new

pkg-pr-new Bot commented Jul 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@cloudflare/sandbox@802

commit: dba6ca2

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

📦 Preview Build

Version: 0.0.0-pr-802-dba6ca2a

Install the SDK preview:

npm i https://pkg.pr.new/cloudflare/sandbox-sdk/@cloudflare/sandbox@802

🐳 Docker images were not rebuilt — no container changes detected. Use the latest release images from Docker Hub.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Extension HTTP proxy leases are not cleaned up when the container stops, unlike other outbound interceptions

The in-memory proxy lease map is never cleared on container stop (onStop at packages/sandbox/src/sandbox.ts:2697), so stale entries persist if the Durable Object stays alive across restarts — unlike R2 and S3 credential proxy mounts, which are both explicitly cleaned up in the same method.

Impact: After a container restart, the next authenticated git clone would push stale lease entries into the new container's outbound configuration, wasting resources and potentially confusing diagnostics.

Mechanism: onStop cleans up R2 and S3 proxy mounts but not extension HTTP proxy leases

In packages/sandbox/src/sandbox.ts:2717-2739, the onStop method:

  • Checks for R2 egress mounts and calls configureR2EgressOutbound({ buckets: {} }) (line 2731)
  • Checks for credential proxy mounts and calls configureS3CredentialProxyOutbound({ mounts: {} }) (line 2734)
  • Clears this.activeMounts (line 2739)

But there is no equivalent cleanup for this.extensionHTTPProxyLeases (declared at packages/sandbox/src/sandbox.ts:974-978). If a withHTTPProxyLease operation is in-flight when onStop fires, the finally block in packages/sandbox/src/extensions/index.ts:178-179 tries lease.dispose(), which calls configureExtensionHTTPProxyOutbound(). That call can fail because the container is no longer running, leaving the lease entry stranded in the map. On the next container start, registerExtensionHTTPProxyLease will include those stale entries when calling configureExtensionHTTPProxyOutbound (packages/sandbox/src/sandbox.ts:5214).

The fix should add to onStop:

if (this.extensionHTTPProxyLeases.size > 0) {
  this.extensionHTTPProxyLeases.clear();
  await this.configureExtensionHTTPProxyOutbound().catch(() => {});
}

(Refers to lines 2738-2739)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +16 to +18
function isRedirect(status: number): boolean {
return status >= 300 && status < 400;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Non-redirect 3xx responses (e.g. 304 Not Modified) are misclassified as redirects and converted to 502 errors

Any non-redirect 3xx status from the upstream (like 304 Not Modified) is treated as a redirect (isRedirect at packages/sandbox/src/extensions/http-proxy-handler.ts:17) and, because it typically lacks a Location header, the proxy returns a 502 error instead of forwarding it.

Impact: Conditional responses from an upstream server are converted into hard errors, which could break caching and cause spurious failures for any extension using this proxy.

Mechanism: isRedirect is over-broad — matches all 3xx, not just redirect codes

The function isRedirect at packages/sandbox/src/extensions/http-proxy-handler.ts:16-18 uses status >= 300 && status < 400, which matches 304 Not Modified, 300 Multiple Choices, and 305 Use Proxy. Only 301, 302, 303, 307, and 308 are actual redirects that need Location header rewriting.

When a 304 arrives:

  1. isRedirect(304) returns true (packages/sandbox/src/extensions/http-proxy-handler.ts:92)
  2. upstreamResponse.headers.get('location') is null (304 has no Location)
  3. rewriteRedirectLocation(null, ...) returns null (packages/sandbox/src/extensions/http-proxy-handler.ts:27)
  4. The handler returns 502 (packages/sandbox/src/extensions/http-proxy-handler.ts:104-106)

The correct set of redirect codes is [301, 302, 303, 307, 308].

Suggested change
function isRedirect(status: number): boolean {
return status >= 300 && status < 400;
}
function isRedirect(status: number): boolean {
return [301, 302, 303, 307, 308].includes(status);
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant