[Bug]: MCP browser_route with only `headers` bypasses --blocked-origins and --allowed-origins
I maintainer di solito rispondono entro 1 giorno
@dhruv-techdev ci sta già lavorando.
Dal 24/10/2026.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 78/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- playwright, typescript
Direzione di ricerca
Il rapporto indica packages/playwright-core/src/tools/backend/route.ts e context.ts, dove la route che gestisce solo gli header aggira i filtri di origine del contesto. Leggi il gestore della route e l’ordine di registrazione dei filtri, quindi verifica se i test di route esistenti coprono questo caso. Il lavoro è completato quando il routing che gestisce solo gli header mantiene il comportamento previsto per le origini consentite e bloccate, aggiungendo al contempo l’header richiesto.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Version
1.64.1
Steps to reproduce
- Create an empty folder containing the
repro.jsbelow and thispackage.json:{ "private": true, "dependencies": { "playwright": "1.64.1" } } npm installnode repro.js
The script starts a local site that logs every request it receives. It then runs npx playwright mcp --headless --isolated --caps=network --blocked-origins=<site origin> and sends three MCP calls:
browser_navigateto the blocked sitebrowser_routewith{ "pattern": "**", "headers": ["X-Test: 1"] }(nobody/status)browser_navigateto the blocked site again
repro.js
const { spawn } = require('child_process');
const http = require('http');
(async () => {
// Local site that logs every request it actually receives.
const site = http.createServer((req, res) => {
console.log(` [site] received ${req.url} (x-test: ${req.headers['x-test'] || 'none'})`);
res.setHeader('content-type', 'text/html');
res.end('<h1>reached</h1>');
});
await new Promise(r => site.listen(0, '127.0.0.1', r));
const origin = `http://127.0.0.1:${site.address().port}`;
const server = spawn('npx', ['playwright', 'mcp', '--headless', '--isolated', '--caps=network', `--blocked-origins=${origin}`],
{ stdio: ['pipe', 'pipe', 'inherit'] });
// Minimal MCP client over stdio.
let id = 0, buf = '';
const pending = new Map();
server.stdout.on('data', d => {
buf += d;
for (let i; (i = buf.indexOf('\n')) !== -1;) {
const line = buf.slice(0, i); buf = buf.slice(i + 1);
const msg = line.trim() && JSON.parse(line);
if (msg && pending.has(msg.id)) { pending.get(msg.id)(msg); pending.delete(msg.id); }
}
});
const rpc = (method, params) => new Promise(resolve => {
pending.set(++id, resolve);
server.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
});
const call = async (name, args) => (await rpc('tools/call', { name, arguments: args })).result.content.map(c => c.text).join('\n');
const outcome = text => text.match(/net::ERR_\w+/)?.[0] || 'loaded ' + text.match(/Page URL: (.*)/)?.[1];
await rpc('initialize', { protocolVersion: '2025-06-18', capabilities: {}, clientInfo: { name: 'repro', version: '1' } });
server.stdin.write(JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized' }) + '\n');
console.log(`blocked origin: ${origin}`);
console.log('1. browser_navigate ->', outcome(await call('browser_navigate', { url: `${origin}/before` })));
console.log('2. browser_route (headers) ->', (await call('browser_route', { pattern: '**', headers: ['X-Test: 1'] })).match(/Route added.*/)[0]);
console.log('3. browser_navigate ->', outcome(await call('browser_navigate', { url: `${origin}/after` })));
server.kill();
site.close();
})();
Expected behavior
Step 3 is still blocked (net::ERR_BLOCKED_BY_CLIENT), the same as step 1. Adding a header-only route should not change which origins the browser may request.
Actual behavior
Step 3 loads the blocked origin, and the site receives the request with the added header:
blocked origin: http://127.0.0.1:49726
1. browser_navigate -> net::ERR_BLOCKED_BY_CLIENT
2. browser_route (headers) -> Route added for pattern: **
[site] received /after (x-test: 1)
3. browser_navigate -> loaded http://127.0.0.1:49726/after
--allowed-origins is bypassed the same way: with --allowed-origins=https://example.com instead, the output is identical.
Additional context
The origin filters are installed as context routes when the context is set up (context.ts#L432-L442). browser_route handlers are registered later (context.ts#L477), so they run first. The header-only branch then sends the request straight to the network (route.ts#L80):
await route.continue({ headers });
Using route.fallback({ headers }) there would keep the header change and still let the origin-filter routes run. In a plain Playwright check with an abort route registered first and a header route registered second, continue() loads the page and fallback() keeps it blocked.
I understand the flags are documented as not a security boundary. This report is only about them silently stopping working once a header-only route exists.
Environment
System:
OS: macOS 27.0.1
CPU: (10) arm64 Apple M4
Memory: 450.25 MB / 16.00 GB
Binaries:
Node: 22.23.1 - ~/.volta/tools/image/node/22.23.1/bin/node
npm: 11.12.1 - ~/.volta/tools/image/npm/11.12.1/bin/npm
pnpm: 11.14.0 - ~/.volta/bin/pnpm
IDEs:
VSCode: 1.142.0 - /usr/local/bin/code
Languages:
Bash: 3.2.57 - /bin/bash
npmPackages:
playwright: ^1.64.1 => 1.64.1
- Lingua principale
- TypeScript
- Stelle
- 98.5k
- Fork
- 6.7k
- Merge medio
- 15h 28m
- PR unite (30g)
- 279
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di microsoft/playwright
-
[Bug]: `playwright install` hangs silently for ~8 minutes when PLAYWRIGHT_BROWSERS_PATH is not writableForse già presa @dcrousso l’ha presa 1 giorno fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
microsoft/playwright#43606 ·
I maintainer di solito rispondono entro 1 giorno
-
[Feature]: Set `this.name` in `TargetClosedError` constructorForse di nuovo libera @LeonxLJX l’ha presa 51 giorni fa e non c’è nessuna pull request aperta. ApertaP3-collecting-feedback
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 86/100
microsoft/playwright#42522 · 7 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
microsoft/playwright#43629 ·
I maintainer di solito rispondono entro 1 giorno
-
[Bug]: ariaSnapshot({ mode: 'ai' }) omits iframe contents when the <iframe> element has fallback textForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 30/100
microsoft/playwright#43628 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 52/100
microsoft/playwright#43626 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di microsoft/playwright
Issue simili
-
bug require-triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
expressjs/expressjs.com#2549 · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 83/100
umami-software/umami#4686 · 1 commento ·
I maintainer di solito rispondono entro 2 giorni
-
[Bug]: Device screenshot fails with 500 on iPhone Duo (Xcode 27.2): serve-sim calls simctl without --displayForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
pingdotgg/t3code#24900 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
inu-appcenter/UNIDorm-web#411 ·
I maintainer di solito rispondono entro 1 giorno
-
🐛 bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 2 giorni