Dashboard › cli › Session 1FK9YfAaOR0H
1FK9YfAaOR0HYc1C9Perform an independent read-only security review of the current worktree patch for getsentry/cli issue #1568. Do not edit files. Analyze env sntrys_ token claim routing, credential precedence between stored OAuth and env credentials, host-scoping and bearer/custom-header leakage, explicit host overrides, invalid claims, and any denial-of-service or trust-boundary regression. Inspect source and tests. Return findings first ordered by severity with file:line references, followed by residual gaps. End with exactly PASS if no merge-blocking security defect, otherwise MUST-FIX. Do not report generic style suggestions.
<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/sentry-client.ts M packages/cli/test/lib/constants.test.ts M packages/cli/test/lib/security/sntrys-claim-mismatch.test.ts .lore.md | 700 ++++++++++++--------- .../skills/sentry-cli/references/dashboard.md | 2 ...
diff --git a/packages/cli/src/lib/constants.ts b/packages/cli/src/lib/constants.ts
index 4e707773a..843b7dcb1 100644
--- a/packages/cli/src/lib/constants.ts
+++ b/packages/cli/src/lib/constants.ts
@@ -64,15 +64,15 @@ export function normalizeUrl(url: string | undefined): string | undefined {
}
/**
- * Resolve the Sentry instance URL from environment variables.
- * Checks SENTRY_HOST first, then SENTRY_URL, then falls back to undefined.
+ * Resolve the Sentry instance URL from explicit environment configuration.
+ * Checks `SENTRY_HOST` first, then `SENTRY_URL`, and otherwise returns undefined.
*
* Bare hostnames (e.g., `sentry.example.com`) are automatically prefixed
* with `https://` to prevent invalid URL construction downstream.
*/
export function getConfiguredSentryUrl(): string | undefined {
- const raw = getEnv().SENTRY_HOST || getEnv().SENTRY_URL || undefined;
- return normalizeUrl(raw);
+ const env = getEnv();
+ return normalizeUrl(env.SENTRY_HOST) ?? ...
Found 100 matches (more matches available)
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/token-type.ts:
Line 17: /** `sntrys_...` — organization-scoped auth token, not tied to a user. */
Line 33: if (token.startsWith("sntrys_")) {
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/token-host.ts:
Line 31: export function isHostTrusted(
Line 55: * token unless `SENTRY_FORCE_ENV_TOKEN` is set.
Line 57: export function getActiveTokenHost(): string | undefined {
Line 59: const forceEnv = hasEnvToken && !!getEnv().SENTRY_FORCE_ENV_TOKEN?.trim();
Line 76: * The shell argv / boot env is at the same trust boundary as `SENTRY_AUTH_TOKEN`
Line 99: return isHostTrusted(host, loginTrustAnchor);
Line 115: if (isHostTrusted(requestInput, anchorHost)) {
Line 129: const tokenHost = getActiveTokenHost();
Line 137: * ...
<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/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/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. ...
Found 13 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 119: * which check the DB first when `SENTRY_FORCE_ENV_TOKEN` is not set.
Line 158: export function getAuthConfig(): AuthConfig | undefined {
Line 159: // When SENTRY_FORCE_ENV_TOKEN is set, check env first (old behavior).
Line 163: const forceEnv = getEnv().SENTRY_FORCE_ENV_TOKEN?.trim();
Line 272: export function getUsableStoredTokenHost(): string | undefined {
Line 299: * With `SENTRY_FORCE_ENV_TOKEN=1`: checks env vars first (old behavior).
Line 301: export function getAuthToken(): string | undefined {
Line 311: const forceEnv = getEnv().SENTRY_FORCE_ENV_TOKEN?.trim();
Line 386: export function setAuthToken(
Line 507: if (getEnv().SENTRY_FORCE_ENV_TOKEN?.trim()) {
Line 637: // With SENTRY_FORCE_ENV_TOKEN, env token takes ...
<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. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/auth.ts</path>
<type>file</type>
<content>
340: }
341: return;
342: }
343:
344: /** Reset the memoized auth token. Tests only — call between auth-state mutations. */
345: export function resetAuthTokenCache(): void {
346: cachedAuthToken = undefined;
347: }
348:
349: /** Memoized result for {@link hasStoredAuthCredentials}. Same wrapper contract as {@link cachedAuthToken}. */
350: let cachedHasStoredCreds: { value: boolean } | undefined;
351:
352: /** Memoized full auth row for {@link refreshToken}. Same wrapper contract as {@link cachedAuthToken}. */
353: let cachedAuthRow: { value: AuthRow | undefined } | undefined;
354:
355: function getCachedAuthRow(): AuthRow | undefined {
356: if (cachedAuthRow !== undefined) {
357: return cachedAuthRow.value;
358: }
359: const row = getAuthRow();
360: cachedAuthRow = { value: row };
361: return ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/security/sntrys-claim-mismatch.test.ts</path>
<type>file</type>
<content>
1: /**
2: * Defense-in-depth: `sntrys_` token claim vs request-origin mismatch.
3: *
4: * The fetch-layer guard refuses to attach a `sntrys_` token when its
5: * embedded `url` claim disagrees with the request origin. Defends users
6: * with access to multiple Sentry instances against routing one
7: * instance's token to another. Claim is unsigned (see token-claims.ts),
8: * so this catches honest misconfigurations more than malicious attacks.
9: */
10:
11: import { afterEach, beforeEach, describe, expect, test } from "vitest";
12: import {
13: extractFetchUrl,
14: mintSntrysToken,
15: resetHostScopingState,
16: useEnvSandbox,
17: useTestConfigDir,
18: } from "../../helpers.js";
19:
20: const ENV_KEYS = [
21: "SENTRY_AUTH_TOKEN",
22: "SENTRY_TOKEN",
23: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/security/sntrys-claim-mismatch.test.ts</path>
<type>file</type>
<content>
255: describe("UX path: env-token-host falls back to sntrys_ claim url", () => {
256: // These tests check the captureEnvTokenHost snapshot — no fetch
257: // mocking needed.
258: useEnvSandbox(ENV_KEYS);
259:
260: beforeEach(resetHostScopingState);
261: afterEach(resetHostScopingState);
262:
263: test("self-hosted user with sntrys_ token but no SENTRY_HOST → snapshot uses claim", async () => {
264: // User pasted a sntrys_ token from their self-hosted UI but didn't
265: // also export SENTRY_HOST. Without the claim fallback, the snapshot
266: // would default to SaaS and every command would trip the host
267: // guard. With the claim fallback, the snapshot picks up the
268: // self-hosted url from the token itself.
269: process.env.SENTRY_AUTH_TOKEN = ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/constants.ts</path>
<type>file</type>
<content>
1: /**
2: * Runtime constants for the CLI.
3: */
4:
5: import { getEnv } from "./env.js";
6:
7: /** Build-time constant injected by esbuild/bun */
8: declare const SENTRY_CLI_VERSION: string | undefined;
9:
10: /**
11: * Build-time debug ID for sourcemap resolution, injected by esbuild.
12: *
13: * During the build, esbuild's `define` replaces this identifier with a
14: * placeholder UUID string literal. After esbuild finishes, the build
15: * script replaces the placeholder with the real debug ID (derived from
16: * the minified JS + sourcemap content hash). The same-length swap keeps
17: * sourcemap character positions valid.
18: */
19: declare const __SENTRY_DEBUG_ID__: string | undefined;
20:
21: /** Default Sentry SaaS hostname */
22: export const DEFAULT_SENTRY_HOST = "sentry.io";
23:
24: /** ...
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/types/oauth.test.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/e2e/auth.test.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/token-host.test.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/token-host.property.test.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/sentry-client.test.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/sentry-client.invalidation.test.ts /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/security/custom-headers-leak.test....
Found 2 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/token-host.test.ts:
Line 25: test("preserves explicit non-default port", () => {
Line 92: // :9000 explicit does not match default :443
Found 34 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/db/auth.host.test.ts:
Line 34: const prevHost = process.env.SENTRY_HOST;
Line 35: process.env.SENTRY_HOST = "https://env-host.example.com";
Line 41: delete process.env.SENTRY_HOST;
Line 43: process.env.SENTRY_HOST = prevHost;
Line 48: test("setAuthToken without host falls back to DEFAULT_SENTRY_URL", () => {
Line 49: const prevHost = process.env.SENTRY_HOST;
Line 50: const prevUrl = process.env.SENTRY_URL;
Line 51: delete process.env.SENTRY_HOST;
Line 52: delete process.env.SENTRY_URL;
Line 58: process.env.SENTRY_HOST = prevHost;
Line 61: process.env.SENTRY_URL = prevUrl;
Line 84: // Simulate the boot ordering: SHELL-exports SENTRY_HOST, then
Line 91: const prevHost = process.env.SENTRY_HOST;
Line 92: process.env.SENTRY_HOST = ...
No files found
<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/test/lib/security/custom-headers-leak.test.ts</path>
<type>file</type>
<content>
1: /**
2: * CVE regression: custom-headers leak via share URL and via the
3: * `auth login` rc-URL bypass.
4: *
5: * Tests `applyCustomHeaders` trust scoping. Two attack shapes:
6: * 1. Share URL: `getSharedIssue(https://evil.com, ...)` — headers must
7: * not attach to URLs that don't match the active token.
8: * 2. auth login bypass: when `env.SENTRY_URL` is rc-poisoned and no
9: * token is active yet, headers must fail closed.
10: *
11: * See also `fetch-layer-guard.test.ts` for the Bearer-token path.
12: */
13:
14: import { afterEach, beforeEach, describe, expect, test } from "vitest";
15: import { getSharedIssue } from "../../../src/lib/api/issues.js";
16: import {
17: _resetCustomHeadersCache,
18: applyCustomHeaders,
19: } from ...
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/security/fetch-layer-guard.test.ts
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/security/fetch-layer-guard.test.ts</path>
<type>file</type>
<content>
1: /**
2: * Defense-in-depth regression tests for the fetch layer.
3: *
4: * Even if a future code path bypasses the URL-arg / .sentryclirc entry-point
5: * guards and writes `SENTRY_HOST`/`SENTRY_URL` directly, the fetch layer
6: * must still refuse to attach credentials to a request whose origin
7: * doesn't match the active token's scope.
8: *
9: * This file simulates the bypass by directly calling the lower-level
10: * primitives (`apiRequest`, `applyCustomHeaders`, `refreshAccessToken`)
11: * with mismatched hosts.
12: */
13:
14: import { afterEach, beforeEach, describe, expect, test } from "vitest";
15: import {
16: _resetCustomHeadersCache,
17: applyCustomHeaders,
18: } from "../../../src/lib/custom-headers.js";
19: import { resetEnvTokenHostForTesting } from ...
<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/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 [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. [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. [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. [info] [auth] Migrated stored credentials to host-scoped model: https://legacy-configured.example.com [info] [auth] Migrated stored credentials to host-scoped model: https://sentry.io [info] ...
<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 ...
{"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, ...
<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 ...
(no output)
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/sentry-client.ts M packages/cli/test/lib/constants.test.ts M packages/cli/test/lib/security/sntrys-claim-mismatch.test.ts d23acb520090d4c7431575e80a0497b112a8ce284254e36cd10edca27e01c08c -
No merge-blocking security defects found.
sentry-client.ts:685-697.token-host.ts:57-68, db/auth.ts:158-203, db/auth.ts:634-657.sentry-client.ts:121-169; custom headers have an independent scoped guard at custom-headers.ts:216-239.token-host.ts:31-48.sntrys-claim-mismatch.test.ts:181-190). A request with a syntactically parsed but invalid claim URL still fails closed before sending a bearer at sentry-client.ts:133-140. This is token-local denial of service, not credential leakage.SENTRY_FORCE_ENV_TOKEN and stored OAuth, nor the equivalent control-silo getter. Source precedence remains consistent.Verified: focused security/auth suite passed, 129/129 tests; tsc --noEmit passed. Reviewed patch hash: d23acb520090d4c7431575e80a0497b112a8ce284254e36cd10edca27e01c08c.
PASS
Date: Sep 10, 2026 * 🔴 (09:34) User stated `.sentryclirc` files are never consulted when snapshotting an env-token host because they have weaker integrity than environment variables or embedded token claims. * 🔴 (09:34) User stated a `sntrys_` token format uses exactly 2 underscores, with the second underscore always separating the Base64 JSON payload from the secret. * 🔴 (09:34) User stated `…
Date: Sep 10, 2026 * 🔴 (09:33) User stated `apiRequestToRegion` always sends JSON and explicitly sets it. * 🔴 (09:33) User stated the last request attempt always returns `"done"` or `"throw"`. * 🔴 (09:33) User stated `throwOnError` is always `false` because errors are handled directly. * 🔴 (09:33) User stated certain endpoints are always on the control silo, including OAuth, user accounts, an…
Date: Sep 10, 2026 * 🟡 (09:33) Review worktree path is `/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus`; repository branch is `chore/preshape-monorepo`, HEAD is `605e8318d1f316bd28f53763f71ed716d1869616` (`605e8318d chore: regenerate docs`), base reference shown is `0b43edad47386844ece1f9f288310e2683afce49`, and the patch checksum shown is `d23ac…
Date: Sep 10, 2026 * 🔴 [requested-security-review] (09:33) User requested an independent, read-only security review of the current worktree patch for getsentry/cli issue #1568; explicitly instructed not to edit files. * 🟡 (09:33) User asked the review to inspect source and tests, analyze `sntrys_` environment-token claim routing, precedence between stored OAuth credentials and environment crede…