fix(auth): apply host-trust gate to auto-login by betegon · Pull Request #1122 · getsentry/cli · GitHub
Skip to content

fix(auth): apply host-trust gate to auto-login#1122

Merged
betegon merged 5 commits into
mainfrom
fix/auto-login-host-guard
Jun 23, 2026
Merged

fix(auth): apply host-trust gate to auto-login#1122
betegon merged 5 commits into
mainfrom
fix/auto-login-host-guard

Conversation

@betegon

@betegon betegon commented Jun 22, 2026

Copy link
Copy Markdown
Member

Summary

Fixes #1121.

sentry auth login refuses to authenticate against an unconfirmed self-hosted host unless --url confirms it. But the auto-login middleware (the error-recovery path that runs the OAuth device flow when any command hits an auth error in an interactive TTY) called runInteractiveLogin() with no such gate. So sentry auth whoami — and any other command — could start an unconfirmed self-hosted login that sentry auth login would have refused:

> sentry auth login          # refused (no --url)
Error: Refusing to log in against https://sentry.example.com — --url was not provided.

> sentry auth whoami         # bug: auto-login proceeds unchecked
Authentication required. Starting login flow...
  URL:  https://sentry.example.com/oauth/device/?user_code=ABCD-EFGH

Because a .sentryclirc shim can inject env.SENTRY_URL, this bypass also reopened the OAuth-phishing vector the auth login gate was added to close (see test/lib/security/login-token-rc-poison.test.ts).

Changes

  • New src/lib/login-host-guard.ts — single source of truth for login host trust: resolveEffectiveLoginHost(), isLoginHostTrusted() (explicit-login gate), isAutoLoginHostTrusted() (auto-login gate), and buildHostRefusalMessage().
  • src/cli.ts — the auto-auth middleware now resolves the effective host and throws HostScopeError with the standard refusal message before any browser opens, if the host isn't auto-login-trusted. Extracted a shouldAutoAuth type-guard to keep the function under the complexity limit.
  • src/commands/auth/login.tsrefuseLoginToUntrustedHost and applyLoginUrl now reuse the shared helpers (no behavior change; refusal wording preserved).

Design note

isAutoLoginHostTrusted is deliberately more lenient than the explicit auth login gate: it additionally trusts the stored token's host. This avoids a regression where a legitimate self-hosted user with an expired session would be forced to re-run auth login --url on every token expiry. An injected host that differs from the stored host (e.g. poisoned env.SENTRY_URLevil.com) is still refused. The reporter's exact case (not_authenticated, no stored token) correctly falls through to refusal, matching the issue's Expected Result.

Testing

  • New test/lib/security/auto-login-host-guard.test.ts (15 cases): env host resolution, the explicit-login gate, the auto-login predicate (bug case refused, expired re-auth allowed, injected host refused, anchor/SaaS trusted), and refusal messages.
  • Full test/lib/security + test/commands/auth + db host suite: 146 passed. Typecheck and biome clean.

🤖 Generated with Claude Code

`sentry auth login` refuses to authenticate against an unconfirmed
self-hosted host unless `--url` is passed, but the auto-login middleware
(triggered when any command hits an auth error in a TTY) called the OAuth
device flow with no such gate. So `sentry auth whoami` — and any other
command — could start an unconfirmed self-hosted login that `auth login`
would have refused (#1121).

Since a `.sentryclirc` shim can inject `env.SENTRY_URL`, the bypass also
reopened the OAuth-phishing vector the `auth login` gate was added to close.

Extract the shared trust logic into `login-host-guard.ts` and call it from
both paths. Auto-login is deliberately a touch more lenient than the
explicit gate: it also trusts the stored token's host, so an expired
self-hosted session can re-auth without forcing `--url` again — but an
injected host that differs from the stored host is still refused.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/lib/login-host-guard.ts Outdated
betegon and others added 2 commits June 22, 2026 17:03
Address review: the stored token's host can't anchor expired-session
re-auth because both expired paths call clearAuth() before the AuthError
reaches the middleware (db/auth.ts refreshToken), so getStoredAuthHost()
is always undefined by the time the guard runs — self-hosted re-auth was
wrongly refused.

Use defaults.url instead: it survives clearAuth() and is only written by
explicit, confirmed user actions (auth login --url, cli defaults url,
confirmed cli import), never by the .sentryclirc env shim. So expired
re-auth against a previously confirmed host proceeds, while an injected
host that doesn't match is still refused.

Also tidy up: move shouldAutoAuth above its JSDoc so the doc attaches to
autoAuthMiddleware, and drop a filler test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Codecov Results 📊

✅ Patch coverage is 81.25%. Project has 5050 uncovered lines.
✅ Project coverage is 81.26%. Comparing base (base) to head (head).

Files with missing lines (1)
File Patch % Lines
src/cli.ts 0.00% ⚠️ 6 Missing
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
+ Coverage    81.24%    81.26%    +0.02%
==========================================
  Files          388       390        +2
  Lines        26936     26954       +18
  Branches     17493     17505       +12
==========================================
+ Hits         21884     21904       +20
- Misses        5052      5050        -2
- Partials      1825      1826        +1

Generated by Codecov Action

Comment thread src/cli.ts Outdated
Move the auto-login recovery flow out of the cli.ts middleware closure into
a dependency-injected module (lib/auto-auth.ts) so the host-trust gate and
login/retry behavior are unit-testable — cli.ts itself has no test coverage,
which left the #1121 fix's patch coverage at 37.5%. The new module is
covered 100%.

Also close a second instance of the same bypass flagged in review:
scopeRecoveryMiddleware (added by #1032) runs the OAuth device flow on a 403
missing-scope error without the host-trust gate, so a .sentryclirc-injected
SENTRY_URL could steer re-auth to an attacker host. Both interactive re-auth
paths now call the shared assertAutoLoginHostTrusted().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e2c9654. Configure here.

Comment thread src/lib/login-host-guard.ts
Address review: isAutoLoginHostTrusted only consulted SaaS, the process
anchor, and defaults.url. On a 403 scope-recovery the OAuth token row is
still present (clearAuth has not run), and a self-hosted user whose effective
host matches their token host but who has no persisted default URL — e.g.
defaults.url persistence failed (it's best-effort) or was cleared — was
wrongly refused.

Also trust the stored token host, which setAuthToken always records. It's the
authoritative anchor when a token exists (scope recovery), complementing
defaults.url which covers the expired path where the token row is already
wiped. isHostTrusted requires an exact host match, so an injected
env.SENTRY_URL pointing elsewhere is still refused.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@betegon betegon requested a review from BYK June 23, 2026 08:41
@betegon betegon merged commit 98cab2f into main Jun 23, 2026
29 checks passed
@betegon betegon deleted the fix/auto-login-host-guard branch June 23, 2026 12:24
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.

Auto-login flow bypasses self-hosted host confirmation

2 participants