`--blockedUrlPattern`/`--allowedUrlPattern` bypass: URLPattern default-port and trailing-dot hostname not normalized
Description of the bug
The --blockedUrlPattern / --allowedUrlPattern options are documented as: "Restricts browser's network access by blocking specified URL patterns... blocks runtime requests (including navigations and subresources)."
Enforcement matches each URL with new URLPattern(pattern).test(url) in #validateUrlNotBlocked() and #validateUrlAllowed() (src/McpContext.ts, ~lines 895–916). Two URLPattern behaviors create a normalization gap between what an operator intends to block and what actually matches:
- Omitted port compiles to an exact default-port match. A blocklist entry
https://internal.corpcompiles withport: "", so it does NOT matchhttps://internal.corp:8443/— the same server on a non-default port. - No trailing-dot hostname normalization.
https://internal.corp./is the same origin ashttps://internal.corp/in browser context, but does not match the pattern — so the blocklist is bypassed by appending a dot to the hostname.
Both gaps apply symmetrically to the allowlist path: an operator believing an https://example.com allowlist entry "covers the site" does not have https://example.com:8443 covered.
Impact: anyone using the blocklist as a containment boundary (e.g., preventing the agent from reaching internal hosts, admin panels, or cloud metadata endpoints such as 169.254.169.254) gets silent under-blocking. No warning is emitted, and the failure mode is invisible.
(Note: URLPattern also defaults username/password to wildcard *, so credential-in-URL variants still match — verified this does NOT bypass; noted for completeness.)
This was reported to the Google OSS VRP (https://issuetracker.google.com/issues/565800037) and closed as infeasible for security tracking; filing here publicly as suggested by the Bug Hunter team.
Reproduction
- Configure
--blockedUrlPattern "https://internal.corp"(or the equivalent in your MCP config). - Minimal reproduction of the shipped logic (
src/McpContext.ts):
const blocklist = ['https://internal.corp', 'https://*.internal.corp/*'];
function isBlocked(u) {
const url = new URL(u);
return blocklist.some(b => new URLPattern(b).test(url));
}
### Expectation
```markdown
URLs that are the same origin as a blocked/allowed host should be subject to the same restriction regardless of:
- non-default port variants (`https://internal.corp:8443` should be blocked by an entry for `https://internal.corp`, or the docs should state ports must be enumerated),
- trailing-dot FQDN forms (`https://internal.corp./` should match an entry for `internal.corp`).
Suggested fix: normalize both sides before matching — if a configured pattern specifies no port, treat the port as `*` (or expand http→80 / https→443), and canonicalize trailing dots from hostnames on both the incoming URL and the pattern. Alternatively, document explicitly that patterns are raw URLPattern matches — but given the option's security purpose, normalization in code is the safer default.
<html><body>
<!--StartFragment--><!DOCTYPE html>
<html><head></head><body>
Attempted URL | `isBlocked()` | Meaning
-- | -- | --
`https://internal.corp/portal` | `true` | Control works as documented
`https://internal.corp:8443/portal` | `false` | Same server on another port sails through
`https://internal.corp./portal` | `false` | Same origin via trailing-dot FQDN sails through
</body></html><!--EndFragment-->
</body>
</html>
### MCP configuration
n/a — reproduced against the validation logic directly (see Reproduction). Any config using `--blockedUrlPattern "https://internal.corp"` triggers the behavior.
### Chrome DevTools MCP version
1.10.0
### Chrome version
n/a
### Coding agent version
n/a
### Model version
n/a
### Chat log
_No response_
### Node version
v24.15.0
### Operating system
None
### Extra checklist
- [x] I want to provide a PR to fix this bugSource: ChromeDevTools/chrome-devtools-mcp