Commit graph

3 commits

Author SHA1 Message Date
ozymandiashh
1b4c00f8d5 fix(menubar): follow GH_HOST and gh's own host for the Copilot env and gh rungs
#1286 taught the credential files to carry the GitHub host their token came
from, but the environment and `gh auth token` rungs carried none, so they were
always addressed to api.github.com. A user whose only Copilot credential is a
GitHub Enterprise Cloud login — GH_TOKEN exported alongside GH_HOST, or
`gh auth login --hostname <tenant>.ghe.com` — got a 401 and a terminal failure
with nothing to act on.

The environment rung now reads GH_HOST out of the same environment as the
token. The gh rung resolves the host from gh's own hosts.yml, found via
GH_CONFIG_DIR, then XDG_CONFIG_HOME/gh, then ~/.config/gh, and picked the way
gh picks the host for `gh auth token`: GH_HOST, else the single configured
host, else dotcom. That is a file read rather than a second `gh auth status`
spawn, because gh writes hosts.yml for every login, keyring-backed ones
included.

Both are new untrusted inputs, so they stay raw all the way to
`apiHost(for:)`, where #1286's character validation refuses a crafted value
such as `evil.com?.ghe.com` before any URL exists — that host passes the
`.ghe.com` suffix check but builds a URL whose host is api.evil.com, which
would then receive the Authorization header.
2026-09-12 12:02:13 -07:00
iamtoruk
0225088984 fix(quota): reject a Copilot host carrying a URL delimiter
The `.ghe.com` suffix check ran on a normalized string that still allowed
`?`, `#`, `\` and spaces, and that string was interpolated straight into
the request URL. `evil.com?.ghe.com` in a hosts.json key passes the
suffix check but builds a URL whose host is `api.evil.com`, so the
Authorization header lands on a host that was never approved. Verified
on macOS: URL(string:) parses it as host `api.evil.com`, path `/`.

Both readers now require the normalized host to look like a DNS name
before an endpoint is derived; anything else fails as an unsupported
host, with no request sent.
2026-09-12 06:14:26 -07:00
ozymandiashh
8b5bd3ed4c fix(menubar): follow the credential's GitHub host for Copilot quota
Both Copilot quota readers hardcoded
https://api.github.com/copilot_internal/user and dropped the host their
credential was read from, so an enterprise on a GitHub Enterprise Cloud
data-residency host (<tenant>.ghe.com) could only ever report
"Temporarily unavailable": its token is not a dotcom token, and dotcom is
not an endpoint it may be sent to.

A discovered credential now carries its host. hosts.json is keyed by
GitHub host and newer apps.json files key by "<host>:<app id>", so the
token and the host always come from the same entry; the rungs that carry
no host of their own (an app-name apps.json key, COPILOT_GITHUB_TOKEN /
GH_TOKEN / GITHUB_TOKEN, gh auth token, a pasted token) mean dotcom. The
endpoint follows the host: api.github.com for github.com, and
api.<tenant>.ghe.com for an Enterprise Cloud host. With several hosts
signed in github.com wins, otherwise the first .ghe.com tenant in sorted
order, which keeps the pick stable across reads; with exactly one, that
host is used.

A host neither rule can address, such as a self-hosted GitHub Enterprise
Server install, fails with a message naming that host instead of falling
back to dotcom, and network and HTTP failures name the host that was
tried. macOS Settings now says which host answered in the Copilot
connection row. Tokens are still never logged and sanitizeForUI is
unchanged.

Closes #1286
2026-09-12 05:56:05 -07:00