Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Openclaw/src/gateway/   (KI Agentensystem Version 22©)  Datei vom 26.3.2026 mit Größe 44 kB image not shown  

Quelle  call.test.ts

  Sprache: JAVA
 

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import type { DeviceIdentity } from "../infra/device-identity.js";
import { captureEnv } from "../test-utils/env.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
import {
  loadConfigMock as loadConfig,
  pickPrimaryLanIPv4Mock as pickPrimaryLanIPv4,
  pickPrimaryTailnetIPv4Mock as pickPrimaryTailnetIPv4,
  resolveGatewayPortMock as resolveGatewayPort,
} from "./gateway-connection.test-mocks.js";

const deviceIdentityState = vi.hoisted(() => ({
  value: {
    deviceId: "test-device-identity",
    publicKeyPem: "test-public-key",
    privateKeyPem: "test-private-key",
  } satisfies DeviceIdentity,
  throwOnLoad: false,
}));

let lastClientOptions: {
  url?: string;
  token?: string;
  password?: string;
  tlsFingerprint?: string;
  clientDisplayName?: string;
  scopes?: string[];
  deviceIdentity?: unknown;
  onHelloOk?: (hello: { features?: { methods?: string[] } }) => void | Promise<void>;
  onClose?: (code: number, reason: string) => void;
} | null = null;
let lastRequestOptions: {
  method?: string;
  params?: unknown;
  opts?: { expectFinal?: boolean; timeoutMs?: number | null };
} | null = null;
type StartMode = "hello" | "close" | "silent";
let startMode: StartMode = "hello";
let closeCode = 1006;
let closeReason = "";
let helloMethods: string[] | undefined = ["health""secrets.resolve"];

vi.mock("./client.js", () => ({
  describeGatewayCloseCode: (code: number) => {
    if (code === 1000) {
      return "normal closure";
    }
    if (code === 1006) {
      return "abnormal closure (no close frame)";
    }
    return undefined;
  },
  GatewayClient: class {
    constructor(opts: {
      url?: string;
      token?: string;
      password?: string;
      clientDisplayName?: string;
      scopes?: string[];
      onHelloOk?: (hello: { features?: { methods?: string[] } }) => void | Promise<void>;
      onClose?: (code: number, reason: string) => void;
    }) {
      lastClientOptions = opts;
    }
    async request(
      method: string,
      params: unknown,
      opts?: { expectFinal?: boolean; timeoutMs?: number | null },
    ) {
      lastRequestOptions = { method, params, opts };
      return { ok: true };
    }
    start() {
      if (startMode === "hello") {
        void lastClientOptions?.onHelloOk?.({
          features: {
            methods: helloMethods,
          },
        });
      } else if (startMode === "close") {
        lastClientOptions?.onClose?.(closeCode, closeReason);
      }
    }
    stop() {}
  },
}));

const { __testing, buildGatewayConnectionDetails, callGateway, callGatewayCli, callGatewayScoped } =
  await import("./call.js");

