Repository navigation
fix(copilot): resolve the gh host and key every credential source and the endpoint to it - #1438
Conversation
… the endpoint to it
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
There was a problem hiding this comment.
All reported issues were addressed across 1 file
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
junhoyeo
left a comment
There was a problem hiding this comment.
Requesting changes on 3def6c038c82e94474a33b18a376f586556edfca.
1. Host classification and request destination can select different credential audiences
normalize_gh_host accepts github.com:443, but is_dotcom_style_host compares the port-bearing string against github.com. This chooses GH_ENTERPRISE_TOKEN/GITHUB_ENTERPRISE_TOKEN, then constructs https://api.github.com:443/copilot_internal/user. Rust's URL parser canonicalizes that destination to https://api.github.com, and fetch_api attaches the selected enterprise credential to the request. The currently accepted malformed spelling github.com: takes the same path. This defeats the host-isolation guarantee this PR is meant to provide (copilot.rs:57-73,134-155,541-546,615-624).
Please reject ports entirely, or parse and validate the hostname/port once and classify credentials using the hostname without the port. Add regressions for github.com:443, tenant.ghe.com:443, empty and nonnumeric ports, and multiple-colon inputs. This was verified with source tracing and a no-network Rust URL probe; no real credentials or provider API calls were used.
2. Valid YAML comments break the enterprise fallback
Both host enumeration and token lookup require the host line to end exactly in :. A valid ghe.example.com: # comment section is therefore missed; oauth_token: fixture_token # comment instead includes the comment in the credential (copilot.rs:78-98,499-523). Please parse hosts.yml as a YAML mapping and select the requested host's top-level active token. Cover commented/quoted host keys and inline token comments.
Validation: the existing focused Copilot tests passed 22/22, formatting and diff checks passed. They do not cover these failure cases. The upstream Rust workflow runs are still awaiting maintainer authorization, so the lightweight green checks are not a Rust CI pass.
Address review on junhoyeo#1438. - normalize_gh_host now parses a DNS hostname plus at most one port in 1..=65535, writes the port back canonically and drops :443. Empty, non-numeric and multi-colon ports, empty labels and trailing dots are rejected. github.com:443 is github.com both when the credential is picked and when the URL is built. - is_dotcom_style_host classifies on the hostname without the port. - hosts.yml is read as a block mapping: quoted keys and values, inline comments, CRLF. The token is the host's direct oauth_token child, not one nested under users:.
|
Thanks for the careful review. Both points are fixed in c7ee54c. 1. Host and port.
2. hosts.yml. It is now read as a block mapping by a small line parser. It handles quoted keys and values, inline comments ( Regression tests cover
|
junhoyeo
left a comment
There was a problem hiding this comment.
Thanks for the careful follow-up. The port validation, hostname-only audience classification, and the comment/quoted-key cases from the earlier review are now covered well.
Two small items remain before this is ready:
-
crates/tokscale-cli/src/commands/usage/copilot.rs:267-271always guesseshttps://api.{host}.ghsupports a per-hostapi_hostmapping for API transport, so this can send the Copilot request to a guessed endpoint instead of the configured one. Please resolve the logical host's optionalapi_hostwhen building the request URL, while continuing to select credentials by the logical host, and add a no-network regression where those two hosts differ. -
crates/tokscale-cli/src/commands/usage/copilot.rs:120-195and:620-637implement a handwritten line subset rather than parsing the YAML mapping requested in the previous review. It handles the listed examples, but valid YAML mapping features such as aliases or merge keys can yield a wrong active token. Please deserializehosts.ymlas a mapping, select only the requested host's direct activeoauth_token, and add coverage for that case as well as the existing comments/quotes and nesteduserscase.
The remaining minor compatibility items will be handled by maintainers in a follow-up after merge. Thanks again for carrying this through.
|
Thanks again for the solid follow-up. I pushed
The formatter, |
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="crates/tokscale-cli/src/commands/usage/copilot.rs">
<violation number="1" location="crates/tokscale-cli/src/commands/usage/copilot.rs:147">
P2: Equivalent keys such as `ghe.example.com` and `ghe.example.com:443` both normalize to the same host but remain duplicated here. `select_gh_host` then treats the sole logical enterprise host as multiple and falls back to `github.com`; deduplicate normalized hosts before returning.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
View guided diff | Re-trigger cubic
| .keys() | ||
| .filter_map(yaml_serde::Value::as_str) | ||
| .filter_map(normalize_gh_host) | ||
| .collect() |
There was a problem hiding this comment.
P2: Equivalent keys such as ghe.example.com and ghe.example.com:443 both normalize to the same host but remain duplicated here. select_gh_host then treats the sole logical enterprise host as multiple and falls back to github.com; deduplicate normalized hosts before returning.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At crates/tokscale-cli/src/commands/usage/copilot.rs, line 147:
<comment>Equivalent keys such as `ghe.example.com` and `ghe.example.com:443` both normalize to the same host but remain duplicated here. `select_gh_host` then treats the sole logical enterprise host as multiple and falls back to `github.com`; deduplicate normalized hosts before returning.</comment>
<file context>
@@ -117,98 +117,63 @@ fn gh_host_name(host: &str) -> &str {
+ .keys()
+ .filter_map(yaml_serde::Value::as_str)
+ .filter_map(normalize_gh_host)
+ .collect()
}
</file context>
| .collect() | |
| .fold(Vec::new(), |mut hosts, host| { | |
| if !hosts.contains(&host) { | |
| hosts.push(host); | |
| } | |
| hosts | |
| }) |
junhoyeo
left a comment
There was a problem hiding this comment.
Thanks for the contribution and for the thorough follow-up. The maintainer completion is now covered by the full cross-platform CI matrix, and everything is green. Approved — I’ll squash-merge this now.
Fixes #1260.
The Copilot usage provider was pinned to
github.comin four places: the keyring service name, thehosts.ymlsection it read, the API URL, and the token environment variables. This PR resolves one host and keys all four to it, so a token can only go to the host that issued it.Host resolution (same order as
gh)GH_HOST(normalised: scheme, trailing slash and case are stripped, and anything that is not a barehost[:port]is ignored) → the only host inhosts.yml→github.com. Ifhosts.ymllists several hosts andGH_HOSTis unset, it usesgithub.com, which is whatghdoes too.What is keyed to the host
gh:github.comgh:<host>(wincred targetsgh:<host>:,gh:<host>)hosts.ymlgithub.com:section<host>:section; an enterprise-only file now worksGH_TOKEN/GITHUB_TOKENalwaysgithub.comand*.ghe.com:GH_TOKEN/GITHUB_TOKEN; GHES:GH_ENTERPRISE_TOKEN/GITHUB_ENTERPRISE_TOKEN(go-ghTokenForHost)https://api.github.com/copilot_internal/userhttps://api.<host>/copilot_internal/userThe endpoint uses the shape the official Copilot language server builds for an enterprise URI,
api.<host>, as @aron-intframe found in@github/copilot-language-server. Forgithub.comit gives the same URL as before.The leak described in the issue is closed in both directions. A github.com
GH_TOKENis never read for an enterprise host, and an enterprise login is never sent toapi.github.com. The probe output also reports the resolved host and endpoint (never token values), and the error messages give the host and the right variables, for examplegh auth login --hostname <host>.Verification (Linux, CPU)
GH_HOST,hosts.ymlhost listing, per-host token extraction including the enterprise-only file from the issue, keyring/endpoint/wincred composition, env vars that never cross hosts, and an end-to-end check throughGH_CONFIG_DIR+GH_HOSTthat also confirms the probe does not leak the token.cargo test -p tokscale-cli: 1471 passed, 1 ignored (bin) / 192 / 8 / 1, 0 failed.cargo clippy -p tokscale-cli --all-targets -- -D warningsandcargo fmt --checkare clean.Caveats
api.<ghes-host>/copilot_internal/useris untested. That needs subdomain isolation on the appliance. The URL builder is one function (copilot_user_url), so switching to/api/v3later is a one-line change.hostargument.Summary by cubic
Fixes #1260. The Copilot usage provider was pinned to
github.comin four places: the keyring service name, thehosts.ymlsection it read, the API URL, and the token environment variables. It now resolves one host and keys all four to it, so a token can only go to the host that issued it.The host is resolved the same way
ghdoes:GH_HOST→ the only host inhosts.yml→github.com.GH_HOSTand configured hosts are parsed as a DNS name plus at most one port in1..=65535, with:443dropped so a key and its URL always share one spelling; anything else is ignored rather than spliced into a URL.hosts.ymlis parsed withyaml_serdeas a block mapping (quoted keys and values, inline comments, CRLF, aliases), and the token is read from the host's directoauth_tokenchild, not one nested underusers:. An optionalapi_hostrecord routes the API request to a different transport without rekeying credentials, which stay bound to the logical host.What is keyed to the host
gh:github.com→gh:<host>hosts.yml: only a literalgithub.com:section → the<host>:section, so an enterprise-only file now worksGH_TOKEN/GITHUB_TOKEN→GH_TOKEN/GITHUB_TOKENfor github.com and*.ghe.com,GH_ENTERPRISE_TOKEN/GITHUB_ENTERPRISE_TOKENfor GHEShttps://api.github.com/copilot_internal/user→https://api.<host>/copilot_internal/user(or the configuredapi_host), the shape the official Copilot language server builds for an enterprise URIThe leak is closed in both directions: a github.com
GH_TOKENis never read for an enterprise host, and an enterprise login is never sent toapi.github.com. The probe reports the resolved host and endpoint (never token values), and errors name the host and the right variables, e.g.gh auth login --hostname <host>.Caveats
/copilot_internal/userendpoint is untested; that needs subdomain isolation on the appliance.hostargument.Written for commit c7a4ec9. Summary will update on new commits.