Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 77 additions & 6 deletions src/commands/auth/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
normalizeOrigin,
normalizeUserInputToOrigin,
} from "../../lib/sentry-urls.js";
import { loadSentryCliRc } from "../../lib/sentryclirc.js";
import {
isLoginTrustAnchorFor,
registerLoginTrustAnchor,
Expand Down Expand Up @@ -115,10 +116,15 @@
* a trusted source (`--url` flag or boot-time env snapshot), so "no
* matching anchor" is the load-bearing signal that the host arrived via
* an untrusted channel.
*
* @param rcSource - Path of the `.sentryclirc` file that provided the URL,
* if that's where the host came from. Used to produce a more actionable
* error message pointing at the specific file.
*/
function refuseLoginToUntrustedHost(
flags: LoginFlags,
effectiveHost: string
effectiveHost: string,
rcSource?: string
): void {
if (
flags.url ||
Expand All @@ -127,11 +133,62 @@
) {
return;
}
const tokenHint = flags.token ? " --token <token>" : "";
const tokenFlag = flags.token ? ` --token ${flags.token.slice(0, 8)}…` : "";

Check warning on line 136 in src/commands/auth/login.ts

View check run for this annotation

@sentry/warden / warden: find-bugs

Partial API token embedded in HostScopeError message

The first 8 characters of `flags.token` are included in the error message thrown when an untrusted host is detected — this is the exact scenario where credential leakage is most dangerous, and the partial token may end up in CI logs, terminal recordings, or bug reports.
Comment thread
betegon marked this conversation as resolved.
Outdated
const sourceClause = rcSource
? `this URL was read from .sentryclirc (${rcSource}) but hasn't been confirmed as trusted yet`
: "--url was not provided";
throw new HostScopeError(
`Refusing to log in against ${effectiveHost} without explicit --url.\n` +
"Pass the host explicitly to confirm you trust it:\n" +
` sentry auth login --url ${effectiveHost}${tokenHint}`
`Refusing to log in against ${effectiveHost} — ${sourceClause}.\n\n` +
"To authenticate against this self-hosted instance, confirm the host explicitly:\n" +
` sentry auth login --url ${effectiveHost}${tokenFlag}`
);
}

/**
* Resolve which `.sentryclirc` file (if any) provided the effective host, and
* return its path alongside the full rc config for downstream use.
*
* Separating this into its own function keeps {@link loginCommand}'s `func`
* complexity within limits.
*/
async function resolveRcContext(
flagUrl: string | undefined,
cwd: string,
effectiveHost: string
): Promise<{
rcConfig: Awaited<ReturnType<typeof loadSentryCliRc>>;
urlFromRc: string | undefined;
}> {
const rcConfig = await loadSentryCliRc(cwd);
const rcUrlNormalized = rcConfig.url
? normalizeOrigin(normalizeUrl(rcConfig.url))
: undefined;
const urlFromRc =
!flagUrl &&
!!rcUrlNormalized &&
normalizeOrigin(effectiveHost) === rcUrlNormalized
? rcConfig.sources.url
: undefined;
return { rcConfig, urlFromRc };
}

/**
* When the user is about to start an OAuth flow but `.sentryclirc` already
* has a token, surface the faster `--token` path as a one-line tip.
*/
function maybeWarnRcToken(
rcConfig: Awaited<ReturnType<typeof loadSentryCliRc>>,
urlFromRc: string | undefined,
effectiveHost: string
): void {
if (!rcConfig.token) {
return;
}
const masked = `${rcConfig.token.slice(0, 8)}…`;
const urlHint = urlFromRc ? ` --url ${effectiveHost}` : "";

Check warning on line 188 in src/commands/auth/login.ts

View check run for this annotation

@sentry/warden / warden: find-bugs

[6VG-Y7B] Partial API token embedded in HostScopeError message (additional location)

The first 8 characters of `flags.token` are included in the error message thrown when an untrusted host is detected — this is the exact scenario where credential leakage is most dangerous, and the partial token may end up in CI logs, terminal recordings, or bug reports.
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
log.info(
`Tip: Found a token in .sentryclirc (${rcConfig.sources.token}). ` +
`To use it: sentry auth login --token ${masked}${urlHint}`
);
}
Comment thread
betegon marked this conversation as resolved.

Expand Down Expand Up @@ -287,7 +344,16 @@
// requested instance. Default URL persistence is deferred until login
// succeeds — see persistLoginUrlAsDefault calls below.
const effectiveHost = applyLoginUrl(flags.url);
refuseLoginToUntrustedHost(flags, effectiveHost);

// Check whether the effective URL came from .sentryclirc so we can name
// the source file in trust-refusal errors and show a migration tip.
const { rcConfig, urlFromRc } = await resolveRcContext(
flags.url,
process.cwd(),
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
effectiveHost
);

refuseLoginToUntrustedHost(flags, effectiveHost, urlFromRc);

// Check if already authenticated and handle re-authentication
if (isAuthenticated()) {
Expand All @@ -297,6 +363,11 @@
}
}

// If going through the OAuth flow but .sentryclirc has a token, tip the user.
if (!flags.token) {
maybeWarnRcToken(rcConfig, urlFromRc, effectiveHost);
}

// Clear stale cached responses from a previous session
try {
await clearResponseCache();
Expand Down
Loading