Repository: browserbase/stagehand
File: packages/core/lib/v3/understudy/context.ts (TypeScript)

Code before:
// lib/v3/understudy/context.ts
import type { Protocol } from "devtools-protocol";
import { v3Logger } from "../logger";
import { CdpConnection, CDPSessionLike } from "./cdp";
import { Page } from "./page";
import { installV3PiercerIntoSession } from "./piercer";
import { v3ScriptContent } from "../dom/build/scriptV3Content";
import { executionContexts } from "./executionContextRegistry";
import type { StagehandAPIClient } from "../api";
import { LocalBrowserLaunchOptions } from "../types/public";
import { InitScriptSource } from "../types/private";
import { normalizeInitScriptSource } from "./initScripts";
import { TimeoutError, PageNotFoundError } from "../types/public/sdkErrors";

type TargetId = string;
type SessionId = string;

type TargetType = "page" | "iframe" | string;

function isNonWebTarget(info: Protocol.Target.TargetInfo): boolean {
  return (
    (info.type !== "page" && info.type !== "iframe") ||
    info.url?.startsWith("chrome-extension://") ||
    info.url?.startsWith("chrome://")
  );
}

function isTopLevelPage(info: Protocol.Target.TargetInfo): boolean {
  const ti = info as unknown as { subtype?: string };
  return info.type === "page" && ti.subtype !== "iframe";
}

/**
 * V3Context
 *
 * Owns the root CDP connection and wires Target/Page events into Page.
 * Maintains one Page per top-level target, adopts OOPIF child sessions into the owner Page,
 * and tracks target→page and (root) frame→target mappings for lookups.
 *
 * IMPORTANT: FrameId → session ownership is managed inside Page (via its FrameRegistry).
 * Context never “guesses” owners; it simply forwards events (with the emitting session)
 * so Page can record the correct owner at event time.
 */
export class V3Context {
  private constructor(
    readonly conn: CdpConnection,
    private readonly env: "LOCAL" | "BROWSERBASE" = "LOCAL",
    private readonly apiClient: StagehandAPIClient | null = null,
    private readonly localBrowserLaunchOptions: LocalBrowserLaunchOptions | null = null,

Review comment (security):
there are a bunch of others to exclude here:

`about:*`
`data:*`
`file:*`
`view-source:*`
`javascript:`
`blob:*`
`filesystem:*`

`chrome-error://*`
`devtools://*`
`file://*`
`chrome-untrusted://`
`chrome-search://`
`chrome-native://`
`chrome-distiller://`

might be easier to whitelist http(s)?://

check out [chrome://chrome-urls/](brave://chrome-urls/) for more

---

also other target types you might want to include are `tab` and maybe `webview`:

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/devtools_agent_host_impl.cc?ss=chromium&q=f:devtools%20-f:out%20%22::kTypeTab%5B%5D%22



Code after:
// lib/v3/understudy/context.ts
import type { Protocol } from "devtools-protocol";
import { v3Logger } from "../logger.js";
import { CdpConnection, CDPSessionLike } from "./cdp.js";
import { Page } from "./page.js";
import { installV3PiercerIntoSession } from "./piercer.js";
import { v3ScriptContent } from "../dom/build/scriptV3Content.js";
import { executionContexts } from "./executionContextRegistry.js";
import type { StagehandAPIClient } from "../api.js";
import { LocalBrowserLaunchOptions } from "../types/public/index.js";
import { InitScriptSource } from "../types/private/index.js";
import { normalizeInitScriptSource } from "./initScripts.js";
import { TimeoutError, PageNotFoundError } from "../types/public/sdkErrors.js";
import { getEnvTimeoutMs, withTimeout } from "../timeoutConfig.js";

type TargetId = string;
type SessionId = string;

type TargetType = "page" | "iframe" | string;

/**
 * Returns true when the target's URL points to a document with a real,
 * pierceable HTML DOM.  We allowlist the small set of schemes that carry
 * web content rather than trying to blacklist every internal browser scheme
 * (chrome://, chrome-extension://, devtools://, brave://, edge://, …).
 */
function hasInjectableDOM(url: string | undefined): boolean {
  if (!url || url === "") return true;
  if (
    url === "about:blank" ||
    url === "about:srcdoc" ||
    url.startsWith("about:blank#")
  )
    return true;
  if (url.startsWith("http://") || url.startsWith("https://")) return true;
  if (
    url.startsWith("data:") ||
    url.startsWith("blob:") ||
    url.startsWith("file://") ||
    url.startsWith("filesystem:")
  )
    return true;
  return false;
}

function isNonWebTarget(info: Protocol.Target.TargetInfo): boolean {
  return (
    (info.type !== "page" && info.type !== "iframe") ||
    !hasInjectableDOM(info.url)