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

Description

Hardens how glab uses credentials from environment variables. Before this change, an environment-provided token (GITLAB_TOKEN/GITLAB_ACCESS_TOKEN/OAUTH_TOKEN, and CI_JOB_TOKEN under CI auto-login) was sent to whichever host a command targeted, including a host named only in a command argument:

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

The affected path is specifically an explicit host argument (a URL argument or -R), which bypasses remote resolution through RepoOverride. Git remotes were never affected: on main, a remote pointing at an unrecognized host is already refused ("None of the git remotes configured for this repository point to a known GitLab host").

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, which is the global host key: from GITLAB_HOST/GITLAB_URI/GL_HOST, the configuration file, or CI_SERVER_FQDN under CI auto-login, falling back to gitlab.com. Setting GITLAB_HOST alone is enough: no glab auth login or configuration file is needed. A subfolder in the default host (for example GITLAB_HOST=https://example.com/gitlab) is supported.
    • any host present in the configuration's hosts map.

    This mirrors the approach gh took in 2.63.0. A new exported config.IsTrustedHost holds the check.

  • internal/cmdutils/factory.go: ApiClient prints a stderr warning when a token or job token environment variable is set but was withheld for the target host, with 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 which hosts receive an environment token.

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

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

Closes #8545

How has this been tested?

  • Unit tests in internal/config (Test_GetWithSource_CredentialEnvVarsRequireTrustedHost, TestIsTrustedHost) cover: default-host fallback to gitlab.com, hosts-map membership, default host from the configuration file and from GITLAB_HOST, a subfolder in the default 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.
  • TestNewClientFromConfig_EnvTokenScopedToTrustedHosts in internal/api pins that a client for an unconfigured host carries no credential, while trusted hosts still receive the environment token.
  • TestFactory_ApiClientEnvTokenWarning in internal/cmdutils pins the stderr warning.
  • Existing tests that encoded the old behavior (internal/api/client_test.go, internal/api/oauth_provenance_test.go) now make their hosts trusted through GITLAB_HOST or the CI server variables.
  • The lefthook pre-push suite passes: build, lint, tests, the generated-docs check, and markdownlint, vale, and lychee on the regenerated documentation.
  • End-to-end against a local server that records inbound credential headers (hosts: {}, token only in the environment, host passed as a command argument):
    • before this change: the server receives the token.
    • with this change: no credential is sent, the warning is printed, and the command still returns the project data.
    • control: setting GITLAB_HOST to the server's address makes it the trusted default host, and the token is sent as before.

Screenshots (if appropriate):

N/A

Edited by Dinesh Bolkensteyn

Merge request reports

Loading
Loading