mirror of https://github.com/openclaw/openclaw.git
test(gateway): cover trusted backend attestation policy
This commit is contained in:
parent
91de302a70
commit
aa3eb2d444
|
|
@ -1,8 +1,10 @@
|
|||
import { describe, expect, test } from "vitest";
|
||||
import { GATEWAY_CLIENT_IDS, GATEWAY_CLIENT_MODES } from "../../protocol/client-info.js";
|
||||
import {
|
||||
evaluateMissingDeviceIdentity,
|
||||
isTrustedProxyControlUiOperatorAuth,
|
||||
resolveControlUiAuthPolicy,
|
||||
shouldSkipBackendSelfPairing,
|
||||
shouldSkipControlUiPairing,
|
||||
} from "./connect-policy.js";
|
||||
|
||||
|
|
@ -300,4 +302,89 @@ describe("ws connect policy", () => {
|
|||
).toBe(tc.expected);
|
||||
}
|
||||
});
|
||||
|
||||
test("backend self-pairing skip requires trusted local backend handshake conditions", () => {
|
||||
const makeConnectParams = (clientId: string, mode: string) => ({
|
||||
client: {
|
||||
id: clientId,
|
||||
mode,
|
||||
version: "1.0.0",
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
shouldSkipBackendSelfPairing({
|
||||
connectParams: makeConnectParams(
|
||||
GATEWAY_CLIENT_IDS.GATEWAY_CLIENT,
|
||||
GATEWAY_CLIENT_MODES.BACKEND,
|
||||
),
|
||||
isLocalClient: true,
|
||||
hasBrowserOriginHeader: false,
|
||||
sharedAuthOk: true,
|
||||
authMethod: "token",
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
shouldSkipBackendSelfPairing({
|
||||
connectParams: makeConnectParams(
|
||||
GATEWAY_CLIENT_IDS.GATEWAY_CLIENT,
|
||||
GATEWAY_CLIENT_MODES.BACKEND,
|
||||
),
|
||||
isLocalClient: false,
|
||||
hasBrowserOriginHeader: false,
|
||||
sharedAuthOk: true,
|
||||
authMethod: "token",
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
shouldSkipBackendSelfPairing({
|
||||
connectParams: makeConnectParams(
|
||||
GATEWAY_CLIENT_IDS.GATEWAY_CLIENT,
|
||||
GATEWAY_CLIENT_MODES.BACKEND,
|
||||
),
|
||||
isLocalClient: true,
|
||||
hasBrowserOriginHeader: true,
|
||||
sharedAuthOk: true,
|
||||
authMethod: "token",
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
shouldSkipBackendSelfPairing({
|
||||
connectParams: makeConnectParams(
|
||||
GATEWAY_CLIENT_IDS.GATEWAY_CLIENT,
|
||||
GATEWAY_CLIENT_MODES.BACKEND,
|
||||
),
|
||||
isLocalClient: true,
|
||||
hasBrowserOriginHeader: false,
|
||||
sharedAuthOk: false,
|
||||
authMethod: "token",
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
shouldSkipBackendSelfPairing({
|
||||
connectParams: makeConnectParams(
|
||||
GATEWAY_CLIENT_IDS.GATEWAY_CLIENT,
|
||||
GATEWAY_CLIENT_MODES.BACKEND,
|
||||
),
|
||||
isLocalClient: true,
|
||||
hasBrowserOriginHeader: false,
|
||||
sharedAuthOk: true,
|
||||
authMethod: "trusted-proxy",
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
shouldSkipBackendSelfPairing({
|
||||
connectParams: makeConnectParams("not-gateway-client", GATEWAY_CLIENT_MODES.BACKEND),
|
||||
isLocalClient: true,
|
||||
hasBrowserOriginHeader: false,
|
||||
sharedAuthOk: true,
|
||||
authMethod: "token",
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { GATEWAY_CLIENT_IDS, GATEWAY_CLIENT_MODES } from "../../protocol/client-info.js";
|
||||
import type { GatewayAuthResult } from "../../protocol/index.js";
|
||||
import type { ConnectParams } from "../../protocol/index.js";
|
||||
import type { GatewayRole } from "../../role-policy.js";
|
||||
import { roleCanSkipDeviceIdentity } from "../../role-policy.js";
|
||||
|
|
@ -75,6 +77,28 @@ export function isTrustedProxyControlUiOperatorAuth(params: {
|
|||
);
|
||||
}
|
||||
|
||||
export function shouldSkipBackendSelfPairing(params: {
|
||||
connectParams: ConnectParams;
|
||||
isLocalClient: boolean;
|
||||
hasBrowserOriginHeader: boolean;
|
||||
sharedAuthOk: boolean;
|
||||
authMethod: GatewayAuthResult["method"];
|
||||
}): boolean {
|
||||
const isGatewayBackendClient =
|
||||
params.connectParams.client.id === GATEWAY_CLIENT_IDS.GATEWAY_CLIENT &&
|
||||
params.connectParams.client.mode === GATEWAY_CLIENT_MODES.BACKEND;
|
||||
if (!isGatewayBackendClient) {
|
||||
return false;
|
||||
}
|
||||
const usesSharedSecretAuth = params.authMethod === "token" || params.authMethod === "password";
|
||||
return (
|
||||
params.isLocalClient &&
|
||||
!params.hasBrowserOriginHeader &&
|
||||
params.sharedAuthOk &&
|
||||
usesSharedSecretAuth
|
||||
);
|
||||
}
|
||||
|
||||
export type MissingDeviceIdentityDecision =
|
||||
| { kind: "allow" }
|
||||
| { kind: "reject-control-ui-insecure-auth" }
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ import {
|
|||
evaluateMissingDeviceIdentity,
|
||||
isTrustedProxyControlUiOperatorAuth,
|
||||
resolveControlUiAuthPolicy,
|
||||
shouldSkipBackendSelfPairing,
|
||||
shouldSkipControlUiPairing,
|
||||
} from "./connect-policy.js";
|
||||
import {
|
||||
|
|
@ -90,7 +91,6 @@ import {
|
|||
resolveHandshakeBrowserSecurityContext,
|
||||
resolveUnauthorizedHandshakeContext,
|
||||
shouldAllowSilentLocalPairing,
|
||||
shouldSkipBackendSelfPairing,
|
||||
} from "./handshake-auth-helpers.js";
|
||||
import { isUnauthorizedRoleError, UnauthorizedFloodGuard } from "./unauthorized-flood-guard.js";
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue