From 77f2f0e96caa46cdbb9927723c11a41135683942 Mon Sep 17 00:00:00 2001 From: kostua16 Date: Fri, 3 Jul 2026 11:22:13 +0500 Subject: [PATCH] fix: clear gh token env when switching account --- .codegraph/.gitignore | 5 ++++ .github/workflows/test-action.yml | 46 +++++++++++++++++++++++++++++-- README.md | 9 +++--- action.yml | 6 ++-- src/main.ts | 20 ++++++++++++-- 5 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 .codegraph/.gitignore diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore new file mode 100644 index 0000000..d20c0fe --- /dev/null +++ b/.codegraph/.gitignore @@ -0,0 +1,5 @@ +# CodeGraph data files — local to each machine, not for committing. +# Ignore everything in .codegraph/ except this file itself, so transient +# files (the database, daemon.pid, sockets, logs) never show up in git. +* +!.gitignore diff --git a/.github/workflows/test-action.yml b/.github/workflows/test-action.yml index a3c2f78..652969b 100644 --- a/.github/workflows/test-action.yml +++ b/.github/workflows/test-action.yml @@ -65,7 +65,7 @@ jobs: "github-actions[bot]": oauth_token: stale-token YAML - - name: Switch account exports provided token + - name: Switch account uses isolated config id: switch-account uses: ./ env: @@ -73,7 +73,7 @@ jobs: with: token: ${{ github.token }} switch-account: "true" - - run: test -n "$GH_TOKEN" + - run: test -z "${GH_TOKEN:-}" - run: test -n "$GH_CONFIG_DIR" - run: test "$GH_CONFIG_DIR" = "${{ steps.switch-account.outputs.gh-config-dir }}" - run: test "$GH_CONFIG_DIR" != "$HOME/.config/gh" @@ -89,6 +89,48 @@ jobs: - run: test "${{ steps.switch-account.outputs.gh-config-dir }}" != "${{ steps.switch-account-again.outputs.gh-config-dir }}" - run: test "$GH_CONFIG_DIR" != "$HOME/.config/gh" - run: gh auth status --hostname github.com + - name: Mock gh where env token shadows stored auth + run: | + fake_bin="$(mktemp -d)" + cat > "$fake_bin/gh" <<'BASH' + #!/usr/bin/env bash + set -euo pipefail + + case "$*" in + "--version") + echo "gh version 2.93.0 (mock)" + ;; + "auth login --with-token --hostname github.com") + cat >/dev/null + ;; + "auth status --hostname github.com") + if [ -n "${GH_TOKEN:-}" ]; then + echo "github.com" >&2 + echo " X Failed to log in to github.com account github-actions[bot] (GH_TOKEN)" >&2 + echo " - The token in GH_TOKEN is invalid." >&2 + echo " ✓ Logged in to github.com account github-actions[bot] (${GH_CONFIG_DIR}/hosts.yml)" >&2 + exit 1 + fi + echo "github.com" >&2 + echo " ✓ Logged in to github.com account github-actions[bot] (${GH_CONFIG_DIR}/hosts.yml)" >&2 + ;; + *) + echo "unexpected gh invocation: $*" >&2 + exit 2 + ;; + esac + BASH + chmod +x "$fake_bin/gh" + echo "$fake_bin" >> "$GITHUB_PATH" + - name: Switch account clears env-token shadowing + id: env-token-shadowed + uses: ./ + with: + token: mock-token + switch-account: "true" + - run: test "${{ steps.env-token-shadowed.outputs.auth }}" = "true" + - run: test -z "${GH_TOKEN:-}" + - run: gh auth status --hostname github.com - name: Mock gh auth status failure run: | fake_bin="$(mktemp -d)" diff --git a/README.md b/README.md index 88937bf..68c46e8 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,11 @@ jobs: `github.token`. - **`switch-account`:** Whether to switch later `gh` commands to the provided - token, even when another account is already authenticated. This also exports - the provided token as `GH_TOKEN` and a fresh isolated `GH_CONFIG_DIR` for - later workflow steps. Each `switch-account: true` invocation creates its own - config directory. The default is `false`. + token, even when another account is already authenticated. This exports a + fresh isolated `GH_CONFIG_DIR` for later workflow steps and clears gh token + environment variables for the configured host so the isolated config is not + shadowed. Each `switch-account: true` invocation creates its own config + directory. The default is `false`. - **`skip-matching-version`:** If `false`, installation is skipped whenever any `gh` is already on `PATH`. If `true`, installation is skipped only when the diff --git a/action.yml b/action.yml index c980dd8..0637ce2 100644 --- a/action.yml +++ b/action.yml @@ -27,8 +27,10 @@ inputs: switch-account: description: > Whether to switch later 'gh' commands to the provided token, even when - another account is already authenticated. When true, this exports - 'GH_TOKEN' and a fresh isolated 'GH_CONFIG_DIR' for later workflow steps. + another account is already authenticated. When true, this exports a + fresh isolated 'GH_CONFIG_DIR' for later workflow steps and clears gh + token environment variables for the configured host so the isolated + config is not shadowed. default: false skip-matching-version: description: > diff --git a/src/main.ts b/src/main.ts index 0fb01f2..aca882f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -158,7 +158,11 @@ function sanitizeAuthStatusOutput(output: string) { } async function getAuthStatus(hostname: string) { - const result = await $({ reject: false })`gh auth status --hostname ${hostname}`; + const result = await $({ + reject: false, + env: envWithoutGhTokens(), + extendEnv: false, + })`gh auth status --hostname ${hostname}`; const output = sanitizeAuthStatusOutput( [result.stdout, result.stderr].filter(Boolean).join("\n"), ); @@ -179,6 +183,18 @@ async function setAuthOutputFromStatus(hostname: string, warning: string) { return authStatus.ok; } +function ghTokenEnvNamesForHost(hostname: string) { + return hostname === "github.com" + ? ["GH_TOKEN", "GITHUB_TOKEN"] + : ["GH_ENTERPRISE_TOKEN", "GITHUB_ENTERPRISE_TOKEN"]; +} + +function clearGhTokenEnvForHost(hostname: string) { + for (const name of ghTokenEnvNamesForHost(hostname)) { + core.exportVariable(name, ""); + } +} + const installedVersion = await getInstalledVersion(); if (installedVersion && !skipMatchingVersion) { core.info(`Using existing GH CLI ${installedVersion} from PATH`); @@ -214,7 +230,7 @@ if (!token) { ghConfigDir = createGhConfigDir(); core.exportVariable("GH_CONFIG_DIR", ghConfigDir); await loginWithToken(hostname, token); - core.exportVariable("GH_TOKEN", token); + clearGhTokenEnvForHost(hostname); await setAuthOutputFromStatus( hostname, `gh auth status --hostname ${hostname} failed after switch-account login; setting auth=false.`,