An address a person can open differs from one a public-site automation task may use. Convenient human access to a local development page should not grant the agent private-network authority. I separate URL format, task origin scope and actual DNS responses in the browser's checks.
Human URL normalization differs from agent scope checking
NormalizeURL and secureNavigationURL tidy human search/address input and upgrade ordinary HTTP to HTTPS, with loopback exceptions for local development. AssertScope separately applies public HTTPS and allowed-origin conditions to an AI task. Human address-bar convenience is not copied into the task grant.
Listed mode requires the exact origin in scope.origins, not merely a plausible protocol and hostname. Credentials embedded in an address and known authentication/payment/security paths are refused. Those path patterns do not identify every sensitive screen on every service. URL syntax and business-meaning review retain different responsibilities.
A public-looking name can have another DNS answer
IsPrivateHost first checks local-name suffixes and private-address forms. For a hostname, verifyPublicDNS obtains every resolver address and refuses an empty answer or any private-category address. A public-looking name alone is not the evidence of a public destination.
Current code has explicit IPv4 and selected IPv6/mapped-address rules. The list is not presented as certification covering every reserved range and future network configuration. The useful distinction is between textual appearance and resolved answers. Fixture resolver results and a real live DNS query also remain different verification evidence.
Recheck task identity after asynchronous verification
ValidateRequest checks navigation scope for a task-owned tab, awaits DNS, then compares task ID and runEpoch again. Cancellation or another task during the wait produces STALE_RUN. An earlier permission must not attach a late result to another execution.
BrowserHost connects this check to mainFrame requests from task-owned tabs and cancels on refusal. The request type where a policy is wired matters. Assuming human tabs and every subordinate resource pass the same automation path would claim a broader guarantee than the actual implementation supplies.
This is not a firewall over every transmission
The historical threat model distinguishes top-level navigation/action scope from a firewall covering all subresources, fetches and forms. It does not solve every post-check address race or server-side effect. Allowing a public site does not certify every external effect of that page.
User request, input material, selected files and outcome verification therefore still need their own boundaries. A URL check does not justify removing them. An internal-work adaptation needs separate deployment-level network controls and data policy. The app function's name must not borrow the guarantees of infrastructure it does not implement.
Retain precisely the address conditions the tests exercise
Policy fixtures use a substituted resolver returning loopback for a public-looking name and expect refusal. They also test private addresses, embedded credentials, unapproved origins and dangerous schemes. Navigation tests separately cover human HTTPS normalization and local exceptions. Those paths are not counted as one identical permission policy.
I reviewed functions and tests without requesting fresh DNS or private-network access. The linked work-context module is not a network checker either. Reuse the separation of address appearance, actual destination, task authority and asynchronous lifetime without claiming controls beyond them. Public automation convenience should not silently expand into private-network permission. A rejection should also retain its own stage: invalid address, origin outside the task, private resolution and a stale run call for different corrections. Replacing all four with a generic connection failure encourages changes to the wrong boundary.