Dashboard › cli › Session 15gO9oosDn72
15gO9oosDn72yghk6Perform an independent, read-only security review of the current uncommitted diff for getsentry/cli issue #1568. Do not edit files. Inspect actual git diff and relevant auth, host-scoping, redirect, custom-header code/tests. Threat-model env injection, shadowed inactive tokens, cross-origin redirects, credential/custom-header exfiltration, token claim malformed URLs, SSRF, redirect status/method changes, and trusted regions. Verify fail-closed behavior and whether tests prove no secret-bearing request reaches an untrusted redirect. Return findings first, ordered severity with exact file:line and deterministic regression for every issue. If no flaws, state PASS with residual risks. End exactly MERGE or DO-NOT-MERGE.
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus</path> <type>directory</type> <entries> .craft.yml .git .github/ .gitignore .lore.md .nojekyll .npmrc .opencode/ .plans/ .ruff_cache/ AGENTS.md apps/ CHANGELOG.md codemods/ coverage/ dist-bin/ dist/ docs/ LICENSE.md node_modules/ package.json packages/ pnpm-lock.yaml pnpm-workspace.yaml README.md test-results.junit.xml (26 entries) </entries>
M .lore.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/dashboard.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/event.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/explore.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/feedback.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/issue.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/log.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/replay.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/span.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/trace.md M packages/cli/src/lib/constants.ts M packages/cli/src/lib/region.ts M packages/cli/src/lib/sentry-client.ts M packages/cli/test/lib/constants.test.ts M packages/cli/test/lib/security/sntrys-claim-mismatch.test.ts M packages/cli/test/lib/sentry-client.test.ts
.lore.md | 700 ++++++++++++--------- .../skills/sentry-cli/references/dashboard.md | 2 +- .../skills/sentry-cli/references/event.md | 2 +- .../skills/sentry-cli/references/explore.md | 2 +- .../skills/sentry-cli/references/feedback.md | 2 +- .../skills/sentry-cli/references/issue.md | 4 +- .../sentry-cli/skills/sentry-cli/references/log.md | 2 +- .../skills/sentry-cli/references/replay.md | 2 +- .../skills/sentry-cli/references/span.md | 2 +- .../skills/sentry-cli/references/trace.md | 4 +- packages/cli/src/lib/constants.ts | 8 +- packages/cli/src/lib/region.ts | 10 +- packages/cli/src/lib/sentry-client.ts | 214 ++++++- packages/cli/test/lib/constants.test.ts | 7 + .../lib/security/sntrys-claim-mismatch.test.ts | 190 +++++- packages/cli/test/lib/sentry-client.test.ts | 47 ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_08af18a9f001diZ3keoaUDn3P4
import { applyCustomHeaders } from "./custom-headers.js";
import { getAuthToken, refreshToken } from "./db/auth.js";
import { ApiError, HostScopeError, TimeoutError } from "./errors.js";
import { logger } from "./logger.js";
import {
clearLastCacheHitAge,
getCachedResponse,
invalidateCachedResponsesMatching,
storeCachedResponse,
} from "./response-cache.js";
import { normalizeOrigin } from "./sentry-urls.js";
import { withTracingSpan } from "./telemetry.js";
import { parseSntrysClaim } from "./token-claims.js";
import {
getActiveTokenHost,
isHostTrustedForClaim,
isRequestOriginTrusted,
} from "./token-host.js";
const log = logger.withTag("http");
/** Default request timeout in milliseconds */
const REQUEST_TIMEOUT_MS = 30_000;
/**
* Per-endpoint timeout overrides, matched against the request URL's
* pathname (first ...
<path>/home/byk/.local/share/opencode/tool-output/tool_08af18a9f001diZ3keoaUDn3P4</path>
<type>file</type>
<content>
1: diff --git a/packages/cli/src/lib/constants.ts b/packages/cli/src/lib/constants.ts
2: index 4e707773a..843b7dcb1 100644
3: --- a/packages/cli/src/lib/constants.ts
4: +++ b/packages/cli/src/lib/constants.ts
5: @@ -1,155 +1,155 @@
6: /**
7: * Runtime constants for the CLI.
8: */
9:
10: import { getEnv } from "./env.js";
11:
12: /** Build-time constant injected by esbuild/bun */
13: declare const SENTRY_CLI_VERSION: string | undefined;
14:
15: /**
16: * Build-time debug ID for sourcemap resolution, injected by esbuild.
17: *
18: * During the build, esbuild's `define` replaces this identifier with a
19: * placeholder UUID string literal. After esbuild finishes, the build
20: * script replaces the placeholder with the real debug ID (derived from
21: * the minified JS + sourcemap content hash). ...
<path>/home/byk/.local/share/opencode/tool-output/tool_08af18a9f001diZ3keoaUDn3P4</path>
<type>file</type>
<content>
1491: captureEnvTokenHost();
1492:
1493: // Direct request to sentry.secondhost.com (matches env scope but NOT the claim).
1494: const { apiRequestToRegion } = await import(
1495: "../../../src/lib/api/infrastructure.js"
1496: );
1497: await expect(
1498: apiRequestToRegion("https://sentry.secondhost.com", "/organizations/", {
1499: method: "GET",
1500: })
1501: ).rejects.toThrow(/embedded claim|sentry\.firsthost\.com/i);
1502:
1503: // Token never hit the wire.
1504: const leaked = fetchCalls.filter((c) =>
1505: c.auth?.includes("secret-tail-for-test")
1506: );
1507: expect(leaked).toEqual([]);
1508: });
1509:
1510: test("token whose claim matches the request proceeds normally", async () => {
1511: process.env.SENTRY_AUTH_TOKEN = mintSntrysToken({
1512: iat: ...
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/token-host.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/token-claims.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/sentry-urls.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/sentry-client.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/region.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/env-token-host.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/custom-headers.ts
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/sentry-client.ts</path>
<type>file</type>
<content>
1: /**
2: * Sentry API Client Configuration
3: *
4: * Provides request configuration for @sentry/api SDK functions,
5: * including authentication, retry logic, timeout, and multi-region support.
6: *
7: * Instead of managing client instances, we pass configuration per-request
8: * through the SDK function options (baseUrl, fetch, headers).
9: */
10:
11: import { setTimeout as sleepMs } from "node:timers/promises";
12: import { getTraceData } from "@sentry/node-core/light";
13: import { maybeWarnEnvTokenIgnored } from "./auth-hint.js";
14: import { computeInvalidationPrefixes } from "./cache-keys.js";
15: import {
16: DEFAULT_SENTRY_URL,
17: getConfiguredSentryUrl,
18: getUserAgent,
19: } from "./constants.js";
20: import {
21: buildTlsErrorDetail,
22: getCustomTlsOptions,
23: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/token-host.ts</path> <type>file</type> <content> 1: /** 2: * Host-Scoped Token Trust Model 3: * 4: * Tokens (env or stored OAuth) are bound to a specific Sentry host. The fetch 5: * layer (and the `.sentryclirc` / URL-arg entry points) check each request's 6: * destination against the token's recorded host and refuse to attach 7: * credentials when they don't match — so untrusted routing inputs can't leak 8: * credentials to an attacker's host. 9: * 10: * Host equivalence: 11: * - Exact origin match (scheme + host + explicit port). 12: * - SaaS equivalence class: a token scoped to `https://sentry.io` is valid for 13: * any `*.sentry.io` subdomain. Non-SaaS hosts match exactly — no subdomain 14: * suffix matching (a `sentry.acme.com` token does NOT match 15: * `sentry.acme.evil.com`). ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/custom-headers.ts</path>
<type>file</type>
<content>
1: /**
2: * Custom Headers for Self-Hosted Sentry
3: *
4: * Parses `SENTRY_CUSTOM_HEADERS` env var (or `defaults.headers` from SQLite)
5: * and injects user-specified HTTP headers into all requests to self-hosted
6: * Sentry instances. Designed for environments behind reverse proxies
7: * (e.g., Google IAP, Cloudflare Access) that require extra headers.
8: *
9: * Format: semicolon-separated `Name: Value` pairs (newlines also accepted).
10: *
11: * @example
12: * ```bash
13: * # Single header
14: * SENTRY_CUSTOM_HEADERS="X-IAP-Token: abc123"
15: *
16: * # Multiple headers
17: * SENTRY_CUSTOM_HEADERS="X-IAP-Token: abc123; X-Forwarded-For: 10.0.0.1"
18: *
19: * # Via defaults command
20: * sentry cli defaults headers "X-IAP-Token: abc123"
21: * ```
22: */
23:
24: import { ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/token-claims.ts</path>
<type>file</type>
<content>
1: /**
2: * Sentry Org-Auth-Token (`sntrys_`) Claim Extraction
3: *
4: * Format: `sntrys_<base64(JSON{iat, url, region_url, org})>_<random-secret>`
5: * (server-side: getsentry/sentry `orgauthtoken_token.py`).
6: *
7: * The claim is **NOT signed** — anyone can forge a `sntrys_` string with any
8: * `url`. However, for legitimate tokens the claim IS authoritative: the real
9: * server wrote it at issuance time, and it's immune to env-injection attacks
10: * (the attacker who can poison `SENTRY_HOST` via `$GITHUB_ENV` can't read or
11: * modify the token bytes). `captureEnvTokenHost` uses the claim as the
12: * primary trust source for `sntrys_` tokens, ahead of env vars. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/env-token-host.ts</path> <type>file</type> <content> 1: /** 2: * Env-Token Host Snapshot 3: * 4: * Captures the host an env-var auth token (`SENTRY_AUTH_TOKEN` / 5: * `SENTRY_TOKEN`) is scoped to, BEFORE any post-boot code path can mutate 6: * `env.SENTRY_HOST`/`env.SENTRY_URL` (specifically before 7: * `applySentryCliRcEnvShim` writes from a `.sentryclirc` file). 8: * 9: * Trust model for the snapshot source: 10: * 11: * - `SENTRY_HOST`/`SENTRY_URL` from env are NOT unconditionally trusted. 12: * In layered CI environments (e.g. GitHub Actions `$GITHUB_ENV`), a 13: * low-privilege step can write env vars that a later high-privilege step 14: * inherits — without having read access to `SENTRY_AUTH_TOKEN`. So 15: * env-host and env-token may have different integrity levels. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/sentry-urls.ts</path>
<type>file</type>
<content>
1: /**
2: * Sentry URL Utilities
3: *
4: * Utilities for constructing Sentry web URLs.
5: * Supports self-hosted instances via SENTRY_URL environment variable.
6: */
7:
8: import {
9: DEFAULT_SENTRY_HOST,
10: DEFAULT_SENTRY_URL,
11: getConfiguredSentryUrl,
12: normalizeUrl,
13: } from "./constants.js";
14:
15: /**
16: * Get the Sentry web base URL.
17: * Supports self-hosted instances via SENTRY_URL env var.
18: */
19: export function getSentryBaseUrl(): string {
20: return getConfiguredSentryUrl() ?? DEFAULT_SENTRY_URL;
21: }
22:
23: /**
24: * Build the org-scoped base URL using the subdomain pattern.
25: * E.g. "https://sentry.io" + "my-org" → "https://my-org.sentry.io"
26: *
27: * @param orgSlug - Organization slug
28: * @returns Origin URL with org as subdomain
29: */
30: export ...
Found 5 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/auth.ts:
Line 100: export function getRawEnvToken(): string | undefined {
Line 272: export function getUsableStoredTokenHost(): string | undefined {
Line 301: export function getAuthToken(): string | undefined {
Line 386: export function setAuthToken(
Line 634: export async function refreshToken(
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/auth.ts</path>
<type>file</type>
<content>
70: // Non-fatal: if the migration write fails, callers still get a
71: // well-formed host from this function. The migration will retry
72: // on the next access.
73: }
74: row.host = host;
75: return host;
76: }
77:
78: /** Prefix for environment variable auth sources in {@link AuthSource} */
79: export const ENV_SOURCE_PREFIX = "env:";
80:
81: /** Where the auth token originated */
82: export type AuthSource = "env:SENTRY_AUTH_TOKEN" | "env:SENTRY_TOKEN" | "oauth";
83:
84: export type AuthConfig = {
85: token?: string;
86: refreshToken?: string;
87: expiresAt?: number;
88: issuedAt?: number;
89: source: AuthSource;
90: };
91:
92: /**
93: * Read the raw token string from environment variables, ignoring all filters. ...
Found 5 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/regions.ts:
Line 64: export function registerTrustedRegionUrls(urls: readonly string[]): void {
Line 77: export function isTrustedRegionOrigin(origin: string): boolean {
Line 90: export function clearTrustedHostState(): void {
Line 182: export function setOrgRegion(orgSlug: string, regionUrl: string): void {
Line 204: export function setOrgRegions(entries: OrgRegionEntry[]): void {
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/regions.ts</path>
<type>file</type>
<content>
1: /**
2: * Organization region cache for multi-region support.
3: *
4: * Sentry has multiple regions (US, EU, etc.) and organizations are bound
5: * to a specific region. This module caches the organization-to-region
6: * mapping to avoid repeated lookups.
7: *
8: * The `org_id` column (added in schema v8) enables offline resolution
9: * of numeric org IDs extracted from DSN hosts (e.g., `o1081365` →
10: * look up by `org_id = '1081365'` → get the slug).
11: */
12:
13: import { normalizeOrigin } from "../sentry-urls.js";
14: import { recordCacheHit } from "../telemetry.js";
15: import { getDatabase } from "./index.js";
16: import { runUpsert } from "./utils.js";
17:
18: const TABLE = "org_regions";
19:
20: /**
21: * Process-local trust extension: origins that were vouched for by the
22: * active ...
Found 11 matches /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/region.ts: Line 93: setOrgRegion(orgSlug, regionUrl); /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/api/projects.ts: Line 75: // Mirrors how listOrganizations() calls setOrgRegions(). /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/api/organizations.ts: Line 165: setOrgRegions(regionEntries); /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/regions.ts: Line 52: // setOrgRegion(s) or registerTrustedRegionUrls. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/region.ts</path>
<type>file</type>
<content>
1: /**
2: * Region resolution for multi-region Sentry support.
3: *
4: * Provides utilities to resolve the correct region URL for an organization,
5: * using cached data when available or fetching from the API when needed.
6: */
7:
8: import { getOrganization } from "@sentry/api";
9: import { getConfiguredSentryUrl } from "./constants.js";
10: import { getOrgByNumericId, getOrgRegion, setOrgRegion } from "./db/regions.js";
11: import { stripDsnOrgPrefix } from "./dsn/index.js";
12: import { withAuthGuard } from "./errors.js";
13: import { getApiBaseUrl, getSdkConfig } from "./sentry-client.js";
14: import { isSentrySaasUrl } from "./sentry-urls.js";
15:
16: /**
17: * Promise cache for org region resolution, keyed by orgSlug. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/sentry-client.test.ts</path>
<type>file</type>
<content>
1: /**
2: * Tests for the authenticated fetch retry + timeout behavior — CLI-1D6
3: * regression coverage.
4: */
5:
6: import { afterEach, beforeEach, describe, expect, test } from "vitest";
7: import { setAuthToken } from "../../src/lib/db/auth.js";
8: import { TimeoutError } from "../../src/lib/errors.js";
9: import {
10: __injectTimeoutOverrideForTests,
11: __resolveRequestTimeoutMsForTests,
12: getSdkConfig,
13: resetAuthenticatedFetch,
14: } from "../../src/lib/sentry-client.js";
15: import { mockFetch, useTestConfigDir } from "../helpers.js";
16:
17: useTestConfigDir("sentry-client-");
18:
19: let originalFetch: typeof globalThis.fetch;
20: const REGION_URL = "https://us.sentry.io";
21:
22: beforeEach(async () => {
23: originalFetch = globalThis.fetch;
24: // Non-expiring token — ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/package.json</path>
<type>file</type>
<content>
1: {
2: "name": "sentry",
3: "version": "0.40.0-dev.0",
4: "description": "Sentry CLI - A command-line interface for using Sentry built by robots and humans for robots and humans",
5: "repository": {
6: "type": "git",
7: "url": "git+https://github.com/getsentry/cli.git"
8: },
9: "main": "./dist/index.cjs",
10: "type": "module",
11: "types": "./dist/index.d.cts",
12: "exports": {
13: ".": {
14: "import": {
15: "types": "./dist/index.d.mts",
16: "default": "./dist/index.mjs"
17: },
18: "require": {
19: "types": "./dist/index.d.cts",
20: "default": "./dist/index.cjs"
21: }
22: }
23: },
24: "bin": {
25: "sentry": "./dist/bin.cjs"
26: },
27: "files": [
28: "dist/bin.cjs",
29: "dist/index.cjs",
30: ...
DEPRECATED `test.poolOptions` was removed in Vitest 4. All previous `poolOptions` are now top-level options. Please, refer to the migration guide: https://vitest.dev/guide/migration#pool-rework
RUN v4.1.10 /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli
[info] [auth] Detected SENTRY_AUTH_TOKEN env var but using stored login for stored OAuth user.
Set SENTRY_FORCE_ENV_TOKEN=1 to prefer the env var.
[info] [auth] Detected SENTRY_AUTH_TOKEN env var but using stored login for stored OAuth user.
Set SENTRY_FORCE_ENV_TOKEN=1 to prefer the env var.
Test Files 3 passed (3)
Tests 45 passed (45)
Start at 10:55:39
Duration 12.00s (transform 6.62s, setup 295ms, import 7.73s, tests 9.36s, environment 0ms)
Found 4 matches /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/oauth.ts: Line 492: tokenResponse.refresh_token, Line 509: export function refreshAccessToken( Line 523: grant_type: "refresh_token", Line 524: refresh_token: refreshToken,
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/oauth.ts</path>
<type>file</type>
<content>
440: await callbacks.onUserCode(
441: user_code,
442: verification_uri,
443: verification_uri_complete ?? `${verification_uri}?user_code=${user_code}`
444: );
445:
446: // Calculate absolute timeout
447: const timeoutAt = Date.now() + Math.min(timeout, expires_in * 1000);
448:
449: // Track polling interval (may increase on slow_down)
450: let pollInterval = interval;
451:
452: // Step 2: Poll for token
453: while (Date.now() < timeoutAt) {
454: await sleep(pollInterval * 1000);
455:
456: const result = await attemptPoll(device_code);
457:
458: switch (result.status) {
459: case "success":
460: return result.token;
461: case "pending":
462: continue;
463: case "slow_down":
464: pollInterval += 5;
465: continue;
466: case ...
Found 2 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/oauth.ts:
Line 44: function getSentryUrl(): string {
Line 246: function assertRefreshHostTrusted(): void {
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/oauth.ts</path>
<type>file</type>
<content>
1: /**
2: * OAuth Authentication
3: *
4: * Implements RFC 8628 Device Authorization Grant for Sentry OAuth.
5: * https://datatracker.ietf.org/doc/html/rfc8628
6: */
7:
8: import type { TokenResponse } from "../types/index.js";
9: import {
10: DeviceCodeResponseSchema,
11: TokenErrorResponseSchema,
12: TokenResponseSchema,
13: } from "../types/index.js";
14: import { SENTRY_SCOPES } from "./api-scope.js";
15: import { DEFAULT_SENTRY_URL, getConfiguredSentryUrl } from "./constants.js";
16: import {
17: buildTlsErrorDetail,
18: getCustomTlsOptions,
19: isTlsCertError,
20: warnIfSaasWithEnvCa,
21: } from "./custom-ca.js";
22: import { applyCustomHeaders } from "./custom-headers.js";
23: import { setAuthToken } from "./db/auth.js";
24: import { getEnv } from "./env.js";
25: import {
26: ApiError,
27: ...
{"body":"When only `SENTRY_AUTH_TOKEN` is set (an `sntrys_` org-auth token embedding a custom instance URL, e.g. `http://localhost:8000`) and no `SENTRY_URL`/`SENTRY_HOST`/`--url` is provided, commands such as `sentry debug-files upload` fail with:\n\n```\nError: Credentials: https://sentry.io\nRefusing to route requests here because it doesn't match the host your Sentry credentials are for (http://localhost:8000).\nTo use this host, run: sentry auth login --url https://sentry.io\nTo keep using your current credentials, remove this URL override.\n```\n\nRunning `sentry auth logout` first does not help; the same error persists.\n\nRoot cause (source-verified):\n- `getConfiguredSentryUrl()` in `packages/cli/src/lib/constants.ts` (used by `getApiBaseUrl()`/`getControlSiloUrl()` in `lib/sentry-client.ts`) only reads `SENTRY_HOST`/`SENTRY_URL` and otherwise falls back to `DEFAULT_SENTRY_URL` (`https://sentry.io`). It never consults the `sntrys_` token's embedded `url` claim.\n- Separately, ...
Found 20 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/sentry-client.ts:
Line 108: * by the SDK. Without this, fetch(Request, {headers}) would override the
Line 145: // When the SDK calls fetch(request) with no init, read headers from the Request
Line 146: // object to preserve Content-Type. On Node.js, fetch(request, {headers}) replaces
Line 394: // On Bun, this passes `tls: { ca }` to fetch(); on Node, the
Line 420: const response = await fetch(fetchInput, fetchInit);
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/delta-upgrade.ts:
Line 588: async function prefetch(
Line 607: return prefetch(nightlySource(), targetVersion, signal);
Line 614: return prefetch(stableSource(), targetVersion, ...
Found 71 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/upgrade.test.ts:
Line 1626: // Return gzipped blob directly (no redirect needed for test)
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/arg-parsing.test.ts:
Line 263: test("@latest throws with redirect to issue view", () => {
Line 272: test("@most_frequent throws with redirect to issue view", () => {
Line 297: test("/@latest (leading slash) throws with redirect", () => {
Line 303: test("sentry/@latest (org/selector) throws with redirect", () => {
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/sentryclirc.test.ts:
Line 295: // the CVE where a committed .sentryclirc could redirect requests ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/security/refresh-token-poison.test.ts</path>
<type>file</type>
<content>
1: /**
2: * CVE defense-in-depth: OAuth refresh-token credential exfiltration.
3: *
4: * Attack: if something bypasses the entry-point guards and poisons
5: * `env.SENTRY_URL` before the next OAuth refresh fires, the refresh token
6: * would previously be POSTed to the attacker's `/oauth/token/` endpoint.
7: *
8: * Fix: `refreshAccessToken` calls `assertRefreshHostTrusted()` before
9: * building the request body, which throws `CliError` on mismatch.
10: */
11:
12: import { afterEach, beforeEach, describe, expect, test } from "vitest";
13: import {
14: captureEnvTokenHost,
15: resetEnvTokenHostForTesting,
16: } from "../../../src/lib/env-token-host.js";
17: import { refreshAccessToken } from "../../../src/lib/oauth.js";
18: import { extractFetchUrl, useEnvSandbox } from ...
RUN v4.1.10 /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli
DEPRECATED `test.poolOptions` was removed in Vitest 4. All previous `poolOptions` are now top-level options. Please, refer to the migration guide: https://vitest.dev/guide/migration#pool-rework
[warn] [custom-headers] Skipping custom headers for request to untrusted host. If this is legitimate, run 'sentry auth login --url <url>' against the intended instance.
[warn] [custom-headers] Skipping custom headers for request to untrusted host. If this is legitimate, run 'sentry auth login --url <url>' against the intended instance.
[warn] [custom-headers] Skipping custom headers for request to untrusted host. If this is legitimate, run 'sentry auth login --url <url>' against the intended instance.
Test Files 2 passed (2)
Tests 6 passed (6)
Start at 10:59:00
Duration 2.97s (transform 3.37s, setup 222ms, import 4.80s, tests 79ms, environment 0ms)
70e2e3d4be2d13a4c54badf81fc4f930e403feb2ccd123d5b1099ecb3791facf - M .lore.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/dashboard.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/event.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/explore.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/feedback.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/issue.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/log.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/replay.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/span.md M packages/cli/plugins/sentry-cli/skills/sentry-cli/references/trace.md M packages/cli/src/lib/constants.ts M packages/cli/src/lib/region.ts M packages/cli/src/lib/sentry-client.ts M packages/cli/test/lib/constants.test.ts M packages/cli/test/lib/security/sntrys-claim-mismatch.test.ts M ...
Found 4 matches /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/commands/auth/login.ts: Line 365: await clearAuth(); Line 386: await clearAuth(); Line 531: // OAuth device flow (host scope recorded via completeOAuthFlow → setAuthToken) /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/commands/auth/logout.ts: Line 62: await clearAuth();
HIGH — redirect trust can change mid-flight, leaking the original bearer.
sentry-client.ts:359 revalidates redirects against mutable active auth state, then reuses the old bearer from headers. If clearAuth() or an auth-source change occurs while the initial trusted request awaits fetch() (:420), token-host.ts:129 returns no anchor and accepts an attacker redirect.
Regression: defer a trusted response, call clearAuth(), resolve it with a 302 to an attacker, and assert rejection with exactly one outbound request.
HIGH — OAuth refresh still follows cross-origin 307/308 redirects. (Pre-existing, but relevant to the requested auth guarantee.)
oauth.ts:210 uses native redirect following after only validating the initial host at :246. A 307/308 forwards the refresh-token POST body (:521-525) and custom headers to the redirect target.
Regression: two local servers; trusted server returns 307 to attacker server. Assert the attacker receives neither refresh_token nor SENTRY_CUSTOM_HEADERS.
MEDIUM — tests do not prove the full no-exfiltration property.
sntrys-claim-mismatch.test.ts:261 covers only a 302 header case. sentry-client.test.ts:119 proves trusted 307 body forwarding, but no untrusted 307/308 test proves that a secret body, bearer, and custom header never reach the attacker.
Malformed claim URLs reject before fetch, inactive env claims do not override stored OAuth, and registered regions pass the existing trust extension. Relevant tests passed: 51 tests across five files. Inspected diff SHA-256: 70e2e3d4be2d13a4c54badf81fc4f930e403feb2ccd123d5b1099ecb3791facf.
DO-NOT-MERGE