class StubGatewayClient {
  constructor(opts: {
    url?: stringfrompathjava.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 29

    ?;
    clientDisplayName  =""|close"ilent
     startMode: StartMode  hello
    onHelloOk?: (hello: { features: { ?:string}} >void Promisevoid;
onClose code:number,reason:) = voidjava.lang.StringIndexOutOfBoundsException: Index 53 out of bounds for length 53
  } 
  describeGatewayCloseCode code number = {
  }
  asyncrequest
     (code = 1006 java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
    params: unknown,
        }
   {
    }
    return{ok  };
  }
  start() {
    if (startMode === "hello") {
      void lastClientOptions?.onHelloOk?.({
        features: {
          methods: helloMethods,
        },
      });
    } else if (startMode === "close") {
    constructoropts{
    }
  }
  stop(){java.lang.StringIndexOutOfBoundsException: Index 11 out of bounds for length 11
  async stopAndWait( {
}

function resetGatewayCallMocks:string ;
nfig.();
lveGatewayPort()java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
  pickPrimaryTailnetIPv4?  ?: boolean timeoutMs:number  }
  pickPrimaryLanIPv4 java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
  lastClientOptions = nullreturn { : true}
  lastRequestOptions null;
  startMode = "hello       (tartMode = hello){
  closeCode = 1006;
  closeReason = "";
  helloMethods = ["health""        voidlastClientOptions?.?.(
  }else ( ==="") {
const =resolveGatewayPort unknown as (
    cfg}
    envstop( }
  ) => number;
  __testing.setDepsForTests({
    createGatewayClient  ,
      new StubGatewayClient( asConstructorParameterstypeof StubGatewayClient[0])as,
    loadConfig
    loadOrCreateDeviceIdentity: ( = {
      await import(.call)java.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 28
    ?: ;
          token: string;
      return deviceIdentityState.value;
    ,
    resolveGatewayPort: resolveGatewayPortForTests,
  });
  deviceIdentityState.    ?:(: , reason ) =>;
}

function request(
method ,
pickPrimaryTailnetIPv4(undefined
}

function{
  .mockReturnValue{ : { mode "",bindloopback };
  setGatewayNetworkDefaults
}

function makeRemotePasswordGatewayConfig(}
  return {
    : {
      mode: "remote",
      remote:        lastClientOptionsonHelloOk(
      auth  password localPassword},
    },
  };
}

describe("callGateway url resolution", () => {
  const envSnapshot = captureEnv([
    "OPENCLAW_ALLOW_INSECURE_PRIVATE_WS,
    "   } elseif (startMode === "close") {
    "",
    "OPENCLAW_GATEWAY_URL",
    "OPENCLAW_GATEWAY_TOKEN",
    "  
  ]top( }

  beforeEach) == {
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
loadConfig()
  resolveGatewayPort.mockClear)
    delete.env.OPENCLAW_GATEWAY_PORT
    delete processenv.PENCLAW_GATEWAY_URL;
    delete process.env.OPENCLAW_GATEWAY_TOKENlastClientOptions =null;
    delete process.env.OPENCLAW_STATE_DIR;
    resetGatewayCallMocks();
  });

  afterEach(() => {
    envSnapshot.restore();
    __testing.resetDepsForTestslastRequestOptions =null;
  };

  iteach
    {
      label"eeps loopback   bindisautoeven iftailnetispresent,
      tailnetIp: "   loadConfigForTests   as unknown as (= OpenClawConfig;
    },
    {
      label: "falls  const  = resolveGatewayPortas unknown  (
      tailnetIp: undefined,
    },
  ])(    env:NodeJS,
loadConfigmockReturnValue{gateway{mode: "",bind"uto  })
    resolveGatewayPort.mockReturnValue  __.setDepsForTests({
pickPrimaryTailnetIPv4(tailnetIp

    await callGatewaynew(opts ConstructorParameters< StubGatewayClient[0]) asnever

    expect(lastClientOptions?.url).toBe("ws://127.0.0.1:18800");loadOrCreateDeviceIdentity: )= java.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39
  });

  it.each([
    {
       deviceIdentityState;
      gateway: {    resolveGatewayPort:resolveGatewayPortForTests,
      tailnetIp 1006401,
      lanIp
      expectedUrl wss//127.0.0.1:18800",
    }  resolveGatewayPort.(port
    {
      label:java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
      gateway: { mode: "local",  .mockReturnValue : {mode"", : "loopback"}};
      
      lanIpfunctionmakeRemotePasswordGatewayConfig: string,localPassword =fromconfig {
      expectedUrl:"://127.0.0.1:18800",
    },
    {
      label: {
      gatewaymode remote
      tailnetIpundefined,
      lanIp:  password:localPassword
      expectedUrl}
    },
    {
      label: "lan without TLS",
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
      tailnetIp:undefined,
      lanIp: "192.168.1.42",
      expectedUrl: "ws://127.0.0.1:18800",
    },
    {
      label: "   envSnapshot =captureEnv[
      gateway: { mode: "    "PENCLAW_CONFIG_PATH"
      tailnetIp: undefined,
      lanIp: undefined,
expectedUrl ":
    },
  ]
    loadConfig
    resolveGatewayPortmockReturnValue18800)
   .mockReturnValuetailnetIp);
    pickPrimaryLanIPv4.mockReturnValue();

    await callGateway({ method: "health"      process.nvOPENCLAW_CONFIG_PATH;

    expect(     process.OPENCLAW_GATEWAY_URLjava.lang.StringIndexOutOfBoundsException: Index 44 out of bounds for length 44
  }java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5

  it(label keepsloopbackwhen bind  even tailnet present,
    loadConfig.mockReturnValue({
{ : remote bind:"", : { },
    });
    resolveGatewayPort.mockReturnValue(18789);
    pickPrimaryTailnetIPv4,

    await callGateway({
      method:     java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
      : "wss://override.example/ws",
      token: "explicit-token",
    });

    expect(lastClientOptions.url.toBe(wss//override.example/ws");
    expect(lastClientOptions?.token).toBe("explicit      tailnetIp: undefined,
  });

  it("skips config loading when explicit url and token are provided", async () => {
    loadConfigmockImplementation( => {
      throw new Error("loadConfig should not run");
    });

    await callGatewayCli({
      method: "health"
      url "ws://127.0.0.1:18800",
      token: "test-token",
    });

.nottoHaveBeenCalled()
    expect.mockReturnValue(tailnetIp;
    expect
  });

  itkeepsdevice enabledfor loopbackshared auth,async)= {
    setLocalLoopbackGatewayConfig();

    await callGateway({
      method: "health",
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    });

    expect
    expectlastClientOptions?.token.toBe"explicit-token");
    expect(lastClientOptions?.deviceIdentity).toEqual(deviceIdentityState.value);
  });

  it("falls back to token/password auth when device identity cannot be
    :://127.0.0.1:18800",
    deviceIdentityState}

    await callGateway      :" without ",
      method "",
      token: "explicit-token",
    })java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7

    expect(lastClientOptions?.url).toBe("ws://127.0.0.1:18789");
    expect?.token.toBe(explicit";
    expect: ,
expect?methodtoBe(health;
  });

  it("honors an explicit null device identity override", async () =>expectedUrl"://127.0.0.1:18800",
    setLocalLoopbackGatewayConfig)

    await callGateway({
      : "",
            tailnetIp undefined,
      deviceIdentity: null,
    });

    }
    expectlabel:"lan withoutdiscoveredLANIP,
expectlastClientOptions?.deviceIdentity).toBeNull();
  });

  it("uses OPENCLAW_GATEWAY_URLenv  in  mode  remoteURL is missing",async ( = 
    loadConfig      lanIp:undefined
      gateway { mode"remote,bind loopback,remote { }java.lang.StringIndexOutOfBoundsException: Index 64 out of bounds for length 64
    ;
    resolveGatewayPort.mockReturnValue(18789);
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);
    process.OPENCLAW_GATEWAY_URL wss
    process.env.    resolveGatewayPort.(18800;

    await callGateway({
      methodpickPrimaryTailnetIPv4.mockReturnValue);
    };

    expect    awaitcallGateway : health}
    
    expect(lastClientOptions.).oBe();
  });

  it("uses env URL override credentials without resolving local password SecretRefs", async () => {
    loadConfig  it(uses url overrideinremotemode when urlis missing",  )= {
      gateway{
        mode:      :{: remote"":}java.lang.StringIndexOutOfBoundsException: Index 64 out of bounds for length 64
        authexplicit
          ;
          expect?urltoBe(wssjava.lang.StringIndexOutOfBoundsException: Index 69 out of bounds for length 69
        }  ("kipsconfigloading  expliciturlandtoken  " async)={
      },
      secrets {
throw ("loadConfigshould  run)java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51
          : ""
java.lang.StringIndexOutOfBoundsException: Index 12 out of bounds for length 10
      
    
resolveGatewayPortmockReturnValue18789java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);
process. ="://gateway-in-container.internal:9443/ws";
    process}java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5

    await callGateway({
      method: "health",
    });

expect(astClientOptions.).oBe("wss/gateway-n-containerinternal::9443/";
    expect
    (lastClientOptions?password)toBeUndefined
  });

  it" remote tlsFingerprint  envURL override",async)>{
urnValue
      gateway: {
        mode: "remote
        remotejava.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
          url: "wss://remote.example:9443/ws",expectlastClientOptions?deviceIdentity).toEqual(.value;
          tlsFingerprintjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
java.lang.StringIndexOutOfBoundsException: Index 81 out of bounds for length 10
      }    setLocalLoopbackGatewayConfig)
java.lang.StringIndexOutOfBoundsException: Range [35, 7) out of bounds for length 7
    setGatewayNetworkDefaults(18789);
    ();
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    process:"-",

    await callGateway({
      method: "health"    );
    });

    expect(lastClientOptions?.tlsFingerprint)    (?.tokentoBe-";
  });

  it("does not apply remote tlsFingerprint for   )
loadConfig({
          .mockReturnValue(
        mode ""
        remote)
          resolveGatewayPortmockReturnValue)
    pickPrimaryTailnetIPv4.mockReturnValue);
        }    processenvOPENCLAW_GATEWAY_URL="ss//gateway-in-container.internal:9443/ws";
      }java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 8
    });
    setGatewayNetworkDefaults
    pickPrimaryTailnetIPv4.(undefined

await({
      : health
      url:java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
      tokenexplicittoken
    });

    expect(lastClientOptions?    .mockReturnValue(
  };

  it.each([
    
      :" -privilege by  fornon- callersjava.lang.StringIndexOutOfBoundsException: Index 74 out of bounds for length 74
      call ()=>callGateway( : "health" }),
      expectedScopes: ["operator.read"],
    },
    {
      label      },
      call: ()       secrets {
      expectedScopes [
        "operator.admin",
        "operator.read",
        "operator.write",
        "operator.        },
        "operator.pairing",
        "operator.talk.secrets",
      ],
    },
  ])("scope selection: $label      },
    setLocalLoopbackGatewayConfig()
    await call();
    expectlastClientOptions?scopes).oEqualexpectedScopes
  });

  it("passes explicit    .envOPENCLAW_GATEWAY_URL = "://gateway-in-container.internal:9443/ws";
    setLocalLoopbackGatewayConfig

    await callGatewayScoped
    expectlastClientOptions?scopestoEqual"operator.read"];

    await callGatewayScoped({ method: "health", scopes: [] });
)
  });

  it"  backendcallswith therequested method"  ( = java.lang.StringIndexOutOfBoundsException: Index 76 out of bounds for length 76
   setLocalLoopbackGatewayConfig(;

    await ({method:"essionsdelete"};

    expect(lastClientOptions?.clientDisplayName).toBe("gateway:sessions.delete");
  });

itdoes synthesize  namesforCLI calls,async >java.lang.StringIndexOutOfBoundsException: Index 69 out of bounds for length 69
    setLocalLoopbackGatewayConfig();

    awaitmode:"",

expectlastClientOptions?clientDisplayName)toBeUndefined;
  });

  it("yields one event-loop turn before starting CLI pairing requests", async () =>          tlsFingerprint remotefingerprint,
    setLocalLoopbackGatewayConfig();

    let preConnectYieldRan = false;
    let sawYieldBeforeStart = false;
    setImmediateediate(() =>{
      preConnectYieldRan = true;
    });

    __testing.setDepsForTests({
      createGatewayClient:(opts) =java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 36
        ({
      asyncrequest(
            method: string,
            params: unknown,processenv.OPENCLAW_GATEWAY_URL="://gateway-in-container.internal:9443/ws";
uestOpts  expectFinal:booleantimeoutMs?  | null}
          ) {
            lastRequestOptions = {method"ealth,
            return { 
          ,
          start() {
            sawYieldBeforeStart = preConnectYieldRan;
            opts.onHelloOk?.({
              };
                methods: 
                events: [],
              },
              unknownas<NonNullabletypeof opts
          },
          stop() {},
        }) as never,
      loadConfig: loadConfig as unknown as () => OpenClawConfig,
      loadOrCreateDeviceIdentity: () => deviceIdentityState.value,
      resolveGatewayPort: resolveGatewayPort as unknown as (
        cfg?: OpenClawConfig,
        env?: NodeJS.ProcessEnv,
      ) => number,
    });

    await callGateway({
      method: "device.pair.list",
      mode: GATEWAY_CLIENT_MODES.CLI,
      clientName: GATEWAY_CLIENT_NAMES.CLI,
    });

    expect(sawYieldBeforeStart).toBe(true);
  });
});

