fix(gateway): block node role when device identity is missing
This commit is contained in:
@@ -125,6 +125,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/Security: remove shared-IP fallback for canvas endpoints and require token or session capability for canvas access. Thanks @thewilloftheshadow.
|
||||
- Gateway/Security: require secure context and paired-device checks for Control UI auth even when `gateway.controlUi.allowInsecureAuth` is set, and align audit messaging with the hardened behavior. (#20684) Thanks @coygeek and @Vasco0x4 for reporting.
|
||||
- Gateway/Security: scope tokenless Tailscale forwarded-header auth to Control UI websocket auth only, so HTTP gateway routes still require token/password even on trusted hosts. Thanks @zpbrent for reporting.
|
||||
- Gateway/Security: require device identity for `role: node` websocket connections even when shared-token auth succeeds, preventing unpaired device-less clients from invoking `node.event`. Thanks @tdjackey for reporting.
|
||||
- Docker/Security: run E2E and install-sh test images as non-root by adding appuser directives. Thanks @thewilloftheshadow.
|
||||
- Skills/Security: sanitize skill env overrides to block unsafe runtime injection variables and only allow sensitive keys when declared in skill metadata, with warnings for suspicious values. Thanks @thewilloftheshadow.
|
||||
- Security/Commands: block prototype-key injection in runtime `/debug` overrides and require own-property checks for gated command flags (`bash`, `config`, `debug`) so inherited prototype values cannot enable privileged commands. Thanks @tdjackey for reporting.
|
||||
|
||||
@@ -363,6 +363,28 @@ describe("gateway server auth/connect", () => {
|
||||
await expectMissingScopeAfterConnect(port, { device: null });
|
||||
});
|
||||
|
||||
test("rejects node role when device identity is omitted", async () => {
|
||||
const ws = await openWs(port);
|
||||
const token = resolveGatewayTokenOrEnv();
|
||||
try {
|
||||
const res = await connectReq(ws, {
|
||||
role: "node",
|
||||
token,
|
||||
device: null,
|
||||
client: {
|
||||
id: GATEWAY_CLIENT_NAMES.NODE_HOST,
|
||||
version: "1.0.0",
|
||||
platform: "test",
|
||||
mode: GATEWAY_CLIENT_MODES.NODE,
|
||||
},
|
||||
});
|
||||
expect(res.ok).toBe(false);
|
||||
expect(res.error?.message ?? "").toContain("device identity required");
|
||||
} finally {
|
||||
ws.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("allows health when scopes are empty", async () => {
|
||||
const ws = await openWs(port);
|
||||
try {
|
||||
|
||||
@@ -490,7 +490,7 @@ export function attachGatewayWsMessageHandler(params: {
|
||||
return true;
|
||||
}
|
||||
clearUnboundScopes();
|
||||
const canSkipDevice = sharedAuthOk;
|
||||
const canSkipDevice = role === "operator" && sharedAuthOk;
|
||||
|
||||
if (isControlUi && !controlUiAuthPolicy.allowBypass) {
|
||||
const errorMessage =
|
||||
|
||||
Reference in New Issue
Block a user