Gateway: block profile mutations via browser.request (#43800)

* Gateway: block profile mutations via browser.request

* Changelog: note GHSA-vmhq browser request fix

* Gateway: normalize browser.request profile guard paths
This commit is contained in:
Vincent Koc
2026-03-12 04:21:03 -04:00
committed by GitHub
parent 46a332385d
commit f37815b323
3 changed files with 70 additions and 0 deletions

View File

@@ -100,4 +100,42 @@ describe("browser.request profile selection", () => {
}),
);
});
it.each([
{
method: "POST",
path: "/profiles/create",
body: { name: "poc", cdpUrl: "http://10.0.0.42:9222" },
},
{
method: "DELETE",
path: "/profiles/poc",
body: undefined,
},
{
method: "POST",
path: "profiles/create",
body: { name: "poc", cdpUrl: "http://10.0.0.42:9222" },
},
{
method: "DELETE",
path: "profiles/poc",
body: undefined,
},
])("blocks persistent profile mutations for $method $path", async ({ method, path, body }) => {
const { respond, nodeRegistry } = await runBrowserRequest({
method,
path,
body,
});
expect(nodeRegistry.invoke).not.toHaveBeenCalled();
expect(respond).toHaveBeenCalledWith(
false,
undefined,
expect.objectContaining({
message: "browser.request cannot create or delete persistent browser profiles",
}),
);
});
});

View File

@@ -20,6 +20,26 @@ type BrowserRequestParams = {
timeoutMs?: number;
};
function normalizeBrowserRequestPath(value: string): string {
const trimmed = value.trim();
if (!trimmed) {
return trimmed;
}
const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
if (withLeadingSlash.length <= 1) {
return withLeadingSlash;
}
return withLeadingSlash.replace(/\/+$/, "");
}
function isPersistentBrowserProfileMutation(method: string, path: string): boolean {
const normalizedPath = normalizeBrowserRequestPath(path);
if (method === "POST" && normalizedPath === "/profiles/create") {
return true;
}
return method === "DELETE" && /^\/profiles\/[^/]+$/.test(normalizedPath);
}
function resolveRequestedProfile(params: {
query?: Record<string, unknown>;
body?: unknown;
@@ -167,6 +187,17 @@ export const browserHandlers: GatewayRequestHandlers = {
);
return;
}
if (isPersistentBrowserProfileMutation(methodRaw, path)) {
respond(
false,
undefined,
errorShape(
ErrorCodes.INVALID_REQUEST,
"browser.request cannot create or delete persistent browser profiles",
),
);
return;
}
const cfg = loadConfig();
let nodeTarget: NodeSession | null = null;