`--blockedUrlPattern`/`--allowedUrlPattern` bypass: URLPattern default-port and trailing-dot hostname not normalized

Author: LanZerothCreated Sep 25, 2026Updated Sep 26, 2026

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:

  1. Omitted port compiles to an exact default-port match. A blocklist entry https://internal.corp compiles with port: "", so it does NOT match https://internal.corp:8443/ — the same server on a non-default port.
  2. No trailing-dot hostname normalization. https://internal.corp./ is the same origin as https://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

  1. Configure --blockedUrlPattern "https://internal.corp" (or the equivalent in your MCP config).
  2. Minimal reproduction of the shipped logic (src/McpContext.ts):
javascript
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 bug

Source: ChromeDevTools/chrome-devtools-mcp