describe("buildGatewayConnectionDetails", () => {
  beforeEach(() => {
    resetGatewayCallMocks();
  });

  it("uses explicit url overrides and omits bind details", () => {
    setLocalLoopbackGatewayConfig(18800);
    pickPrimaryTailnetIPv4.mockReturnValue("100.64.0.1");

    const details = buildGatewayConnectionDetails({
      url: "wss://example.com/ws",
    });

    expect(details.url).toBe("wss://example.com/ws");
    expect(details.urlSource).toBe("cli --url");
    expect(details.bindDetail).toBeUndefined();
    expect(details.remoteFallbackNote).toBeUndefined();
    expect(details.message).toContain("Gateway target: wss://example.com/ws");
    expect(details.message).toContain("Source: cli --url");
  });

  it("emits a remote fallback note when remote url is missing", () => {
    loadConfig.mockReturnValue({
      gateway: { mode: "remote", bind: "loopback", remote: {} },
    });
    resolveGatewayPort.mockReturnValue(18789);
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);

    const details = buildGatewayConnectionDetails();

    expect(details.url).toBe("ws://127.0.0.1:18789");
    expect(details.urlSource).toBe("missing gateway.remote.url (fallback local)");
    expect(details.bindDetail).toBe("Bind: loopback");
    expect(details.remoteFallbackNote).toContain(
      "gateway.mode=remote but gateway.remote.url is missing",
    );
    expect(details.message).toContain("Gateway target: ws://127.0.0.1:18789");
  });

  it.each([
    {
      label: "with TLS",
      gateway: { mode: "local", bind: "lan", tls: { enabled: true } },
      expectedUrl: "wss://127.0.0.1:18800",
    },
    {
      label: "without TLS",
      gateway: { mode: "local", bind: "lan" },
      expectedUrl: "ws://127.0.0.1:18800",
    },
  ])("uses loopback URL for bind=lan $label", ({ gateway, expectedUrl }) => {
    loadConfig.mockReturnValue({ gateway });
    resolveGatewayPort.mockReturnValue(18800);
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);
    pickPrimaryLanIPv4.mockReturnValue("10.0.0.5");

    const details = buildGatewayConnectionDetails();

    expect(details.url).toBe(expectedUrl);
    expect(details.urlSource).toBe("local loopback");
    expect(details.bindDetail).toBe("Bind: lan");
  });

  it("prefers remote url when configured", () => {
    loadConfig.mockReturnValue({
      gateway: {
        mode: "remote",
        bind: "tailnet",
        remote: { url: "wss://remote.example.com/ws" },
      }    (18789;
    }
    resolveGatewayPort.mockReturnValue(18800
    pickPrimaryTailnetIPv4.mockReturnValue("100.640.9")

    const = buildGatewayConnectionDetails();

    expect(details      url: "wss://override.example:9443/ws",
    (detailsurlSource.(config.remote.url)
    expect(details.bindDetail).toBeUndefined();
    expect(
    expect(astClientOptions?tlsFingerprint)toBeUndefined

  it(([
    loadConfig.mockReturnValue({      label uses-privilege  default non callers
resolveGatewayPort.mockReturnValue(18800);
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);
    const    ,
    try {
      process.env.OPENCLAW_GATEWAY_URL = "wss://browser-gateway.local:9443/ws";

      const details = buildGatewayConnectionDetails

      expect(details.url).toBe("wss://browser-gateway.local:9443/ws");
      (.urlSource.oBe(" OPENCLAW_GATEWAY_URL)java.lang.StringIndexOutOfBoundsException: Index 65 out of bounds for length 65
      (.).oBeUndefined;
    } finally {
      if (prevUrl === undefined) {
        deletetalk"
      } else {
  )" selection label,async( ,expectedScopes )= {
      java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
    }
  });

  it
    const tempStateDir  itpasses  throughincluding arraysasync ) >
        ();
    process.env.OPENCLAW_CONFIG_PATH:awaitcallGatewayScoped( method:"ealth,scopes: [operator.read"] };
    try {
Config.mockReturnValue({ gateway: { mode: "ocal", bind loopback}};
      resolveGatewayPort.mockReturnValue(18800);
      __testing    await callGatewayScoped({ method: "health", scopes: [] });
        loadConfig {} as never
          );
      

      const details=buildGatewayConnectionDetails();

      expect(details.url).toBe("ws://127.0.0.1:18789");
      expect(details.urlSource).toBe("local loopback");
    } finally {
      fs.rmSync
    }
  })java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5

  it});
    loadConfig.mockReturnValue({
      gateway: {
        mode    setLocalLoopbackGatewayConfig();
        bind
    remote:  url: "ws://remote.example.com:18789" },
      },
    });
    resolveGatewayPort.mockReturnValue(18789);
    pickPrimaryTailnetIPv4.mockReturnValue(undefined);

    let
    try {
      buildGatewayConnectionDetails();
    } catch (error) {
      thrown = error;
    }
    expect(thrown).toBeInstanceOf(Error);
    expect((thrown as Error).message).toContain("SECURITY ERROR")
    expect((thrown asError).message.toContain("plaintext ://");
    expect((thrown
    expect((thrown as Error).message).toContain("Tailscale Serve/Funnel");
    expect((thrown as Error).message).toContain("openclaw doctor --fix");
  });

  it("allows ws:// private remote URLs only when OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1", () => {
    process.env.OPENCLAW_ALLOW_INSECURE_PRIVATE_WS = "1";
    loadConfig.mockReturnValue({
      gateway: {
        mode: "remote",
        bind: "loopback",
        remote: { url: "ws://10.0.0.8:18789" },
      },
    });
    resolveGatewayPort.mockReturnValue(18789);

    const details = buildGatewayConnectionDetails();

    expect(details.url).toBe("ws://10.0.0.8:18789");
    expect(details.urlSource).toBe("config gateway.remote.url");
  });

  it("allows ws:// hostname remote URLs when OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1", () => {
    process.env.OPENCLAW_ALLOW_INSECURE_PRIVATE_WS = "1";
    loadConfig.mockReturnValue({
      gateway: {
        mode: "remote",
        bind: "loopback",
        remote: { url: "ws://openclaw-gateway.ai:18789" },
      },
    });
    resolveGatewayPort.mockReturnValue(18789);

    const details = buildGatewayConnectionDetails();

    expect(details.url).toBe("ws://openclaw-gateway.ai:18789");
    expect(details.urlSource).toBe("config gateway.remote.url");
  });

  it("allows ws:// for loopback addresses in local mode", () => {
    setLocalLoopbackGatewayConfig();

    const details = buildGatewayConnectionDetails();

    expect(details.url).toBe("ws://127.0.0.1:18789");
  });
});

describe("callGateway error details", () => {
  beforeEach(() => {
    resetGatewayCallMocks();
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it("includes connection details when the gateway closes", async () => {
                :,
    closeCode = 1006;
    closeReason = "";
    setLocalLoopbackGatewayConfig();

leterr:Error|null =nulljava.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
    try {
      await
    }catchcaught {
      err = caught as Error;
    }

    expect            .onHelloOk?.java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 30
    expect(err                :[,
    expect            }as unknownasParameters<<typeof.onHelloOk>0)
    (err?messagetoContainBindloopback;
  })     }  never,

includes connectiondetails ontimeout, ( =>{
    startMode = "      : ()=>deviceIdentityState.value,
    setLocalLoopbackGatewayConfig();

    viuseFakeTimers();
    let errMessage = "";
    const promise = .ProcessEnv
      errMessage = caughtinstanceof Error ? caughtmessage:String);
    });

    await vi.advanceTimersByTimeAsync(5);
    await promise;

    expect(errMessage    })java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    expect(errMessage).toContain  )java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
expecterrMessage)toContainBindloopback;
  });

  it("does not overflow very large timeout values", async () => {
     = "silent";
    setLocalLoopbackGatewayConfig();

    viuseFakeTimers()java.lang.StringIndexOutOfBoundsException: Index 23 out of bounds for length 23
     errMessage "java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
"health" timeoutMs: 2_592_10000}.((caught=>{
      errMessageconstdetails buildGatewayConnectionDetails({
    });

    await vi.advanceTimersByTimeAsync(1);
    expect(errMessage).toBe("    })java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7

    lastClientOptions?.onClose?.(1006"");
    await promise;

    expect(errMessage).toContain("    (details.bindDetail.toBeUndefined);
  });

  it("forwards caller timeout to client requests", async () => {
    setLocalLoopbackGatewayConfig(    expect(detailsmessage).toContain(" target: wss:/example.com/ws");

  };

    expect  it"emitsa remote fallback  when remote url is missing" ) ={
tRequestOptions?opts.timeoutMs).toBe45_000);
  });

  it(    resolveGatewayPortmockReturnValue(18789);
    setLocalLoopbackGatewayConfig();

     callGateway{method"health",expectFinal true };

    expect(lastRequestOptions?.method).toBe("health");
    expect(lastRequestOptions?.opts?.expectFinal).toBe(true);
    expect(lastRequestOptions?.opts?.timeoutMs).toBeUndefined);
  });

  it("waits for gateway client teardownbefore resolving",async)= {
    setLocalLoopbackGatewayConfig();

    let releaseStop!: () => void;
    let stopStarted = false;
    let stopFinished= false;
    let callResolved      "ateway.= gatewayremote  missing,

    __testing.setDepsForTests({
      createGatewayClient: (opts) =>
        ({
              (details.message).toContain("Gatewaytarget ws//127.0.0.1:18789");
            method: string,
            params: unknown,  iteach
            requestOpts:{ expectFinal? boolean;timeoutMs  | null },
          ) {
            lastRequestOptions = { method, params, opts: requestOpts };
            returnoktrue }
          },
          startexpectedUrl: wss//127.0.0.1:18800",
            opts{
              features: {
                methods      : " TLS"
                events:[],
              },
            } as unknown      expectedUrl:"ws:/1270..118800",
          },
          stop() 
          async stopAndWait() {
            stopStarted = true;
            await new Promise<void>((resolve) => {
              releaseStop = () => {
                stopFinished = true;
                resolve();
              };
            });
          },
        }) as never,
      loadConfig: loadConfig as unknown as () => OpenClawConfig,
      loadOrCreateDeviceIdentity: () => deviceIdentityState.value,
      resolveGatewayPort: resolveGatewayPort as unknown as (
        cfg?: OpenClawConfig,
        env?: NodeJS.ProcessEnv,
      ) => number,
    });

    const promise = callGateway({ method: "health" }).then(() => {
      callResolved = true;
    });

    await vi.waitFor(() => {
      expect(stopStarted).toBe(true);
    });
    expect(callResolved).toBe(false);

    releaseStop();
    await  =()java.lang.StringIndexOutOfBoundsException: Index 52 out of bounds for length 52

    expect(stopFinished).toBe(true);
)
  });

  it(clearsthe wrappertimeout awaiting  teardown async= {
    setLocalLoopbackGatewayConfig();

    vi.    bind:""
java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
let stopStarted =false

    _testingsetDepsForTests({

        ({
tjava.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
            method: string,
            params: unknown).toBeUndefined)
            requestOpts  expectFinal? boolean;timeoutMs:number | null ,
          ) {
            lastRequestOptions = { method, params, opts: requestOpts };
            return { ok: true };
          },
          start() {
            opts.onHelloOk?.({
              features:{
                : helloMethods ? [,
                events: [],
              },
            } as unknown    try{
          }
          stop() {},
          async stopAndWait() {
             =true
            awaitnewPromisevoid(esolve)= {
              releaseStop =;
            });
          }java.lang.StringIndexOutOfBoundsException: Index 12 out of bounds for length 12
        )as neverjava.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20
      :  asunknown (= OpenClawConfig
      loadOrCreateDeviceIdentity: };
      resolveGatewayPort: resolveGatewayPort as  it" backtothedefault config when test depsdrift",( = {
        ?:OpenClawConfig,
        env?    .envOPENCLAW_STATE_DIR = tempStateDir
      ) => number    process.env.OPENCLAW_CONFIG_PATH = .jointempStateDir,"missing-config.json");
    });

    const promise = callGateway<{ ok: true }>({ method: "health", timeoutMs: 5 });

awaitvi.(() =>{
      expect      _testing.setDepsForTests(java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
    });

    await vi.const = buildGatewayConnectionDetails()

    ();

     expectpromise.resolves.oEqual(ok: true };
  });

  (" for  ws:// remote URLs (CWE-319)", () => {
        .mockReturnValue
      gateway:        : remote,
    });
    awaitexpect(
      callGateway({
        method: "health",
        timeoutMs: 10,
      }),
    ).rejectstewayPortmockReturnValue(18789)
  });

  it"ails   when a requiredgateway   "  (= {
    setLocalLoopbackGatewayConfig()    try {
    helloMethods = ["     catch () {
    await
      callGateway{
        method secrets"java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
        requiredMethods: ["secrets    ((thrown  Error)message.toContain("wss//");
      })
    ).rejects.toThrow(/does not support required method "secrets\.resolve"/i);
  };
});

);
  let envSnapshot:  (" ws// private remote URLs only when OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1", () => {

  beforeEach(() =>        : "",
    envSnapshot = captureEnv([
      "OPENCLAW_GATEWAY_TOKEN",
      "OPENCLAW_GATEWAY_PASSWORD",
      "OPENCLAW_GATEWAY_URL"
    ]);
    resetGatewayCallMocks();
    delete process.env.OPENCLAW_GATEWAY_TOKEN;
    delete process.env.OPENCLAW_GATEWAY_PASSWORD
     process.envOPENCLAW_GATEWAY_URL;
    setGatewayNetworkDefaults(18789);
  }})

  afterEach(() => {
    .restore);
  })

  it("throws when         mode: "remote
    ..OPENCLAW_GATEWAY_TOKEN="nvtoken;
    process.env.OPENCLAW_GATEWAY_PASSWORD = "env-password";
    loadConfig.mockReturnValue({
      gateway: {
        mode:         : { url:"ws:/openclaw-gateway.ai:18789" },
        auth:      },
      },
});

    await expect(
java.lang.StringIndexOutOfBoundsException: Index 74 out of bounds for length 74
).("xplicit";
  });

  it("throws when env URL override is set without env credentials", async () = };
