mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 14:16:43 +00:00
fix(browser): block sensitive header intercept matchers
Squash merge PR #1294 into dev.
This commit is contained in:
@@ -83,6 +83,16 @@ const TOOL_HOVER = 'browser_hover';
|
||||
const TOOL_QUERY = 'browser_query';
|
||||
const TOOL_TAKE_ELEMENT_SCREENSHOT = 'browser_take_element_screenshot';
|
||||
const TOOL_WAIT_FOR_EVENT = 'browser_wait_for_event';
|
||||
const SENSITIVE_INTERCEPT_HEADER_NAMES = new Set([
|
||||
'authorization',
|
||||
'cookie',
|
||||
'cookie2',
|
||||
'proxy-authorization',
|
||||
'x-api-key',
|
||||
'x-api-token',
|
||||
'x-auth-token',
|
||||
]);
|
||||
|
||||
const TOOL_NAMES = [
|
||||
TOOL_SESSION_INFO,
|
||||
TOOL_URL_TITLE,
|
||||
@@ -612,10 +622,15 @@ function getTools() {
|
||||
urlRegex: { type: 'string' },
|
||||
headerMatchers: {
|
||||
type: 'array',
|
||||
description:
|
||||
'Match non-sensitive request headers. Cookie, Authorization, and token headers are not allowed.',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Non-sensitive request header name to match.',
|
||||
},
|
||||
valueIncludes: { type: 'string' },
|
||||
valueRegex: { type: 'string' },
|
||||
},
|
||||
@@ -1379,6 +1394,10 @@ function parseOptionalPriority(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
function isSensitiveInterceptHeaderName(name) {
|
||||
return SENSITIVE_INTERCEPT_HEADER_NAMES.has(name.toLowerCase());
|
||||
}
|
||||
|
||||
function parseOptionalHeaderMatchers(value) {
|
||||
if (value === undefined) {
|
||||
return [];
|
||||
@@ -1391,6 +1410,9 @@ function parseOptionalHeaderMatchers(value) {
|
||||
throw new Error('headerMatchers entries must be objects');
|
||||
}
|
||||
const name = requireNonEmptyString(entry.name, 'headerMatchers.name');
|
||||
if (isSensitiveInterceptHeaderName(name)) {
|
||||
throw new Error(`headerMatchers.name cannot target sensitive request header: ${name}`);
|
||||
}
|
||||
const valueIncludes =
|
||||
entry.valueIncludes === undefined
|
||||
? ''
|
||||
|
||||
@@ -1779,6 +1779,46 @@ describe('ccs-browser MCP server - session and interception', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects intercept rules that match sensitive request headers', async () => {
|
||||
const responses = await runMcpRequests(
|
||||
[{ id: 'page-1', title: 'Home', currentUrl: 'https://example.com/' }],
|
||||
[
|
||||
{
|
||||
jsonrpc: '2.0',
|
||||
id: 1003,
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'browser_add_intercept_rule',
|
||||
arguments: {
|
||||
headerMatchers: [{ name: 'Cookie', valueRegex: '^session=' }],
|
||||
action: 'continue',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
jsonrpc: '2.0',
|
||||
id: 1004,
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'browser_add_intercept_rule',
|
||||
arguments: {
|
||||
headerMatchers: [{ name: 'authorization', valueIncludes: 'Bearer ' }],
|
||||
action: 'continue',
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
for (const id of [1003, 1004]) {
|
||||
const response = responses.find((message) => message.id === id);
|
||||
expect((response?.result as { isError?: boolean }).isError).toBe(true);
|
||||
expect(getResponseText(response)).toContain(
|
||||
'Browser MCP failed: headerMatchers.name cannot target sensitive request header'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('removes fulfill rules and request summaries after the bound page is closed', async () => {
|
||||
const responses = await runMcpRequests(
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user