Dashboard › cli › Session 0U52UtWcOYiY
0U52UtWcOYiY9Zb2BPerform a substantive, independent, read-only correctness/adversarial review of the current uncommitted getsentry/cli worktree patch for GitHub issue #1568. Do not edit any file, do not run formatting or generation, and do not alter Git state. Worktree root: /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus. Base revision is HEAD=605e8318d. The exact final patch identity at launch is SHA-1 d6fb08a6fc3ccdd14c52020f745eb20893148998 from git diff --binary | git hash-object --stdin; verify it before and after review and report BLOCKED if it changes. Review all issue-relevant production and test changes, especially packages/cli/src/lib/sentry-client.ts, token-host.ts, env-token-host.ts, db/auth.ts, region.ts, db/regions.ts, oauth.ts, constants.ts, resolve-target.ts, and corresponding tests. The key behavior: when only SENTRY_AUTH_TOKEN is an sntrys_ token with an embedded custom URL and no explicit SENTRY_HOST/SENTRY_URL, requests must use that claim URL. Preserve explicit URL precedence, stored OAuth precedence unless SENTRY_FORCE_ENV_TOKEN is active, legacy NULL stored-host migration provenance, region-cache identity isolation, exact-origin credential guards, redirect method/body semantics, SDK/raw request headers, and the rule that headers are never forwarded across an unvalidated origin. A new fail-first regression showed that a legacy NULL-host stored OAuth row plus an inactive forged env token claim was migrated/routed to the forged claim; the final patch separates the explicit boot URL snapshot from env-token claim scope so migration defaults to SaaS absent explicit boot URL. Inspect tests for deterministic coverage, early-return or cache holes, and behavioral regressions. Validation already passed 266 focused tests and the full 416-file/8,768-pass suite, typecheck, Biome, and policy checks, but do not infer correctness from that. Return a non-empty findings-first report with exact file:line references. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/AGENTS.md</path> <type>file</type> <content> 1: <!-- This section is maintained by the coding agent via lore (https://github.com/BYK/loreai) --> 2: ## Long-term Knowledge 3: 4: For long-term knowledge entries managed by [lore](https://github.com/BYK/loreai) (gotchas, patterns, decisions, architecture), see [`.lore.md`](.lore.md) in the project root. 5: <!-- End lore-managed section --> (End of file - total 5 lines) </content>
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/CONTRIBUTING.md
No files found
<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: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/CONTRIBUTING.md</path> <type>file</type> <content> 1: # Contributing to Sentry CLI 2: 3: This guide documents the patterns and conventions used in this CLI for consistency. 4: 5: ## Command Patterns 6: 7: We follow [gh CLI](https://cli.github.com/) conventions for best-in-class developer experience. 8: 9: ### List Commands 10: 11: List commands use **optional positional arguments** for context with smart auto-detection. 12: 13: ```bash 14: sentry org list [--limit N] [--json] 15: sentry project list [org] [--limit N] [--json] 16: sentry issue list [<org>/<project>] [--json] 17: ``` 18: 19: **Target syntax**: 20: - `<org>/<project>` - Explicit organization and project (e.g., `my-org/frontend`) 21: - `<org>/` - All projects in the specified organization 22: - `<project>` - Search for project by name across all accessible organizations 23: - *(omit)* - Auto-detect ...
No files found
No files found
No files found
605e8318d1f316bd28f53763f71ed716d1869616 d6fb08a6fc3ccdd14c52020f745eb20893148998 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/commands/org/list.ts M packages/cli/src/commands/project/delete.ts M packages/cli/src/lib/api/organizations.ts M packages/cli/src/lib/api/projects.ts M packages/cli/src/lib/constants.ts M ...
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) ?? ...
diff --git a/packages/cli/src/commands/org/list.ts b/packages/cli/src/commands/org/list.ts
index 75388d34f..b552b2ccf 100644
--- a/packages/cli/src/commands/org/list.ts
+++ b/packages/cli/src/commands/org/list.ts
@@ -19,6 +19,7 @@ import {
FRESH_FLAG,
} from "../../lib/list-command.js";
import { withProgress } from "../../lib/polling.js";
+import { getApiBaseUrl } from "../../lib/sentry-client.js";
import type { SentryOrganization, Writer } from "../../types/index.js";
type ListFlags = {
@@ -141,7 +142,7 @@ export const listCommand = buildCommand({
const limitedOrgs = orgs.slice(0, flags.limit);
// Check if user has orgs in multiple regions
- const orgRegions = getAllOrgRegions();
+ const orgRegions = getAllOrgRegions(getApiBaseUrl());
const uniqueRegions = new Set(orgRegions.values());
const showRegion = uniqueRegions.size > 1;
diff --git a/packages/cli/src/commands/project/delete.ts b/packages/cli/src/commands/project/delete.ts
index ...
diff --git a/packages/cli/src/lib/oauth.ts b/packages/cli/src/lib/oauth.ts
index e25bd368a..3f2c9e67f 100644
--- a/packages/cli/src/lib/oauth.ts
+++ b/packages/cli/src/lib/oauth.ts
@@ -32,7 +32,13 @@ import {
import { logger } from "./logger.js";
import { normalizeOrigin } from "./sentry-urls.js";
import { withHttpSpan } from "./telemetry.js";
-import { getActiveTokenHost, isRequestOriginTrusted } from "./token-host.js";
+import {
+ getActiveTokenHost,
+ isHostTrusted,
+ isRequestOriginTrusted,
+} from "./token-host.js";
+
+const MAX_REDIRECTS = 20;
/**
* Get the Sentry instance URL for OAuth endpoints.
@@ -195,19 +201,8 @@ async function fetchWithConnectionError(
url: string,
init: RequestInit
): Promise<Response> {
- // Inject custom headers for self-hosted proxies (IAP, mTLS, etc.) —
- // URL-scoped so they don't leak to untrusted hosts.
- const merged = new Headers(init.headers);
- applyCustomHeaders(merged, url);
- const effectiveInit: RequestInit = { ...
diff --git a/packages/cli/test/lib/constants.test.ts b/packages/cli/test/lib/constants.test.ts
index 0e6e020f3..7733161da 100644
--- a/packages/cli/test/lib/constants.test.ts
+++ b/packages/cli/test/lib/constants.test.ts
@@ -152,6 +152,13 @@ describe("getConfiguredSentryUrl", () => {
expect(getConfiguredSentryUrl()).toBe("https://host.example.com");
});
+ test("ignores an empty SENTRY_HOST in favor of SENTRY_URL", () => {
+ process.env.SENTRY_HOST = " ";
+ process.env.SENTRY_URL = "https://configured.example.com";
+
+ expect(getConfiguredSentryUrl()).toBe("https://configured.example.com");
+ });
+
test("preserves protocol when already present", () => {
process.env.SENTRY_HOST = "https://sentry.example.com";
expect(getConfiguredSentryUrl()).toBe("https://sentry.example.com");
diff --git a/packages/cli/test/lib/db/auth.host.test.ts b/packages/cli/test/lib/db/auth.host.test.ts
index 1cab411fd..753e55fb2 100644
--- ...
diff --git a/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/dashboard.md b/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/dashboard.md index 75babca80..96ebe39a6 100644 --- a/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/dashboard.md +++ b/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/dashboard.md @@ -42,7 +42,7 @@ View a dashboard - `-w, --web - Open in browser` - `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data` - `-r, --refresh <value> - Auto-refresh interval in seconds (default: 60, min: 10)` -- `-t, --period <value> - Time range: "7d", "2026-06-01..2026-07-01", ">=2026-06-01"` +- `-t, --period <value> - Time range: "7d", "2026-08-01..2026-09-01", ">=2026-08-01"` **Examples:** diff --git a/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/event.md b/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/event.md index 9aef2b487..eb286824b 100644 --- ...
<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/db/auth.ts</path>
<type>file</type>
<content>
1: /**
2: * Authentication credential storage (single-row table pattern).
3: */
4:
5: import { createHash } from "node:crypto";
6: import { DEFAULT_SENTRY_URL, getConfiguredSentryUrl } from "../constants.js";
7: import { getEnv } from "../env.js";
8: import { getBootConfiguredSentryUrl } from "../env-token-host.js";
9: import { logger } from "../logger.js";
10: import { normalizeOrigin } from "../sentry-urls.js";
11: import { withDbSpan } from "../telemetry.js";
12: import { getDatabase } from "./index.js";
13: import { clearAllIssueOrgCache } from "./issue-org-cache.js";
14: import { clearTrustedHostState } from "./regions.js";
15: import { runUpsert } from "./utils.js";
16:
17: /** Refresh when less than 10% of token lifetime remains */
18: export const REFRESH_THRESHOLD = 0.1;
19:
20: /** Default token ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/auth.ts</path>
<type>file</type>
<content>
490: */
491: export function getIdentityFingerprint(): string {
492: if (cachedFingerprint === undefined) {
493: cachedFingerprint = computeIdentityFingerprint();
494: }
495: return cachedFingerprint;
496: }
497:
498: /** Reset the memoized fingerprint. Tests only — call between auth-state mutations. */
499: export function resetIdentityFingerprintCache(): void {
500: cachedFingerprint = undefined;
501: }
502:
503: function computeIdentityFingerprint(): string {
504: // Forced env-token: matches what `refreshToken()` will actually send.
505: if (getEnv().SENTRY_FORCE_ENV_TOKEN?.trim()) {
506: const envToken = getRawEnvToken();
507: if (envToken) {
508: return hashIdentity("env", envToken);
509: }
510: }
511:
512: const row = withDbSpan("getIdentityFingerprint", () => {
513: ...
Found 89 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/token-host.ts:
Line 21: import { getEnvTokenHost } from "./env-token-host.js";
Line 67: return hasEnvToken ? getEnvTokenHost() : undefined;
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/token-claims.ts:
Line 11: * modify the token bytes). `captureEnvTokenHost` uses the claim as the
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/sentryclirc.test.ts:
Line 13: captureEnvTokenHost,
Line 302: captureEnvTokenHost();
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/sentry-url-parser.test.ts:
Line 688: // calling captureEnvTokenHost() (implicit on first getEnvTokenHost ...
<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/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 { DEFAULT_SENTRY_URL } from "../constants.js";
14: import { normalizeOrigin } from "../sentry-urls.js";
15: import { recordCacheHit } from "../telemetry.js";
16: import { getDatabase } from "./index.js";
17: import { runUpsert } from "./utils.js";
18:
19: const TABLE = "org_regions";
20:
21: /**
22: * Process-local regional ...
Found 20 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/api-client.multiregion.test.ts:
Line 342: const cachedRegions = getAllOrgRegions();
Line 393: const cachedRegions = getAllOrgRegions();
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/security/sntrys-claim-mismatch.test.ts:
Line 275: test("forced env token never uses a region cached for stored OAuth", async () => {
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/resolve-effective-org.test.ts:
Line 76: const region = getOrgRegion("acme");
Line 86: const region = getOrgRegion("no-id-org");
Line 102: const region = getOrgRegion("my-org");
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/region.test.ts:
...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/api/organizations.ts</path>
<type>file</type>
<content>
1: /**
2: * Organization API functions
3: *
4: * CRUD operations and region discovery for Sentry organizations.
5: */
6:
7: import {
8: getOrganization as sdkGetOrganization,
9: listOrganizations as sdkListOrganizations,
10: } from "@sentry/api";
11:
12: import {
13: type Region,
14: type SentryOrganization,
15: type UserRegionsResponse,
16: UserRegionsResponseSchema,
17: } from "../../types/index.js";
18:
19: import { ApiError } from "../errors.js";
20: import {
21: getApiBaseUrl,
22: getControlSiloUrl,
23: getSdkConfig,
24: } from "../sentry-client.js";
25:
26: import {
27: API_MAX_PER_PAGE,
28: apiRequestToRegion,
29: autoPaginate,
30: getOrgSdkConfig,
31: MAX_PAGINATION_PAGES,
32: type PaginatedResponse,
33: unwrapPaginatedResult,
34: unwrapResult,
35: } from ...
Found 15 matches /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/sentryclirc-import.ts: Line 512: await getUserRegions(); /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/region.ts: Line 97: setOrgRegion(orgSlug, regionUrl, baseUrl); /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/commands/auth/login.ts: Line 499: await getUserRegions(); Line 552: * doesn't pay the cold-start cost of `getUserRegions()` + fan-out to /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/api/projects.ts: Line 75: // Mirrors how listOrganizations() calls setOrgRegions(). Line 469: const regionsResult = await withAuthGuard(() => ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/api/projects.ts</path>
<type>file</type>
<content>
420: *
421: * Uses `\b` regex word boundary, which matches:
422: * - Start/end of string
423: * - Between word char (`\w`) and non-word char (like "-")
424: *
425: * Matching is bidirectional:
426: * - Directory name in project slug: dir "cli" matches project "cli-website"
427: * - Project slug in directory name: project "docs" matches dir "sentry-docs"
428: *
429: * @param pattern - Directory name to match against project slugs
430: * @returns Array of matching projects with their org context
431: */
432: export async function findProjectsByPattern(
433: pattern: string
434: ): Promise<ProjectWithOrg[]> {
435: const orgs = await listOrganizations();
436:
437: const limit = pLimit(ORG_FANOUT_CONCURRENCY);
438: const searchResults = await Promise.all(
439: orgs.map((org) =>
440: ...
/**
* Organization API functions
*
* CRUD operations and region discovery for Sentry organizations.
*/
import {
getOrganization as sdkGetOrganization,
listOrganizations as sdkListOrganizations,
} from "@sentry/api";
import {
type Region,
type SentryOrganization,
type UserRegionsResponse,
UserRegionsResponseSchema,
} from "../../types/index.js";
import { ApiError } from "../errors.js";
import { getControlSiloUrl, getSdkConfig } from "../sentry-client.js";
import {
API_MAX_PER_PAGE,
apiRequestToRegion,
autoPaginate,
getOrgSdkConfig,
MAX_PAGINATION_PAGES,
type PaginatedResponse,
unwrapPaginatedResult,
unwrapResult,
} from "./infrastructure.js";
/**
* Get the list of regions the user has organization membership in.
* This endpoint is on the control silo (sentry.io) and returns all regions. ...
<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: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/oauth.ts</path>
<type>file</type>
<content>
430: const tokenResult = TokenResponseSchema.safeParse(data);
431: if (tokenResult.success) {
432: return tokenResult.data;
433: }
434:
435: // Try to parse as error response
436: const errorResult = TokenErrorResponseSchema.safeParse(data);
437: if (errorResult.success) {
438: throw new DeviceFlowError(
439: errorResult.data.error,
440: errorResult.data.error_description
441: );
442: }
443:
444: // If neither schema matches, throw a generic error
445: throw new ApiError(
446: "Unexpected response from token endpoint",
447: response.status,
448: JSON.stringify(data),
449: "/oauth/token/"
450: );
451: });
452: }
453:
454: type PollResult =
455: | { status: "success"; token: TokenResponse }
456: | { status: "pending" ...
Found 21 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/db/auth.host.test.ts:
Line 66: test("refresh-style update preserves existing host when options.host omitted", () => {
Line 72: // Simulate a refresh: new access token + same refresh token, no host
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/init/wizard-runner.test.ts:
Line 1481: setAuthToken("self-hosted-token", 3600, "refresh-token", {
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/security/sntrys-claim-mismatch.test.ts:
Line 179: test("stored OAuth host takes precedence over an inactive env token claim", async () => {
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/security/refresh-token-poison.test.ts:
...
<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 { setAuthToken } from "../../../src/lib/db/auth.js";
14: import {
15: captureEnvTokenHost,
16: resetEnvTokenHostForTesting,
17: } from "../../../src/lib/env-token-host.js";
18: import { refreshAccessToken } from ...
<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: /** ...
<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 21 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/telemetry.ts:
Line 709: Sentry.setTag("is_self_hosted", !isSentrySaasUrl(getSentryBaseUrl()));
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/sentry-urls.ts:
Line 19: export function getSentryBaseUrl(): string {
Line 31: const base = getSentryBaseUrl();
Line 51: return isSentrySaasUrl(getSentryBaseUrl());
Line 164: return `${getSentryBaseUrl()}/organizations/${orgSlug}/`;
Line 178: return `${getSentryBaseUrl()}/settings/${orgSlug}/projects/${projectSlug}/`;
Line 192: return `${getSentryBaseUrl()}/organizations/${orgSlug}/issues/${issueId}/`;
Line 207: return `${getSentryBaseUrl()}/organizations/${orgSlug}/issues/?query=event.id:${eventId}`;
Line 222: : `${getSentryBaseUrl()}/settings/${orgSlug}/`;
Line 236: return ...
{"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 8 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/commands/event/view.ts:
Line 886: // Issue short ID auto-redirect: user passed an issue short ID
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/sentry-client.ts:
Line 329: return [request, { headers, signal, redirect: "manual", ...customTls }];
Line 334: { ...init, headers, signal, redirect: "manual", ...customTls },
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/oauth.ts:
Line 255: redirect: "manual",
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/ghcr.ts:
Line 15: * Using `fetch` with `redirect: "follow"` would forward the Authorization
Line 322: * `fetch` with `redirect: "follow"` would forward the ...
<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. ...
Found 1 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/region.ts:
Line 111: export function isMultiRegionEnabled(): boolean {
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/schema.ts</path>
<type>file</type>
<content>
130: project_aliases: {
131: columns: {
132: alias: { type: "TEXT", primaryKey: true },
133: org_slug: { type: "TEXT", notNull: true },
134: project_slug: { type: "TEXT", notNull: true },
135: dsn_fingerprint: { type: "TEXT" },
136: cached_at: {
137: type: "INTEGER",
138: notNull: true,
139: default: "(unixepoch() * 1000)",
140: },
141: last_accessed: {
142: type: "INTEGER",
143: notNull: true,
144: default: "(unixepoch() * 1000)",
145: },
146: },
147: },
148: pagination_cursors: {
149: columns: {
150: command_key: { type: "TEXT", notNull: true },
151: context: { type: "TEXT", notNull: true },
152: cursor_stack: { type: "TEXT", notNull: true },
153: page_index: { type: "INTEGER", ...
Found 31 matches /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/token-host.ts: Line 121: isTrustedRegionOrigin(requestOrigin, anchorHost) /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/complete.ts: Line 190: const orgs = getCachedOrganizations(); Line 295: const orgs = getCachedOrganizations(); /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/api/projects.ts: Line 75: // Mirrors how listOrganizations() calls setOrgRegions(). Line 77: const orgs = getCachedOrganizations(getApiBaseUrl()); Line 575: const cached = getCachedOrganizations(getApiBaseUrl()).find( /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/api/organizations.ts: Line 113: const cached = ...
The primary server restarted. Resume and complete your independent, read-only correctness/adversarial review now. Reconcile current immutable state first: worktree root /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus; HEAD must equal 605e8318d1f316bd28f53763f71ed716d1869616. Lore updated .lore.md during the interruption, so the new full worktree diff hash is 87b7931085d81a11be56c78bba216ddec8885c2a and the product patch hash excluding .lore.md is d4e9faf5bacfcd846cc2b2fd39b98d8fd7596a83. Verify both before and after review. Do not edit files or alter Git state. Review issue #1568 behavior and every relevant production/test change, with emphasis on active token routing, explicit URL precedence, stored OAuth versus forced env token precedence, legacy NULL-host migration provenance, region-cache identity isolation, exact-origin guards, redirect method/body/header semantics, and SDK/raw content-type behavior. The known fail-first flaw was that a legacy NULL-host stored OAuth row could be migrated/routed using an inactive forged env token claim; the current patch separates explicit boot URL snapshot from env-token claim scope. Return a substantive findings-first report with exact current file:line references and PASS/CONCERN/MUST-FIX/BLOCKED labels. For defects, give a deterministic regression and concrete fix. State reviewed scope and residual risks. Empty output is invalid. End exactly MERGE or DO-NOT-MERGE.
HEAD 605e8318d1f316bd28f53763f71ed716d1869616 FULL 87b7931085d81a11be56c78bba216ddec8885c2a PRODUCT d4e9faf5bacfcd846cc2b2fd39b98d8fd7596a83 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/commands/org/list.ts M packages/cli/src/commands/project/delete.ts M packages/cli/src/lib/api/organizations.ts M packages/cli/src/lib/api/projects.ts M ...
POST 1 body abc POST 1 body abc
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/api/infrastructure.ts</path>
<type>file</type>
<content>
1: /**
2: * API Client Infrastructure
3: *
4: * Shared helpers, types, constants, and raw request functions used by
5: * all domain-specific API modules. This is the foundation layer that
6: * other modules in `src/lib/api/` import from.
7: */
8:
9: import { promisify } from "node:util";
10: import { zstdCompress as zstdCompressCb } from "node:zlib";
11: import { parseSentryLinkHeader } from "@sentry/api";
12: // biome-ignore lint/performance/noNamespaceImport: Sentry SDK recommends namespace import
13: import * as Sentry from "@sentry/node-core/light";
14: import type { z } from "zod";
15:
16: import { extractRequiredScopes } from "../api-scope.js";
17: import { getActiveEnvVarName, isEnvTokenActive } from "../db/auth.js";
18: import { getEnv } from "../env.js";
19: import { ApiError, AuthError, ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/api/infrastructure.ts</path>
<type>file</type>
<content>
550: * Extract error detail from a failed HTTP response, attach diagnostic
551: * headers to the Sentry scope, and throw an enriched {@link ApiError}.
552: *
553: * Extracted from `apiRequestToRegion` to keep the main function's
554: * cognitive complexity under the lint threshold.
555: */
556: async function throwRawApiError(
557: response: Response,
558: endpoint: string
559: ): Promise<never> {
560: let detail: string | undefined;
561: try {
562: const text = await response.text();
563: try {
564: const parsed = JSON.parse(text) as { detail?: string };
565: // Enriched statuses (403, 401) pass undefined when there is no
566: // usable string detail so the enrichment renders without a noisy
567: // `{"detail":null}` prefix. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/api/infrastructure.ts</path>
<type>file</type>
<content>
470: const queryString = searchParams ? `?${searchParams.toString()}` : "";
471: // getSdkConfig.baseUrl is the plain region URL; add /api/0/ for raw requests
472: const url = `${config.baseUrl}/api/0/${normalizedEndpoint}${queryString}`;
473:
474: const fetchFn = config.fetch;
475: const headers: Record<string, string> = {
476: "Content-Type": "application/json",
477: };
478: let requestBody: string | Uint8Array | undefined;
479: if (body !== undefined) {
480: const json = JSON.stringify(body);
481: if (bodyEncoding === "zstd" && zstdCompressAsync) {
482: requestBody = await zstdCompressAsync(Buffer.from(json));
483: headers["Content-Encoding"] = "zstd";
484: } else {
485: requestBody = json;
486: }
487: }
488: const response = await fetchFn(url, ...
<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/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/cli.ts</path>
<type>file</type>
<content>
1: /**
2: * CLI runner with fast-path dispatch.
3: *
4: * Shell completion (`__complete`) is dispatched before any heavy imports
5: * to avoid loading `@sentry/node-core` (~280ms). All other commands go through
6: * the full CLI with telemetry, middleware, and error recovery.
7: *
8: * Extracted from `bin.ts` so the logic is testable and reusable without
9: * top-level side effects. `bin.ts` remains a thin wrapper that registers
10: * stream error handlers and calls `startCli()`.
11: */
12:
13: import { getEnv } from "./lib/env.js";
14: import { CliError } from "./lib/errors.js";
15: import { initTimezone } from "./lib/timezone.js";
16:
17: /**
18: * Preload project context: walk up from `cwd` once, finding both the
19: * project root (for DSN detection) and `.sentryclirc` config (for
20: * org/project defaults ...
Found 3 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/env-token-host.ts:
Line 30: * Boot ordering (see `src/cli.ts::preloadProjectContext`):
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/cli.ts:
Line 23: async function preloadProjectContext(cwd: string): Promise<void> {
Line 710: await preloadProjectContext(process.cwd());
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/cli.ts</path>
<type>file</type>
<content>
660: }
661: process.stderr.write(`${error("Error:")} ${formatError(err)}\n`);
662: process.exitCode = getExitCode(err);
663: const notification = getErrorUpdateNotification(err, hoistedArgs);
664: if (notification) {
665: process.stderr.write(notification);
666: }
667: return;
668: } finally {
669: // Abort any pending version check to allow clean exit
670: abortPendingVersionCheck();
671: }
672:
673: // Show update notification after command completes
674: if (!suppressNotification) {
675: const notification = getUpdateNotification();
676: if (notification) {
677: process.stderr.write(notification);
678: }
679: }
680: }
681:
682: /**
683: * Top-level CLI dispatch.
684: *
685: * Reads `process.argv`, dispatches to the completion fast-path or the ...
Found 3 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/utils.ts:
Line 116: * runUpsert(db, 'auth', { id: 1, token: 'abc' }, ['id']);
Line 118: export function runUpsert<T extends Record<string, SqlValue>>(
Line 178: runUpsert(db, "metadata", { key, value }, ["key"]);
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/utils.ts</path>
<type>file</type>
<content>
90: const sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${(conflictColumns as string[]).join(", ")}) ${updateClause}`;
91:
92: return { sql, values };
93: }
94:
95: /** Minimal db interface needed for query execution and metadata helpers */
96: type QueryRunner = {
97: query(sql: string): {
98: run(...values: SqlValue[]): void;
99: all(...values: SqlValue[]): Record<string, SqlValue>[];
100: };
101: transaction<T>(fn: () => T): () => T;
102: };
103:
104: /**
105: * Execute an UPSERT statement directly on the database.
106: *
107: * Convenience wrapper that combines upsert() SQL generation with execution.
108: * For advanced options like excludeFromUpdate, use upsert() directly. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/utils.ts</path>
<type>file</type>
<content>
1: /**
2: * SQL builder utilities for common database operations.
3: * Reduces boilerplate for UPSERT and other repetitive patterns.
4: */
5:
6: import { getDatabase } from "./index.js";
7: import type { SQLQueryBindings } from "./sqlite.js";
8:
9: /** Valid SQLite binding value (re-exported from sqlite.ts adapter) */
10: export type SqlValue = SQLQueryBindings;
11:
12: /**
13: * Result of building an SQL query with parameterized values.
14: */
15: export type SqlQuery = {
16: /** The SQL string with ? placeholders */
17: sql: string;
18: /** The values to bind to the placeholders */
19: values: SqlValue[];
20: };
21:
22: /**
23: * Options for the upsert function.
24: */
25: export type UpsertOptions<T> = {
26: /** Columns to exclude from the UPDATE SET clause */
27: excludeFromUpdate?: (keyof ...
Found 28 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/migration.ts:
Line 149: // runs during DB bootstrap, before getIdentityFingerprint() memoizes.
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/auth.ts:
Line 491: export function getIdentityFingerprint(): string {
Line 512: const row = withDbSpan("getIdentityFingerprint", () => {
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/custom-headers.ts:
Line 40: "authorization",
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/oauth.ts:
Line 341: * Request a device code from Sentry's device authorization endpoint. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/response-cache.ts</path>
<type>file</type>
<content>
100: * @internal Exported for testing
101: */
102: export function classifyUrl(url: string): TtlTier {
103: for (const tier of TIER_CHECK_ORDER) {
104: for (const pattern of URL_TIER_REGEXPS[tier]) {
105: if (pattern.test(url)) {
106: return tier;
107: }
108: }
109: }
110: return "stable";
111: }
112:
113: // ---------------------------------------------------------------------------
114: // Cache key generation
115: // ---------------------------------------------------------------------------
116:
117: /**
118: * Build a deterministic cache key from the active identity + method + URL.
119: *
120: * Query params are sorted alphabetically so `?a=1&b=2` and `?b=2&a=1`
121: * produce the same key. ...
Found 19 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/db/schema.ts:
Line 43: * table-level `PRIMARY KEY (col1, col2, ...)` constraint instead of
Line 44: * per-column `PRIMARY KEY` attributes. Individual columns listed here
Line 47: compositePrimaryKey?: string[];
Line 156: compositePrimaryKey: ["command_key", "context"],
Line 288: compositePrimaryKey?: string[]
Line 293: parts.push("PRIMARY KEY");
Line 307: if (compositePrimaryKey && compositePrimaryKey.length > 0) {
Line 308: columnDefs.push(`PRIMARY KEY (${compositePrimaryKey.join(", ")})`);
Line 322: schema.compositePrimaryKey
Line 348: return columnDefsToDDL(tableName, baseColumns, schema.compositePrimaryKey);
Line 406: * the table has a table-level PRIMARY KEY constraint matching the
Line 408: * PRIMARY KEY instead (e.g., `command_key TEXT PRIMARY KEY`). ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/helpers.ts</path>
<type>file</type>
<content>
1: /**
2: * Test Helpers
3: *
4: * Shared utilities for test setup and teardown.
5: */
6:
7: import { mkdirSync } from "node:fs";
8: import { mkdtemp, rm } from "node:fs/promises";
9: import { join } from "node:path";
10: import { afterEach, beforeEach } from "vitest";
11: import {
12: resetAuthRowCache,
13: resetAuthTokenCache,
14: resetHasStoredCredsCache,
15: resetIdentityFingerprintCache,
16: } from "../src/lib/db/auth.js";
17: import { CONFIG_DIR_ENV_VAR, closeDatabase } from "../src/lib/db/index.js";
18:
19: // biome-ignore lint/performance/noBarrelFile: re-exporting a single constant, not a barrel
20: export { TEST_TMP_DIR } from "./constants.js";
21:
22: import { TEST_TMP_DIR } from "./constants.js";
23:
24: mkdirSync(TEST_TMP_DIR, { recursive: true });
25:
26: type TestConfigDirOptions = {
27: ...
Found 9 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/commands/auth/login.ts:
Line 18: import { setDefaultUrl } from "../../lib/db/defaults.js";
Line 163: * (rc-shim bypass wrote env.SENTRY_URL with no matching trust anchor).
Line 180: * `applyLoginUrl` only registers a trust anchor when the host comes from
Line 284: setDefaultUrl(effectiveHost);
Line 293: * When `--url` is passed, set `env.SENTRY_HOST`/`env.SENTRY_URL` so the
Line 303: export function applyLoginUrl(url: string | undefined): string {
Line 308: env.SENTRY_URL = url;
Line 466: const effectiveHost = applyLoginUrl(flags.url);
Line 531: // OAuth device flow (host scope recorded via completeOAuthFlow → setAuthToken)
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/commands/auth/login.ts</path>
<type>file</type>
<content>
270: /**
271: * Persist a non-SaaS `--url` host as the stored default so subsequent CLI
272: * invocations route correctly without requiring `SENTRY_HOST`. Only writes
273: * when `--url` was explicitly passed; env/rc-sourced values persist
274: * through those channels. Non-fatal on DB failure.
275: */
276: function persistLoginUrlAsDefault(
277: flagUrl: string | undefined,
278: effectiveHost: string
279: ): void {
280: if (!flagUrl || isSaaSTrustOrigin(effectiveHost)) {
281: return;
282: }
283: try {
284: setDefaultUrl(effectiveHost);
285: } catch {
286: log.debug(
287: `Could not persist default URL to DB; host is recorded on the stored token. Set SENTRY_HOST or run 'sentry cli defaults url ${effectiveHost}' if subsequent commands route incorrectly.`
288: );
289: ...
<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>
515: });
516:
517: test("forged claim url is captured (claim is NOT a security primitive)", async () => {
518: // Documents the trust contract: the snapshot picks up whatever the
519: // claim says, even if forged. This is acceptable because:
520: // - For a legitimate token, the url is authoritative.
521: // - For a forged token (user pasted attacker's token), the user
522: // has already authorized the attacker server — out of threat
523: // model.
524: process.env.SENTRY_AUTH_TOKEN = mintSntrysToken({
525: iat: 1_700_000_000,
526: url: "https://evil.com",
527: org: "victim",
528: });
529:
530: const { captureEnvTokenHost, getEnvTokenHost } = await import(
531: "../../../src/lib/env-token-host.js"
532: );
533: ...
Found 13 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/resolve-effective-org.test.ts:
Line 76: const region = getOrgRegion("acme");
Line 86: const region = getOrgRegion("no-id-org");
Line 102: const region = getOrgRegion("my-org");
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/region.test.ts:
Line 129: const before = getOrgRegion("new-org");
Line 165: const after = getOrgRegion("new-org");
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/api-client.multiregion.test.ts:
Line 342: const cachedRegions = getAllOrgRegions();
Line 393: const cachedRegions = getAllOrgRegions();
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/test/lib/db/model-based.test.ts:
...
HEAD 605e8318d1f316bd28f53763f71ed716d1869616 FULL 87b7931085d81a11be56c78bba216ddec8885c2a PRODUCT d4e9faf5bacfcd846cc2b2fd39b98d8fd7596a83
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/constants.ts</path>
<type>file</type>
<content>
60: if (HAS_PROTOCOL_RE.test(trimmed)) {
61: return trimmed;
62: }
63: return `https://${trimmed}`;
64: }
65:
66: /**
67: * Resolve the Sentry instance URL from explicit environment configuration.
68: * Checks `SENTRY_HOST` first, then `SENTRY_URL`, and otherwise returns undefined.
69: *
70: * Bare hostnames (e.g., `sentry.example.com`) are automatically prefixed
71: * with `https://` to prevent invalid URL construction downstream.
72: */
73: export function getConfiguredSentryUrl(): string | undefined {
74: const env = getEnv();
75: return normalizeUrl(env.SENTRY_HOST) ?? normalizeUrl(env.SENTRY_URL);
76: }
77:
78: /** CLI version string, available for help output and other uses */
79: export const CLI_VERSION =
80: typeof SENTRY_CLI_VERSION !== "undefined" ? SENTRY_CLI_VERSION : ...
1 1 packages/cli/plugins/sentry-cli/skills/sentry-cli/references/dashboard.md 1 1 packages/cli/plugins/sentry-cli/skills/sentry-cli/references/event.md 1 1 packages/cli/plugins/sentry-cli/skills/sentry-cli/references/explore.md 1 1 packages/cli/plugins/sentry-cli/skills/sentry-cli/references/feedback.md 2 2 packages/cli/plugins/sentry-cli/skills/sentry-cli/references/issue.md 1 1 packages/cli/plugins/sentry-cli/skills/sentry-cli/references/log.md 1 1 packages/cli/plugins/sentry-cli/skills/sentry-cli/references/replay.md 1 1 packages/cli/plugins/sentry-cli/skills/sentry-cli/references/span.md 2 2 packages/cli/plugins/sentry-cli/skills/sentry-cli/references/trace.md diff --git a/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/event.md b/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/event.md index 9aef2b487..eb286824b 100644 --- a/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/event.md +++ ...
Date: Sep 10, 2026 * 🔴 [requested-review] (16:45) User requested a substantive, independent, read-only correctness/adversarial review of the current uncommitted getsentry/cli worktree patch for GitHub issue #1568, with a non-empty findings-first report and exact file:line references. * 🔴 [enforced-read-only-review] (16:45) User directed that the review must not edit any file, run formatting or …