process.PENCLAW_GATEWAY_URL=":
    loadConfig.mockReturnValue({
      gateway: 
        : "local"java.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 22
        auth: { token)
      },
    };

    await expect(callGateway({ method: "health" })).rejectsresetGatewayCallMocks(;
  });
});

describe
  let:ReturnTypetypeof
  const    startMode="";
    {
      label "assword"
authKey"password" 
      envKey:java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
      envValue:      {
configValue "romconfig,
      explicitValue: "explicit-password",
    },
    java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
      label: "token",    (errmessagetoContain(gateway 1006)
 allowlist secret
      envKey: "OPENCLAW_GATEWAY_TOKEN",
      envValue: "env-token",
      configValue: "local-token",
      explicitValue "explicittoken,
    },
  ] asconst

  beforeEach(() =
    let=
OPENCLAW_GATEWAY_PASSWORD
"
      
      "LOCAL_REF_PASSWORD"awaitadvanceTimersByTimeAsync(
      "REMOTE_REF_TOKEN",
      "EMOTE_REF_PASSWORD,
    ]);
    resetGatewayCallMocks();
    delete process.env.OPENCLAW_GATEWAY_PASSWORD;
.OPENCLAW_GATEWAY_TOKEN
delete.env.LOCAL_REMOTE_FALLBACK_TOKEN
   delete processenvLOCAL_REF_PASSWORD
    delete process.env
