mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 07:47:28 +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/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.
|
- 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.
|
- 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/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.
|
- 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;
|
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: {
|
function resolveRequestedProfile(params: {
|
||||||
query?: Record<string, unknown>;
|
query?: Record<string, unknown>;
|
||||||
body?: unknown;
|
body?: unknown;
|
||||||
@@ -167,6 +187,17 @@ export const browserHandlers: GatewayRequestHandlers = {
|
|||||||
);
|
);
|
||||||
return;
|
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();
|
const cfg = loadConfig();
|
||||||
let nodeTarget: NodeSession | null = null;
|
let nodeTarget: NodeSession | null = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user