Skip to content

Allow public extension installs past SAML enforcement - #14108

Open
loganrosen wants to merge 3 commits into
cli:trunkfrom
loganrosen:loganrosen-fix-extension-saml-install
Open

Allow public extension installs past SAML enforcement#14108
loganrosen wants to merge 3 commits into
cli:trunkfrom
loganrosen:loganrosen-fix-extension-saml-install

Conversation

@loganrosen

@loganrosen loganrosen commented Aug 8, 2026

Copy link
Copy Markdown

Fixes #6675

Description

Organization SAML enforcement can make an otherwise valid token return a 403 when GitHub CLI reads release metadata for a public extension. This prevents commands such as gh extension install github/gh-net, even though the same release is publicly downloadable without authentication.

Extension release requests now remain authenticated by default, but retry without authentication when the response is specifically a SAML-enforced 403 and the repository is confirmed to be public. The selected client is then reused for release metadata and asset downloads. Private repositories and non-SAML failures continue to return the original authenticated error.

The change covers binary and script installs, pinned releases, upgrades, and Git-to-binary migration. It also keeps SSO response-header handling request-scoped so a recovered extension request cannot produce stale authorization guidance elsewhere.

How did you test this change?

  • go test ./pkg/cmd/extension/... - passed
  • go test -race ./pkg/cmd/extension/... - passed
  • env -u GH_TOKEN -u GH_HOST -u GIT_CONFIG_COUNT -u GIT_CONFIG_KEY_0 -u GIT_CONFIG_VALUE_0 go test ./... - passed
  • go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.0 run ./... - 0 issues.

I also reproduced the same failure mode with an active token: GET /repos/github/gh-stack/releases/latest returned 403 with X-GitHub-SSO. Using the built binary and that same token, an isolated gh extension install github/gh-stack installed v0.1.0 successfully, and gh stack --help ran successfully.

Key points

  • Anonymous fallback is limited to 403 responses carrying X-GitHub-SSO and only proceeds after an unauthenticated repository check succeeds.
  • The authenticated and anonymous clients remain separate; successful authenticated requests are unchanged.
  • Binary release lookups cache both the selected client and release metadata to avoid duplicate SAML failures and anonymous API requests.
  • Git extension version failures retain their existing retry and error behavior.

Notes for reviewers

Start with pkg/cmd/extension/http.go for fallback selection, then pkg/cmd/extension/manager.go for client and release reuse. pkg/cmd/factory/default.go and internal/ghcmd/cmd.go contain the request-scoped SSO handling.

Issue #6675 contains the original github/gh-net reproduction. github/gh-stack was used to validate the same failure mode and fix against the current implementation.

Authorship and follow-up

Who wrote this:

  • A human wrote it.
  • An agent wrote it under close human direction.
  • An agent wrote it independently, and no human has guided the implementation beyond the initial prompt.

Who answers review comments:

  • @username will read and reply directly. Name the account.
  • An agent will draft replies and @loganrosen will read them before they are posted.
  • Nobody has explicitly committed to replying.

loganrosen and others added 3 commits August 6, 2026 23:27
Retry read-only extension release requests without authentication when a SAML-protected token blocks a public repository. Preserve private repository errors and propagate upgrade metadata failures.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve retry behavior for Git extension version checks while caching binary release results for install and upgrade reuse.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cover anonymous tag metadata and asset download while preserving the pinned manifest state.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 8, 2026 22:01
@loganrosen
loganrosen requested a review from a team as a code owner August 8, 2026 22:01
@loganrosen
loganrosen requested a review from sergiou87 August 8, 2026 22:01
@github-actions github-actions Bot added unmet-requirements external pull request originating outside of the CLI core team needs-triage needs to be reviewed labels Aug 8, 2026
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Thanks for your pull request! This is a large change (764 lines across 10 files) that doesn't reference a help wanted issue.

Large feature PRs require prior discussion in an issue before implementation — this helps the team assess whether the feature aligns with the project's direction before significant effort is invested.

Please open an issue to discuss this feature first. This PR will be automatically closed in 2 days if requirements are not met.

Full contribution requirements
  1. Include a detailed description of what this PR does
  2. Link to an issue with the help wanted label (use Fixes #123 or Closes #123)

Copilot AI 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.

Pull request overview

This pull request updates the extension installation and upgrade flows to recover from organization SAML enforcement that blocks authenticated API requests for public extension repositories, by retrying specific release/repo lookups without authentication and reusing the selected client for subsequent release/asset operations. It also makes SSO challenge URL handling effectively request-scoped for extension traffic, avoiding stale SSO guidance leaking into unrelated errors.

Changes:

  • Add a SAML-enforced 403 fallback path for extension release lookups that retries anonymously only after confirming the repo is publicly accessible.
  • Propagate and reuse the selected HTTP client (authenticated vs anonymous) across release metadata reads and asset downloads, including upgrades and git→binary migration.
  • Make SSO URL extraction prefer per-error response headers and introduce a transport wrapper to skip global header extraction for extension requests.
Show a summary per file
File Description
pkg/cmd/factory/default.go Uses an extension-scoped HTTP client that skips global X-GitHub-SSO extraction; adds SSOURLFromHeader helper and wires a plain (unauthenticated) client into the extension manager.
pkg/cmd/extension/manager.go Threads plain client support through install/upgrade paths; reuses selected client and cached release data to avoid repeated SAML failures and duplicate requests.
pkg/cmd/extension/manager_test.go Adds coverage for SAML fallback behavior across install/upgrade, including public vs private behavior and git-version retry behavior.
pkg/cmd/extension/http.go Introduces fetch*WithFallback helpers and SAML-protected detection to retry release fetches anonymously after confirming public accessibility.
pkg/cmd/extension/http_test.go Adds focused tests for release fallback selection plus a reusable SAML-protected responder.
pkg/cmd/extension/extension.go Caches latest release + selected client and preserves retry semantics for git extensions while avoiding repeated binary lookup failures.
internal/ghcmd/cmd.go Derives SSO recovery URL from the error’s response headers when available, avoiding reliance on global extracted state for HTTP errors.
internal/ghcmd/cmd_test.go Adds test coverage for the new ssoRecoveryURL behavior.
api/http_client.go Adds SkipHeaderExtraction and updates ExtractHeader to honor request-scoped skipping via context.
api/http_client_test.go Adds test ensuring SkipHeaderExtraction prevents extraction without removing the response header itself.

Review details

Tip

Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 10/10 changed files
  • Comments generated: 0
  • Review effort level: Lite

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

Labels

external pull request originating outside of the CLI core team needs-triage needs to be reviewed unmet-requirements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SAML enforcement prohibits installing public extension

2 participants