[fix]: close popups that violate domain policy - #2294
Merged
seanmcguire12 merged 8 commits intoJun 30, 2026
Merged
Conversation
🦋 Changeset detectedLatest commit: a0dae3b The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Member
Author
Contributor
|
@seanmcguire12 I have started the AI code review. It will take a few minutes to complete. |
Contributor
There was a problem hiding this comment.
1 issue found across 4 files
Confidence score: 4/5
- In
packages/core/tests/integration/context-domain-policy.spec.ts,waitForTargetUrlDestroyedmay miss popups opened directly at a blocked URL because it doesn’t subscribe toTarget.targetCreated, which can leavetargetIdnull and cause timeout-based failures; this is mainly a test reliability and coverage risk rather than a runtime break. Add handling forTarget.targetCreated(alongsidetargetInfoChanged) before merging to de-flake this path and ensure the blocked-popup scenario is actually validated.
Architecture diagram
sequenceDiagram
participant Browser as Browser / Page
participant CDP as Chrome DevTools Protocol
participant V3C as V3Context
participant Policy as Domain Policy
participant Logger
Note over Browser,Logger: NEW: Popup domain policy fallback close
Browser->>CDP: window.open(url)
CDP-->>V3C: Target.targetCreated(targetInfo)
V3C->>V3C: Check if popup (openerId/openerFrameId)
alt Popup detected
V3C->>Policy: getDomainPolicyDecision(url)
alt Decision = "block"
V3C->>V3C: Dedupe check (domainPolicyClosingTargets)
Note over V3C: NEW: Launch close attempt
V3C->>V3C: Store in-flight promise in domainPolicyClosePromises
V3C->>CDP: Target.closeTarget(targetId)
alt Close succeeds
CDP-->>V3C: OK
V3C->>V3C: Add to domainPolicyClosingTargets
V3C->>Logger: Log popup closed
else Close fails (not missing target)
CDP-->>V3C: Error
V3C->>V3C: Remove in-flight promise (allow retry)
V3C->>Logger: Log failure
else Close fails with "No target with given id found"
CDP-->>V3C: Error
V3C->>V3C: Treat as already closed (success)
V3C->>V3C: Add to domainPolicyClosingTargets
end
else Decision = "continue"
V3C-->>V3C: No action
end
end
Note over Browser,V3C: Later events (if popup still alive)
CDP-->>V3C: Target.targetInfoChanged(updatedInfo)
V3C->>V3C: Check dedupe (domainPolicyClosingTargets)
alt Not already closed
V3C->>Policy: getDomainPolicyDecision(url)
alt Decision = "block"
V3C->>CDP: Target.closeTarget(targetId)
Note over V3C: Same close flow as above
end
else Already closed
V3C-->>V3C: Skip (dedup)
end
CDP-->>V3C: Target.attached(targetInfo, sessionId)
V3C->>V3C: Check if in-flight close promise exists
alt In-flight close exists
V3C->>V3C: Await existing close promise
alt Close succeeded
V3C-->>V3C: Skip normal page setup (return early)
else Close failed
V3C->>V3C: Continue with normal attach
end
else No in-flight close
V3C->>Policy: getDomainPolicyDecision(url)
alt Decision = "block"
V3C->>CDP: Target.closeTarget(targetId)
Note over V3C: Same close flow, then return early
else Decision = "continue"
V3C->>V3C: Normal attached handling (page setup)
end
end
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
akeimach
approved these changes
Jun 30, 2026
This was referenced Jun 30, 2026
Merged
seanmcguire12
pushed a commit
that referenced
this pull request
Jul 13, 2026
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @browserbasehq/stagehand@3.7.0 ### Minor Changes - [#2283](#2283) [`871ca7e`](871ca7e) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add `context.setDomainPolicy({ allowedDomains: ["allowed.domain"] })` which allows users to define a set of domains that are accessible to stagehand - [#2274](#2274) [`f31980f`](f31980f) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add `context.setDomainPolicy({blockedDomains: ["some.domain"]})` which allows users to define a list of domains that will be blocked by stagehand ### Patch Changes - [#2305](#2305) [`cd1daad`](cd1daad) Thanks [@shrey150](https://github.com/shrey150)! - Remove the noisy AI SDK "system message in messages" warning logged on every hybrid/DOM `agent.execute()` call. - [#2328](#2328) [`d287ff4`](d287ff4) Thanks [@miguelg719](https://github.com/miguelg719)! - Allow modelName "auto" in the constructor and per-primitive model overrides when running through the Stagehand API - [#2294](#2294) [`3938590`](3938590) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - automatically close popups that violate user defined domain policy - [#2298](#2298) [`892701a`](892701a) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - Fix CUA `keypress` actions to press key combinations as a single chord. - [#2345](#2345) [`21826c7`](21826c7) Thanks [@monadoid](https://github.com/monadoid)! - Repair malformed UTF-16 snapshot text before it reaches model prompts. - [#2306](#2306) [`8dcef1b`](8dcef1b) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - Use the screenshot provider's declared media type when sending CUA image payloads. The `setScreenshotProvider` callback now returns `ScreenshotProviderResult` (`{ base64, mediaType }`) instead of a bare base64 string. - [#2273](#2273) [`93a23d3`](93a23d3) Thanks [@miguelg719](https://github.com/miguelg719)! - Add support for the new `google/gemini-3.5-flash` computer-use tools model - [#2278](#2278) [`022d68f`](022d68f) Thanks [@shrey150](https://github.com/shrey150)! - Fix `TypeError: Converting circular structure to JSON` when creating an agent with MCP `integrations` that include a `Client` instance (e.g. a local/stdio server from `connectToMCPServer`). The agent-creation log serialized the raw `integrations` array, and a live MCP `Client` is circular. It now logs a safe descriptor (URL strings kept, client instances summarized) so `agent({ integrations: [client] })` works. - [#2288](#2288) [`bb5ffa6`](bb5ffa6) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - clean up cdp session event handlers on target detach ## @browserbasehq/stagehand-evals@2.0.4 ### Patch Changes - Updated dependencies \[[`cd1daad`](cd1daad), [`d287ff4`](d287ff4), [`3938590`](3938590), [`892701a`](892701a), [`21826c7`](21826c7), [`8dcef1b`](8dcef1b), [`93a23d3`](93a23d3), [`871ca7e`](871ca7e), [`022d68f`](022d68f), [`bb5ffa6`](bb5ffa6), [`f31980f`](f31980f)]: - @browserbasehq/stagehand@3.7.0 ## @browserbasehq/stagehand-server-v3@3.7.2 ### Patch Changes - Updated dependencies \[[`cd1daad`](cd1daad), [`d287ff4`](d287ff4), [`3938590`](3938590), [`892701a`](892701a), [`21826c7`](21826c7), [`8dcef1b`](8dcef1b), [`93a23d3`](93a23d3), [`871ca7e`](871ca7e), [`022d68f`](022d68f), [`bb5ffa6`](bb5ffa6), [`f31980f`](f31980f)]: - @browserbasehq/stagehand@3.7.0 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
felipeofdev-ai
pushed a commit
to felipeofdev-ai/stagehand
that referenced
this pull request
Aug 4, 2026
# why domain policy enforcement currently relies on `Fetch.requestPaused`, but popups opened with `window.open()` can reach their destination before Fetch interception is installed on the new target. in that race, the blocked popup may successfully open, & therefore break the domain policy # what changed this PR adds a fallback close path for popup targets whose URL violates the active domain policy: - this PR adds listening for `Target.targetCreated`, `Target.targetInfoChanged`, and attach-time target metadata for popup targets - if a popup reaches a blocked/disallowed domain before request interception catches it, the popup gets closed via Target.closeTarget` # test plan - added unit coverage for closing popup targets that already reached a blocked domain, including popups whose opener target is not locally tracked - added unit coverage for duplicate and late target events so a successfully closed popup is not closed/logged repeatedly - added unit coverage for the attach race where a `targetCreated` close is still in flight when `attached` handling runs; attach now continues if the close fails - added unit coverage for close failures, including treating `No target with given id found` as a successful already-closed outcome for this domain-policy fallback only - added integration coverage using the existing external popup fixture to verify a `window.open()` popup that reaches `news.ycombinator.com` is closed and not retained in `context.pages()` <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Fixes a race where `window.open()` popups could reach blocked domains before interception by closing them immediately when their URL violates the active domain policy. Prevents blocked popups from appearing or lingering in `context.pages()`. - **Bug Fixes** - Listen to `Target.targetCreated`, `Target.targetInfoChanged`, and at attach-time; close popup via `Target.closeTarget` if its URL is disallowed. - Deduplicate close attempts across events and let attach wait for an in-flight close; continue attach if the close fails. - Treat “No target with given id found” as a successful already-closed outcome for this fallback; improve logging with rule reason and source. - Skip non-popup targets and persist successful close dedupe across late events. - **Dependencies** - Add changeset to publish a patch for `@browserbasehq/stagehand`. <sup>Written for commit a0dae3b. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/browserbase/stagehand/pull/2294?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
felipeofdev-ai
pushed a commit
to felipeofdev-ai/stagehand
that referenced
this pull request
Aug 4, 2026
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @browserbasehq/stagehand@3.7.0 ### Minor Changes - [browserbase#2283](browserbase#2283) [`871ca7e`](browserbase@871ca7e) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add `context.setDomainPolicy({ allowedDomains: ["allowed.domain"] })` which allows users to define a set of domains that are accessible to stagehand - [browserbase#2274](browserbase#2274) [`f31980f`](browserbase@f31980f) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add `context.setDomainPolicy({blockedDomains: ["some.domain"]})` which allows users to define a list of domains that will be blocked by stagehand ### Patch Changes - [browserbase#2305](browserbase#2305) [`cd1daad`](browserbase@cd1daad) Thanks [@shrey150](https://github.com/shrey150)! - Remove the noisy AI SDK "system message in messages" warning logged on every hybrid/DOM `agent.execute()` call. - [browserbase#2328](browserbase#2328) [`d287ff4`](browserbase@d287ff4) Thanks [@miguelg719](https://github.com/miguelg719)! - Allow modelName "auto" in the constructor and per-primitive model overrides when running through the Stagehand API - [browserbase#2294](browserbase#2294) [`3938590`](browserbase@3938590) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - automatically close popups that violate user defined domain policy - [browserbase#2298](browserbase#2298) [`892701a`](browserbase@892701a) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - Fix CUA `keypress` actions to press key combinations as a single chord. - [browserbase#2345](browserbase#2345) [`21826c7`](browserbase@21826c7) Thanks [@monadoid](https://github.com/monadoid)! - Repair malformed UTF-16 snapshot text before it reaches model prompts. - [browserbase#2306](browserbase#2306) [`8dcef1b`](browserbase@8dcef1b) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - Use the screenshot provider's declared media type when sending CUA image payloads. The `setScreenshotProvider` callback now returns `ScreenshotProviderResult` (`{ base64, mediaType }`) instead of a bare base64 string. - [browserbase#2273](browserbase#2273) [`93a23d3`](browserbase@93a23d3) Thanks [@miguelg719](https://github.com/miguelg719)! - Add support for the new `google/gemini-3.5-flash` computer-use tools model - [browserbase#2278](browserbase#2278) [`022d68f`](browserbase@022d68f) Thanks [@shrey150](https://github.com/shrey150)! - Fix `TypeError: Converting circular structure to JSON` when creating an agent with MCP `integrations` that include a `Client` instance (e.g. a local/stdio server from `connectToMCPServer`). The agent-creation log serialized the raw `integrations` array, and a live MCP `Client` is circular. It now logs a safe descriptor (URL strings kept, client instances summarized) so `agent({ integrations: [client] })` works. - [browserbase#2288](browserbase#2288) [`bb5ffa6`](browserbase@bb5ffa6) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - clean up cdp session event handlers on target detach ## @browserbasehq/stagehand-evals@2.0.4 ### Patch Changes - Updated dependencies \[[`cd1daad`](browserbase@cd1daad), [`d287ff4`](browserbase@d287ff4), [`3938590`](browserbase@3938590), [`892701a`](browserbase@892701a), [`21826c7`](browserbase@21826c7), [`8dcef1b`](browserbase@8dcef1b), [`93a23d3`](browserbase@93a23d3), [`871ca7e`](browserbase@871ca7e), [`022d68f`](browserbase@022d68f), [`bb5ffa6`](browserbase@bb5ffa6), [`f31980f`](browserbase@f31980f)]: - @browserbasehq/stagehand@3.7.0 ## @browserbasehq/stagehand-server-v3@3.7.2 ### Patch Changes - Updated dependencies \[[`cd1daad`](browserbase@cd1daad), [`d287ff4`](browserbase@d287ff4), [`3938590`](browserbase@3938590), [`892701a`](browserbase@892701a), [`21826c7`](browserbase@21826c7), [`8dcef1b`](browserbase@8dcef1b), [`93a23d3`](browserbase@93a23d3), [`871ca7e`](browserbase@871ca7e), [`022d68f`](browserbase@022d68f), [`bb5ffa6`](browserbase@bb5ffa6), [`f31980f`](browserbase@f31980f)]: - @browserbasehq/stagehand@3.7.0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
why
domain policy enforcement currently relies on
Fetch.requestPaused, but popups opened withwindow.open()can reach their destination before Fetch interception is installed on the new target. in that race, the blocked popup may successfully open, & therefore break the domain policywhat changed
this PR adds a fallback close path for popup targets whose URL violates the active domain policy:
Target.targetCreated,Target.targetInfoChanged, and attach-time target metadata for popup targetstest plan
targetCreatedclose is still in flight whenattachedhandling runs; attach now continues if the close failsNo target with given id foundas a successful already-closed outcome for this domain-policy fallback onlywindow.open()popup that reachesnews.ycombinator.comis closed and not retained incontext.pages()Summary by cubic
Fixes a race where
window.open()popups could reach blocked domains before interception by closing them immediately when their URL violates the active domain policy. Prevents blocked popups from appearing or lingering incontext.pages().Bug Fixes
Target.targetCreated,Target.targetInfoChanged, and at attach-time; close popup viaTarget.closeTargetif its URL is disallowed.Dependencies
@browserbasehq/stagehand.Written for commit a0dae3b. Summary will update on new commits.