mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-16 12:30:49 +00:00
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:
@@ -12,6 +12,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Security/host env: block inherited `GIT_EXEC_PATH` from sanitized host exec environments so Git helper resolution cannot be steered by host environment state. (`GHSA-jf5v-pqgw-gm5m`)(#43685) Thanks @zpbrent and @vincentkoc.
|
||||
- Security/session_status: enforce sandbox session-tree visibility and shared agent-to-agent access guards before reading or mutating target session state, so sandboxed subagents can no longer inspect parent session metadata or write parent model overrides via `session_status`. (`GHSA-wcxr-59v9-rxr8`)(#43754) Thanks @tdjackey and @vincentkoc.
|
||||
- Models/secrets: enforce source-managed SecretRef markers in generated `models.json` so runtime-resolved provider secrets are not persisted when runtime projection is skipped. (#43759) Thanks @joshavant.
|
||||
- Security/browser.request: block persistent browser profile create/delete routes from write-scoped `browser.request` so callers can no longer persist admin-only browser profile changes through the browser control surface. (`GHSA-vmhq-cqm9-6p7q`)(#43800) Thanks @tdjackey and @vincentkoc.
|
||||
- Security/agent: reject public spawned-run lineage fields and keep workspace inheritance on the internal spawned-session path so external `agent` callers can no longer override the gateway workspace boundary. (`GHSA-2rqg-gjgv-84jm`)(#43801) Thanks @tdjackey and @vincentkoc.
|
||||
- Security/exec allowlist: preserve POSIX case sensitivity and keep `?` within a single path segment so exact-looking allowlist patterns no longer overmatch executables across case or directory boundaries. (`GHSA-f8r2-vg7x-gh8m`)(#43798) Thanks @zpbrent and @vincentkoc.
|
||||
|
||||
|
||||
@@ -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",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user