Skip to content

fix: send auth header for GitHub release lookup in installer and self-update#1588

Merged
danielmeppiel merged 2 commits into
mainfrom
autopilot/1582-auth-release-lookup
Jun 2, 2026
Merged

fix: send auth header for GitHub release lookup in installer and self-update#1588
danielmeppiel merged 2 commits into
mainfrom
autopilot/1582-auth-release-lookup

Conversation

@danielmeppiel

Copy link
Copy Markdown
Collaborator

TL;DR

Two anonymous GitHub API calls caused rate-limit failures (403) on shared IPs and corporate NAT. Both now send a conditional Authorization header when a token is present, with anonymous fallback preserved.

Closes #1582

Problem

install.sh resolved AUTH_HEADER_VALUE (from GITHUB_APM_PAT / GITHUB_TOKEN) around line 213, but the first releases/latest curl call around line 232 ignored it and ran anonymously. Under rate-limiting the first call would fail, triggering the private-repo fallback unnecessarily.

src/apm_cli/utils/version_checker.py get_latest_version_from_github called requests.get with no headers; self_update.py calls this function, so apm self-update also hit the rate limit silently.

Approach

  • install.sh: conditional first curl -- send Authorization: token $AUTH_HEADER_VALUE when already resolved; anonymous otherwise. Anonymous fallback + private-repo retry path unchanged.
  • version_checker.py: add _get_github_token() following canonical TOKEN_PRECEDENCE["modules"] order (GITHUB_APM_PAT -> GITHUB_TOKEN -> GH_TOKEN); pass header in requests.get only when a token is present.

Implementation

install.sh (around line 231):

if [ -n "$AUTH_HEADER_VALUE" ]; then
    LATEST_RELEASE=$(curl -s -H "Authorization: token $AUTH_HEADER_VALUE" ...)
else
    LATEST_RELEASE=$(curl -s ...)
fi

version_checker.py:

def _get_github_token() -> str | None:
    return (
        os.environ.get("GITHUB_APM_PAT")
        or os.environ.get("GITHUB_TOKEN")
        or os.environ.get("GH_TOKEN")
        or None
    )
# ...
token = _get_github_token()
headers = {"Authorization": f"token {token}"} if token else {}
response = requests.get(url, headers=headers, timeout=timeout)

Auth guardrails

  • Token precedence: GITHUB_APM_PAT > GITHUB_TOKEN > GH_TOKEN (mirrors TOKEN_PRECEDENCE["modules"] in core/token_manager.py). No new env vars.
  • Token value is never logged, echoed, or included in any exception text.
  • Anonymous fallback preserved when no token is set (header is omitted, not empty).
  • scripts/lint-auth-signals.sh passes (no get_bearer_provider or unannotated git ls-remote violations).

Validation evidence

$ uv run --extra dev ruff check src/ tests/ && uv run --extra dev ruff format --check src/ tests/
All checks passed! / 1161 files already formatted

$ uv run --extra dev python -m pylint --disable=all --enable=R0801 --min-similarity-lines=10 --fail-on=R0801 src/apm_cli/
Your code has been rated at 10.00/10

$ bash scripts/lint-auth-signals.sh
[+] auth-signal lint clean

$ bash -n install.sh
(exit 0)

$ uv run --extra dev pytest tests/unit/test_version_checker.py -v
34 passed in 2.44s

New tests (TestGitHubTokenResolution, TestGitHubVersionFetchAuth):

  • Assert Authorization header is sent only when a token is present
  • Assert 403 rate-limit response with no token returns None gracefully
  • Assert token value does not appear in any log/exception output
  • Assert 200 with token returns the version string

…-update

Two anonymous GitHub release API calls caused rate-limit failures (403)
on shared IPs and corporate NAT:

(a) install.sh - the initial releases/latest curl ran anonymously even
    though AUTH_HEADER_VALUE (GITHUB_APM_PAT > GITHUB_TOKEN) was
    resolved above. Now sends the header on the first attempt when set,
    preserving anonymous fallback when no token is configured.

(b) src/apm_cli/utils/version_checker.py - get_latest_version_from_github
    called requests.get with no Authorization header. Adds _get_github_token()
    following canonical TOKEN_PRECEDENCE["modules"] order:
    GITHUB_APM_PAT -> GITHUB_TOKEN -> GH_TOKEN. Header is sent only when a
    token is present; anonymous fallback unchanged when none is set.

Auth guardrails:
- Token value is never logged, echoed, or included in exception text.
- Anonymous fallback preserved when no token env var is set.
- No new env vars introduced; existing precedence preserved.
- scripts/lint-auth-signals.sh passes (no get_bearer_provider or
  unannotated git ls-remote violations introduced).

Closes #1582

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 2, 2026 14:11
@danielmeppiel danielmeppiel added the status/shepherding Actively being driven by an APM shepherd run label Jun 2, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes anonymous GitHub releases/latest lookups during installation and apm self-update by conditionally sending an Authorization header when a GitHub token is available, preserving anonymous fallback when no token is set. This directly addresses rate-limit (403) failures seen on shared IPs/corporate NAT.

