Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions packages/cli/src/lib/sentry-urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { AsyncLocalStorage } from "node:async_hooks";
import { isSentryHost } from "@sentry/toolkit-core/sentry-host";
import {
DEFAULT_SENTRY_HOST,
DEFAULT_SENTRY_URL,
Expand Down Expand Up @@ -84,10 +85,7 @@ export function isSentrySaasUrl(url: string): boolean {
// oxlint-disable-next-line sentry-cli/no-silent-catch -- grandfathered silent catch — see #1531; drain by adding log.debug()/log.warn() or re-throwing.
try {
const parsed = new URL(url);
return (
parsed.hostname === DEFAULT_SENTRY_HOST ||
parsed.hostname.endsWith(`.${DEFAULT_SENTRY_HOST}`)
);
return isSentryHost(parsed.hostname);
} catch {
return false;
}
Expand Down
7 changes: 6 additions & 1 deletion packages/cli/test/script/cli-startup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ test.each(["cli.ts", "index.ts"])(
if (args.kind === "dynamic-import") {
return { path: args.path, external: true };
}
if (args.path.startsWith("@sentry/")) {
// The hostname predicate is pure and has no startup side effects.
// Keep traversing its source to reject any future SDK imports.
if (
args.path.startsWith("@sentry/") &&
args.path !== "@sentry/toolkit-core/sentry-host"
) {
return {
errors: [
{
Expand Down
7 changes: 3 additions & 4 deletions packages/mcp-core/src/utils/url-utils.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { isSentryHost as isSharedSentryHost } from "@sentry/toolkit-core/sentry-host";
import type { SentryProtocol } from "../types";
import {
type EventsDataset,
isMetricsDataset,
isProfilesDataset,
} from "./events-datasets";

/**
* Recognizes Sentry-owned hosts, including single-tenant deployments.
*/
/** Keep the MCP utility export for its existing consumers. */

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, get rid of this please. All consumers should use the shared util. Never do this kind of silly wrapping again.

export function isSentryHost(host: string): boolean {
return host === "sentry.io" || host.endsWith(".sentry.io");
return isSharedSentryHost(host);
}

/** Hosts that use the public SaaS control host and organization web subdomains. */
Expand Down
9 changes: 5 additions & 4 deletions packages/toolkit-core/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Toolkit core

Pure authentication protocol helpers shared by the CLI and MCP. Both product
Pure protocol and hostname helpers shared by the CLI and MCP. Both product
builds bundle this private workspace package into their artifacts.

The shared code validates opaque bearer tokens, constructs OAuth device-flow
form bodies, and applies the RFC 8628 polling interval rule. Each product
retains its own credential storage, host trust checks, polling deadline, HTTP
transport, response validation, and user-facing error types.
form bodies, applies the RFC 8628 polling interval rule, and recognizes Sentry
hostnames. Each product retains its own credential storage, URL and host trust
checks, regional routing, polling deadline, HTTP transport, response validation,
and user-facing error types.
4 changes: 4 additions & 0 deletions packages/toolkit-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"./oauth-poll": {
"types": "./src/oauth-poll.ts",
"default": "./src/oauth-poll.ts"
},
"./sentry-host": {
"types": "./src/sentry-host.ts",
"default": "./src/sentry-host.ts"
}
},
"scripts": {
Expand Down
21 changes: 21 additions & 0 deletions packages/toolkit-core/src/sentry-host.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
import { isSentryHost } from "./sentry-host";

describe("isSentryHost", () => {
it.each(["sentry.io", "us.sentry.io", "tenant.my.sentry.io"])(
"recognizes %s as a Sentry hostname",
(host) => {
expect(isSentryHost(host)).toBe(true);
},
);

it.each([
"",
"sentry.io.example.com",
"notsentry.io",
"sentry.io.",
"SENTRY.IO",
])("does not classify %s as a Sentry hostname", (host) => {
expect(isSentryHost(host)).toBe(false);
});
});
4 changes: 4 additions & 0 deletions packages/toolkit-core/src/sentry-host.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** Classify a parsed hostname as Sentry-owned, including regional and tenant hosts. */
export function isSentryHost(host: string): boolean {
return host === "sentry.io" || host.endsWith(".sentry.io");
}
Loading