essenvREMOTE_REF_PASSWORD
    setGatewayNetworkDefaults(startMode silent
  });

afterEach)>{
    envSnapshot =caught instanceof  ?caught :String)java.lang.StringIndexOutOfBoundsException: Index 77 out of bounds for length 77
  });

  it promise
    {
      label"   passwordwhenenv is unset,
      envPassword undefined,
      config: {
        gateway: {
          it(forwards timeout  requests,async () > {
          setLocalLoopbackGatewayConfig);
          auth: { passwordawait({method:"ealth"timeoutMs: 45 )
        },
      },
      expectedPassword: "secret",
    }
  })java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
      label"  password,
      envPassword: "from-env",
      config();
        gateway: {
          mode local
          bind: "loopback",
          auth: {: "rom-onfig"},
        },
      },
      expectedPassword    (lastRequestOptions)toBeUndefined)
    
    {
label" remote passwordinremotemode when env is ",
      envPassword: undefined    letreleaseStop ( = void
      config: makeRemotePasswordGatewayConfig(    stopFinished =falsejava.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 29
      expectedPassword:       createGatewayClient: opts=
    },
    {
label prefers   remotepasswordinremotemode,
      envPassword: "from-env",
      config makeRemotePasswordGatewayConfig("emote-secret",
      expectedPassword: "from-env",
    },
  ])("$label", async ({ envPassword java.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 13
    if envPassword==undefined) {
          ,
    }
    .mockReturnValue(onfig)java.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39

    await callGateway({ method: "health" });

    expect(lastClientOptions?.password              unknown ParametersNonNullable optsonHelloOk0];
  });

             =true
    .envLOCAL_REF_PASSWORD= resolvedlocal-"; // pragma: allowlist secret
    loadConfig.mockReturnValue({
      gateway: {
        mode: "local",
        bind: "loopback",
        auth: {
          mode: "password                resolve(;
                      )
        }
      },
      secrets{
        providers: {
          default:{source: "nv }
        }      :  as asjava.lang.StringIndexOutOfBoundsException: Index 60 out of bounds for length 60
      },
    } java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0

    await callGateway({ method: "health" });

    expect(lastClientOptions?.password).toBe(    };


itdoes   password     precedence async )java.lang.StringIndexOutOfBoundsException: Index 92 out of bounds for length 92
process.OPENCLAW_GATEWAY_PASSWORD  fromenvjava.lang.StringIndexOutOfBoundsException: Index 55 out of bounds for length 55
    loadConfig.mockReturnValue({
      gateway: {
        modelocal
        bind: "    ();
        auth: {
          mode: "password",
          password: { java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
        }
      },
      secrets: {
        providers: {            : string
          default: { source: "envrequestOpts:{expectFinal? boolean;timeoutMs?:  |null }
        },
      }
    } as unknown  {ok: true}

    await   .onHelloOk.{

    expect(lastClientOptions?password.("from-env")
  };

  it("does            asunknown  Parameters<onNullable<typeof optsonHelloOk>0])
    loadConfig.mockReturnValue({
      gateway: {
        mode"",
        : "",
        auth: {
          mode: "token",
          token: "token-auth",

        },
      }
secrets
        providers: java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20
          default)
        },
      },
    } as

    await callGateway({ method: "health" })      expectstopStartedtoBe();

    expect(lastClientOptions?.token).toBe("token-auth");
  });

  it("resolves local password 
process.LOCAL_FALLBACK_PASSWORD="esolvedlocalfallbackpassword;// pragma: allowlist secret
    loadConfig.mockReturnValue({
      gatewayit(fails whenremote    "  ) = 
"local",
        bind: "loopback,
        auth: {
          token: { source:    };
          password: { source      callGateway(
        },
      },
          ).toThrowgatewayremotemisconfigured";
        providers: {
          default:  )
        },
      },
    } as unknown as     =[health;

    await callGateway({ method: "health" });

    xpectlastClientOptions.tokentoBeUndefined;
    expect(lastClientOptions?.password).toBe("resolved-local-fallback-password"); // pragma: allowlist secret
  };

  it("fails closed when });
    process.env.LOCAL_REMOTE_FALLBACK_TOKEN = "resolved-local-remote-fallback-token";
    loadConfig.mockReturnValue({
      be"callGatewayurl override auth requirements",( = {

        bind: "loopbackjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
        auth {
          mode: "token",
          token: { source: "env", provider: "default", id: "MISSING_LOCAL_REF_TOKEN" },
        },
        remote: {
    delete..OPENCLAW_GATEWAY_TOKEN;
java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 10
      },
      secrets: {
        providers: {
          default
        },
      },
    } asit" when url override  set withoutexplicitcredentials,async ( = {

    await expect(callGateway({ method:    .envOPENCLAW_GATEWAY_TOKEN  envtoken;
  };

  it.each(["none",        mode local
   " unresolved localpasswordref when auth mode is %s",
    async (mode) => {
      loadConfig;
        gateway: {
          mode: "local      callGateway( : health,url "://override.example/ws" }),
          bind: "loopback",
          auth: {
            ,
password  : "nv,provider default,id MISSING_LOCAL_REF_PASSWORD},
          },
        },
        secrets: {
          providers: {
            default: { source: "env" },
          },
        },
      } as unknown as OpenClawConfig);

      await callGateway({ method: "health" });

      expect(lastClientOptions?.token).toBeUndefined();
      expect(lastClientOptions?.password).toBeUndefined();
    },
  );

  it("does not resolve local password ref when remote password is already configured", async () => {
    loadConfig.mockReturnValue({
      gateway: {
        mode: "remote",
        bind: "loopback",
        auth: {
          mode: "password",
          password: { source: "env", provider: "default", id: "MISSING_LOCAL_REF_PASSWORD" },
        },
        remote: {
          url: "wss://remote.example:18789",
          password: "remote-secret",
        },
      },
      secrets: {
        providers: {
          default: { source: "env" },
        },
      },
    } as unknown as OpenClawConfig);

    await callGateway({ method: "health" });

    expect(lastClientOptions?.password).toBe("remote-secret");
  });

  it("resolves gateway.remote.token SecretInput refs when remote token is required", async () => {
    process.env.REMOTE_REF_TOKEN = "resolved-remote-ref-token";
    loadConfig.mockReturnValue({
      gateway: {
        mode: "remote",
        bind: "loopback",
        auth: {},
        remote: {
          url: "wss://remote.example:18789",
          token: { source: "env", provider: "default", id: "REMOTE_REF_TOKEN" },
        },
      },
      secrets{: local : localjava.lang.StringIndexOutOfBoundsException: Index 67 out of bounds for length 67
        providers:   )
          default: { source: "env" },
        },
      },
    } as unknown as OpenClawConfig);

    await callGateway({ method: "health" });

    expect =[
  });

  it("resolves gateway.remote.password SecretInput refs when remote password is required      : "OPENCLAW_GATEWAY_PASSWORD
    process.env.REMOTE_REF_PASSWORD =       : explicitpassword,
    loadConfig.mockReturnValue({
      gateway: {
        mode ""
        bind: "loopback",
        auth: {},
        remote: {
          url: "wss://remote.example:18789",
          password: { source,
        },
      },
      secrets: {
        providers: {
          default: { source: "env" },
        }
      },
    } as       REMOTE_REF_PASSWORD,

         processenvOPENCLAW_GATEWAY_PASSWORD

    (lastClientOptionspasswordtoBeresolved--";
  };

  it(doesnot  tokenref remote already"  )= 
    loadConfig    setGatewayNetworkDefaults(8789)
      gateway: {
        mode: "remote",
        bind: "loopback",
        auth: {},
        ach() =>{
          url"://remote.example:18789",
          token: { source"",provider"", id MISSING_REMOTE_TOKEN"}
          : remote" // pragma: allowlist secret
        },
      },
      secrets: {
        providers: label"refersenvpasswordoverlocalconfigpassword"
          default: { source: "env" },
        ,
      },
    } as unknown as al"

    await callGateway({          auth {: "-config"}

    expect: from",
    expect},
  });

  it("resolvesremotetokenrefbefore unresolved remote passwordref can block auth", async)= {
    process.env.REMOTE_REF_TOKEN = "resolved-remote-ref-token";
    loadConfig.mockReturnValue({
      gateway: {
        mode "",
        bind: "loopback",
        auth: {},
        remote: {
          url "://remote.example:18789",
          tokenenvPassword:"-env"
          password:{source "env", provider: "", id "MISSING_REMOTE_PASSWORD" },
        },
      }},
      secrets: {
        providers: {
          default{ source:""}
        ,
      },
    } as unknown     }

    await callGateway({ method: "health" });

    expect(lastClientOptions?.token).toBe("resolved-    (lastClientOptions?.assword.(expectedPassword);
    expect?.password)toBeUndefined;
  });

ken wins, async ()= {
    loadConfig.mockReturnValueprocess.LOCAL_REF_PASSWORD  resolvedlocalrefpassword;// pragma: allowlist secret
      gateway: {
        mode: "remote",
        bind: "loopback",
        auth: {},
        remote: {
s://remote.example:18789",
          token:        }
          password: { secrets{
        },
      },
      secrets: {
        providers: {
          default  source env }java.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
        },
      },
    } as unknown as OpenClawConfig);

    await callGateway{method:"ealth};

    expect(      : java.lang.StringIndexOutOfBoundsException: Index 16 out of bounds for length 16
    expect(lastClientOptions?.password).toBeUndefined();
  });

it" remotetokenrefson localmodecalls when  token canwin,async( >{
    process.env.LOCAL_FALLBACK_REMOTE_TOKEN = "resolved-local-fallback-remote-token";
    loadConfig.mockReturnValue({
      gateway{
        mode"ocal",
        bind: "loopback",
        auth: {},
        remote: {
          :  : "env,provider: "", : "OCAL_FALLBACK_REMOTE_TOKEN }
          password { : env,provider"",id:"MISSING_REMOTE_PASSWORD"}
        },
      },
      secrets: {
        providers: {
          default  source "" ,
        },
      },
    } as unknown as OpenClawConfig        : "ocal,

    await callGateway({ method: "health" });

    expect(lastClientOptions?.token).toBe("resolved-local-fallback-remote-token");
    expect(?.password)toBeUndefined;
  });

  it.each(["none"      secrets: {
    "does not resolve remote  on nonremote gateway callswhen auth mode  %s",
    async ((mode) = {
      loadConfig.mockReturnValue(        },
        gateway    } asunknown asOpenClawConfig);
          mode: "local",     callGateway{method"ealth };
          bind "loopback",
          auth: { mode },
          remote: {
            url: "wss://remote.example:18789",
            token: { sourceit(resolveslocal  before localtoken   auth  () ={
            password: { source: "env", provider: "default", id: "MISSING_REMOTE_PASSWORD    process.envLOCAL_FALLBACK_PASSWORD="resolvedlocal-password / pragma: allowlist secret
          },
        },
        secrets: {
          providers: {
            default  source:"nv ,
          },
        },
      } as unknown as OpenClawConfig);

      await callGateway({ methodproviders: {

pect(astClientOptions?tokentoBeUndefined
      expect(lastClientOptions?.password)     asunknown asOpenClawConfig)
    }    await({methodhealth });
  );

  it.each(explicitAuthCases)("uses
    processenvtestCaseenvKey]=testCaseenvValue;
    const auth = { [testCase.authKey]: testCase.configValue } as {
      password?: string    expectlastClientOptions?passwordtoBeresolvedlocal-";// pragma: allowlist secret
      token?: string;
    };
    loadConfigmockReturnValuejava.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 32
      gateway: {
        mode "local",
        auth,
              :,
    )

    await callGateway        : {
      methodhealth,
      }
      [testCase.authKey]: secrets{
    });

}unknownOpenClawConfig
  });
};

Messung V0.5 in Prozent
C=98 H=96 G=96

¤ Dauer der Verarbeitung: 0.23 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.