Changes:

  • Update install.sh to include Authorization: token ... on the initial releases/latest curl request when a token is already resolved.
  • Update get_latest_version_from_github() to add an auth header when GITHUB_APM_PAT / GITHUB_TOKEN / GH_TOKEN is present (canonical precedence).
  • Extend unit tests to cover token precedence and header emission behavior.
Show a summary per file
File Description
install.sh Sends auth header on the first GitHub release metadata request when a token is available, avoiding anonymous rate limits.
src/apm_cli/utils/version_checker.py Adds token resolution helper and passes conditional Authorization header for GitHub release lookup used by self-update.
tests/unit/test_version_checker.py Adds tests validating token precedence and that auth headers are included/omitted correctly.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 1

Comment thread src/apm_cli/utils/version_checker.py Outdated
Comment on lines 6 to 7
from pathlib import Path
from typing import Optional, Tuple # noqa: F401, UP035
…back, add CHANGELOG entry

- Remove `from typing import Optional, Tuple  # noqa: F401, UP035` from
  version_checker.py: symbols are unused (file uses PEP 604 str|None and
  builtin tuple); the noqa suppressor was masking dead code
- Add GH_TOKEN as third fallback in install.sh auth resolution block for
  consistency with version_checker.py TOKEN_PRECEDENCE['modules'] order:
  GITHUB_APM_PAT > GITHUB_TOKEN > GH_TOKEN
- Add CHANGELOG entry for the rate-limit fix (closes #1582)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel

Copy link
Copy Markdown
Collaborator Author

apm-review-panel advisory (shepherd-driver, 1 iteration)

Verdict: ship_with_followups -- CI green, auth safe, two items folded, one deferred.


Panel findings

python-architect

  • [FOLDED] from typing import Optional, Tuple # noqa: F401, UP035 removed from version_checker.py -- dead code on a touched file; noqa suppressor was masking it.
  • [MINOR] Add a sync comment in _get_github_token() referencing GitHubTokenManager.TOKEN_PRECEDENCE["modules"] so future readers know the order is canonical. Not blocking; defer to a follow-up if desired.

auth-expert

  • Direct os.environ.get() in version_checker.py is ACCEPTABLE. version_checker is a lightweight non-blocking utility (2 s timeout, fail-gracefully) distinct from AuthResolver which is designed for git/dep operations with (host, org) context and heavier dependencies. Pattern stays out of scope for lint-auth-signals.sh checks.
  • [FOLDED] install.sh initial auth block now includes GH_TOKEN as third fallback, consistent with version_checker.py TOKEN_PRECEDENCE order: GITHUB_APM_PAT > GITHUB_TOKEN > GH_TOKEN.

test-coverage-expert

  • COMPLETE. All precedence paths, anonymous fallback, token-leak prevention, and 403-response trap are covered at unit tier in TestGitHubTokenResolution and TestGitHubVersionFetchAuth. No gaps.

supply-chain-security-expert

  • Minimal risk introduced. API calls pin to api.github.com. Three pre-existing hardening gaps noted (not introduced by this PR):
    • bash -x / xtrace exposure of token in install.sh (pre-existing)
    • GITHUB_URL override without validation (pre-existing)
    • ASSET_URL domain not validated against github.com (pre-existing)
  • [DEFERRED] These are cross-cutting install.sh hardening concerns unrelated to the auth-header fix; tracked for a follow-up security audit.

cli-logging-expert

  • CLEAN. No token leakage in log output. Authorization header set in-memory and never echoed.

devx-ux-expert

  • CLEAN. Fully transparent to end users; anonymous fallback preserved; no new flags or prompts required.

oss-growth-hacker

  • POSITIVE signal. Fixes a recurring pain point for users on shared IPs / corporate NAT hitting anonymous rate limits during install. No docs change needed; fix is self-contained.

Items folded into this PR

Item Source File
Remove dead from typing import Optional, Tuple # noqa: F401, UP035 Copilot LEGIT + python-architect src/apm_cli/utils/version_checker.py
Add GH_TOKEN as third fallback in initial auth block auth-expert nit install.sh
CHANGELOG entry under [Unreleased] ### Added fold-vs-defer rubric (mandatory) CHANGELOG.md

Items deferred (follow-up issues recommended)

Item Reason
install.sh supply-chain hardening (xtrace, GITHUB_URL, ASSET_URL) Pre-existing patterns not introduced by this PR; cross-cutting security audit out of scope
_get_github_token() sync comment referencing TOKEN_PRECEDENCE Cosmetic nit; not blocking

@danielmeppiel danielmeppiel removed the status/shepherding Actively being driven by an APM shepherd run label Jun 2, 2026
@danielmeppiel danielmeppiel merged commit 5d77e5f into main Jun 2, 2026
14 checks passed
@danielmeppiel danielmeppiel deleted the autopilot/1582-auth-release-lookup branch June 2, 2026 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Installer and self-update ignore token env vars for GitHub release lookup

2 participants