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

Quelle  net.test.ts

  Sprache: JAVA
 

Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import os from "node:os";
import { afterEach, describe, expect, it, vi } from "vitest";
import { makeNetworkInterfacesSnapshot } from "../test-helpers/network-interfaces.js";
import {
  __resetContainerCacheForTest,
  defaultGatewayBindMode,
  isContainerEnvironment,
  isLocalishHost,
  isLoopbackHost,
  isPrivateOrLoopbackAddress,
  isPrivateOrLoopbackHost,
  isSecureWebSocketUrl,
  isTrustedProxyAddress,
  pickPrimaryLanIPv4,
  resolveClientIp,
  resolveGatewayBindHost,
  resolveGatewayListenHosts,
  resolveHostName,
} from "./net.js";

describe("resolveHostName", () => {
  it.each([
    { input: "localhost:18789", expected: "localhost" },
    { input: "127.0.0.1:18789", expected: "127.0.0.1" },
    { input: "[::1]:18789", expected: "::1" },
    { input: "::1", expected: "::1" },
  ] as const)("normalizes host form for $input", ({ input, expected }) => {
    expect(resolveHostName(input), input).toBe(expected);
  });
});

describe("isLocalishHost", () => {
  it("accepts loopback and tailscale serve/funnel host headers", () => {
    const accepted = [
      "localhost",
      "localhost.:18789",
      "127.0.0.1:18789",
      "[::1]:18789",
      "[::ffff:127.0.0.1]:18789",
      "gateway.tailnet.ts.net",
    ];
    for (const host of accepted) {
      expect(isLocalishHost(host), host).toBe(true);
    }
  });

  it("rejects non-local hosts", () => {
    const rejected = ["example.com", "192.168.1.10", "203.0.113.5:18789"];
    for (const host of rejected) {
      expect(isLocalishHost(host), host).toBe(false);
    }
  });
});

describe("isLoopbackHost", () => {
  it("accepts localhost absolute-form hostnames", () => {
    expect(isLoopbackHost("localhost.")).toBe(true);
    expect(isLoopbackHost("LOCALHOST...")).toBe(true);
  });
});

describe("isTrustedProxyAddress", () => {
  it.each([
    {
      name: "matches exact IP entries",
      ip: "192.168.1.1",
      trustedProxies: ["192.168.1.1"],
      expected: true,
    },
    {
      name: "rejects non-matching exact IP entries",
      ip: "192.168.1.2",
      trustedProxies: ["192.168.1.1"],
      expected: false,
    },
    {
      name: "matches one of multiple exact entries",
      ip: "10.0.0.5",
      trustedProxies: ["192.168.1.1", "10.0.0.5", "172.16.0.1"],
      expected: true,
    },
    {
      name: "ignores surrounding whitespace in exact IP entries",
      ip: "10.0.0.5",
      trustedProxies: [" 10.0.0.5 "],
      expected: true,
    },
    {
      name: "matches /24 CIDR entries",
      ip: "10.42.0.59",
      trustedProxies: ["10.42.0.0/24"],
      expected: true,
    },
    {
      name: "rejects IPs outside /24 CIDR entries",
      ip: "10.42.1.1",
      trustedProxies: ["10.42.0.0/24"],
      expected: false,
    },
    {
      name: "matches /16 CIDR entries",
      ip: "172.19.255.255",
      trustedProxies: ["172.19.0.0/16"],
      expected: true,
    },
    {
      name: "rejects IPs outside /16 CIDR entries",
      ip: "172.20.0.1",
      trustedProxies: ["172.19.0.0/16"],
      expected: false,
    },
    {
      name: "treats /32 as a single-IP CIDR",
      ip: "10.42.0.0",
      trustedProxies: ["10.42.0.0/32"],
      expected: true,
    },
    {
      name: "rejects non-matching /32 CIDR entries",
      ip: "10.42.0.1",
      trustedProxies: ["10.42.0.0/32"],
      expected: false,
    },
    {
      name: "handles mixed exact IP and CIDR entries",
      ip: "172.19.5.100",
      trustedProxies: ["192.168.1.1", "10.42.0.0/24", "172.19.0.0/16"],
      expected: true,
    },
    {
      name: "rejects IPs missing from mixed exact IP and CIDR entries",
      ip: "10.43.0.1",
      trustedProxies: ["192.168.1.1", "10.42.0.0/24", "172.19.0.0/16"],
      expected: false,
    },
    {
      name: "supports IPv6 CIDR notation",
      ip: "2001:db8::1234",
      trustedProxies: ["2001:db8::/32"],
      expected: true,
    },
    {
      name: "rejects IPv6 addresses outside the configured CIDR",
      ip: "2001:db9::1234",
      trustedProxies: ["2001:db8::/32"],
      expected: false,
    },
    {
      name: "preserves exact matching behavior for plain IP entries",
      ip: "10.42.0.59",
      trustedProxies: ["10.42.0.1"],
      expected: false,
    },
    {
      name: "normalizes IPv4-mapped IPv6 addresses",
      ip: "::ffff:192.168.1.1",
      trustedProxies: ["192.168.1.1"],
      expected: true,
    },
    {
      name: "returns false when IP is undefined",
      ip: undefined,
      trustedProxies: ["192.168.1.1"],
      expected: false,
    },
    {
      name: "returns false when trusted proxies are undefined",
      ip: "192.168.1.1",
      trustedProxies: undefined,
      expected: false,
    },
    {
      name: "returns false when trusted proxies are empty",
      ip: "192.168.1.1",
      trustedProxies: [],
      expected: false,
    },
    {
      name: "rejects invalid CIDR prefixes and addresses",
      ip: "10.42.0.59",
      trustedProxies: ["10.42.0.0/33", "10.42.0.0/-1", "invalid/24", "2001:db8::/129"],
      expected: false,
    },
    {
      name: "ignores surrounding whitespace in CIDR entries",
      ip: "10.42.0.59",
      trustedProxies: [" 10.42.0.0/24 "],
      expected: true,
    },
    {
      name: "ignores blank trusted proxy entries",
      ip: "10.0.0.5",
      trustedProxies: [" ", "10.0.0.5", ""],
      expected: true,
    },
    {
      name: "treats all-blank trusted proxy entries as no match",
      ip: "10.0.0.5",
      trustedProxies: [" ", "\t"],
      expected: false,
    },
  ])("$name", ({ ip, trustedProxies, expected }) => {
    expect(isTrustedProxyAddress(ip, trustedProxies)).toBe(expected);
  });
});

describe("resolveClientIp", () => {
  it.each([
    {
      name: "returns remote IP when remote is not trusted proxy",
      remoteAddr: "203.0.113.10",
      forwardedFor: "10.0.0.2",
      trustedProxies: ["127.0.0.1"],
      expected: "203.0.113.10",
    },
    {
      name: "uses right-most untrusted X-Forwarded-For hop",
      remoteAddr: "127.0.0.1",
      forwardedFor: "198.51.100.99, 10.0.0.9, 127.0.0.1",
      trustedProxies: ["127.0.0.1"],
      expected: "10.0.0.9",
    },
    {
      name: "ignores spoofed loopback X-Forwarded-For hops from trusted proxies",
      remoteAddr: "10.0.0.50",
      forwardedFor: "127.0.0.1",
      trustedProxies: ["10.0.0.0/8"],
      expected: undefined,
    },
    {
      name: "fails closed when all X-Forwarded-For hops are trusted proxies",
      remoteAddr: "127.0.0.1",
      forwardedFor: "127.0.0.1, ::1",
      trustedProxies: ["127.0.0.1", "::1"],
      expected: undefined,
    },
    {
      name: "fails closed when all non-loopback X-Forwarded-For hops are trusted proxies",
      remoteAddr: "10.0.0.50",
      forwardedFor: "10.0.0.2, 10.0.0.1",
      trustedProxies: ["10.0.0.0/8"],
      expected: undefined,
    },
    {
      name: "fails closed when trusted proxy omits forwarding headers",
      remoteAddr: "127.0.0.1",
      trustedProxies: ["127.0.0.1"],
      expected: undefined,
    },
    {
      name: "ignores invalid X-Forwarded-For entries",
      remoteAddr: "127.0.0.1",
      forwardedFor: "garbage, 10.0.0.999",
      trustedProxies: ["127.0.0.1"],
      expected: undefined,
    },
    {
      name: "does not trust X-Real-IP by default",
      remoteAddr: "127.0.0.1",
      realIp: "[2001:db8::5]",
      trustedProxies: ["127.0.0.1"],
      expected: undefined,
    },
    {
      name: "uses X-Real-IP only when explicitly enabled",
      remoteAddr: "127.0.0.1",
      realIp: "[2001:db8::5]",
      trustedProxies: ["127.0.0.1"],
      allowRealIpFallback: true,
      expected: "2001:db8::5",
    },
    {
      name: "ignores invalid X-Real-IP even when fallback enabled",
      remoteAddr: "127.0.0.1",
      realIp: "not-an-ip",
      trustedProxies: ["127.0.0.1"],
      allowRealIpFallback: true,
      expected: undefined,
    },
  ])("$name", (testCase) => {
    const ip = resolveClientIp({
      remoteAddr: testCase.remoteAddr,
      forwardedFor: testCase.forwardedFor,
      realIp: testCase.realIp,
      trustedProxies: testCase.trustedProxies,
      allowRealIpFallback: testCase.allowRealIpFallback,
    });
    expect(ip).toBe(testCase.expected);
  });
});

describe("resolveGatewayListenHosts", () => {
  it.each([
    {
      name: "non-loopback host passthrough",
      host: "0.0.0.0",
      canBindToHost: async () => {
        throw new Error("should not be called");
      },
      expected: ["0.0.0.0"],
    },
    {
      name: "loopback with IPv6 available",
      host: "127.0.0.1",
      canBindToHost: async () => true,
      expected: ["127.0.0.1", "::1"],
    },
    {
      name: "loopback with IPv6 unavailable",
      host: "127.0.0.1",
      canBindToHost: async () => false,
      expected: ["127.0.0.1"],
    },
  ] as const)("resolves listen hosts: $name", async ({ host, canBindToHost, expected }) => {
    const hosts = await resolveGatewayListenHosts(host, {
      canBindToHost,
    });
    expect(hosts).toEqual(expected);
  });
});

describe("pickPrimaryLanIPv4", () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  it.each([
    {
      name: "prefers en0",
      interfaces: makeNetworkInterfacesSnapshot({
        lo0: [{ address: "127.0.0.1", family: "IPv4", internal: true }],
        en0: [{ address: "192.168.1.42", family: "IPv4" }],
      }),
      expected: "192.168.1.42",
    },
    {
      name: "falls back to eth0",
      interfaces: makeNetworkInterfacesSnapshot({
        lo: [{ address: "127.0.0.1", family: "IPv4", internal: true }],
        eth0: [{ address: "10.0.0.5", family: "IPv4" }],
      }),
      expected: "10.0.0.5",
    },
    {
      name: "falls back to any non-internal interface",
      interfaces: makeNetworkInterfacesSnapshot({
        lo: [{ address: "127.0.0.1", family: "IPv4", internal: true }],
        wlan0: [{ address: "172.16.0.99", family: "IPv4" }],
      }),
      expected: "172.16.0.99",
    },
    {
      name: "no non-internal interface",
      interfaces: makeNetworkInterfacesSnapshot({
        lo: [{ address: "127.0.0.1", family: "IPv4", internal: true }],
      }),
      expected: undefined,
    },
  ] as const)(
    "prefers en0, then eth0, then any non-internal IPv4: $name",
    ({ interfaces, expected }) => {
      vi.spyOn(os, "networkInterfaces").mockReturnValue(interfaces);
      expect(pickPrimaryLanIPv4()).toBe(expected);
    },
  );

  it("throws when interface discovery throws", () => {
    vi.spyOn(os, "networkInterfaces").mockImplementation(() => {
      throw new Error("uv_interface_addresses failed");
    });
    expect(() => pickPrimaryLanIPv4()).toThrow("uv_interface_addresses failed");
  });
});

describe("isPrivateOrLoopbackAddress", () => {
  it("accepts loopback, private, link-local, and cgnat ranges", () => {
    const accepted = [
      "127.0.0.1",
      "::1",
      "10.1.2.3",
      "172.16.0.1",
      "172.31.255.254",
      "192.168.0.1",
      "169.254.10.20",
      "100.64.0.1",
      "100.127.255.254",
      "::ffff:100.100.100.100",
      "fc00::1",
      "fd12:3456:789a::1",
      "fe80::1",
      "fe9a::1",
      "febb::1",
    ];
    for (const ip of accepted) {
      expect(isPrivateOrLoopbackAddress(ip)).toBe(true);
    }
  });

  it("rejects public addresses", () => {
    const rejected = ["1.1.1.1", "8.8.8.8", "172.32.0.1", "203.0.113.10", "2001:4860:4860::8888"];
    for (const ip of rejected) {
      expect(isPrivateOrLoopbackAddress(ip)).toBe(false);
    }
  });
});

describe("isPrivateOrLoopbackHost", () => {
  it("accepts localhost", () => {
    expect(isPrivateOrLoopbackHost("localhost")).toBe(true);
    expect(isPrivateOrLoopbackHost("localhost.")).toBe(true);
  });

  it("accepts loopback addresses", () => {
    expect(isPrivateOrLoopbackHost("127.0.0.1")).toBe(true);
    expect(isPrivateOrLoopbackHost("::1")).toBe(true);
    expect(isPrivateOrLoopbackHost("[::1]")).toBe(true);
  });

  it("accepts RFC 1918 private addresses", () => {
    expect(isPrivateOrLoopbackHost("10.0.0.5")).toBe(true);
    expect(isPrivateOrLoopbackHost("10.42.1.100")).toBe(true);
    expect(isPrivateOrLoopbackHost("172.16.0.1")).toBe(true);
    expect(isPrivateOrLoopbackHost("172.31.255.254")).toBe(true);
    expect(isPrivateOrLoopbackHost("192.168.1.100")).toBe(true);
  });

  it("accepts CGNAT and link-local addresses", () => {
    expect(isPrivateOrLoopbackHost("100.64.0.1")).toBe(true);
    expect(isPrivateOrLoopbackHost("169.254.10.20")).toBe(true);
  });

  it("accepts IPv6 private addresses", () => {
    expect(isPrivateOrLoopbackHost("[fc00::1]")).toBe(true);
    expect(isPrivateOrLoopbackHost("[fd12:3456:789a::1]")).toBe(true);
    expect(isPrivateOrLoopbackHost("[fe80::1]")).toBe(true);
  });

  it("rejects unspecified IPv6 address (::)", () => {
    expect(isPrivateOrLoopbackHost("[::]")).toBe(false);
    expect(isPrivateOrLoopbackHost("::")).toBe(false);
    expect(isPrivateOrLoopbackHost("0:0::0")).toBe(false);
    expect(isPrivateOrLoopbackHost("[0:0::0]")).toBe(false);
    expect(isPrivateOrLoopbackHost("[0000:0000:0000:0000:0000:0000:0000:0000]")).toBe(false);
  });

  it("rejects multicast IPv6 addresses (ff00::/8)", () => {
    expect(isPrivateOrLoopbackHost("[ff02::1]")).toBe(false);
    expect(isPrivateOrLoopbackHost("[ff05::2]")).toBe(false);
    expect(isPrivateOrLoopbackHost("[ff0e::1]")).toBe(false);
  });

  it("rejects public addresses", () => {
    expect(isPrivateOrLoopbackHost("1.1.1.1")).toBe(false);
    expect(isPrivateOrLoopbackHost("8.8.8.8")).toBe(false);
    expect(isPrivateOrLoopbackHost("203.0.113.10")).toBe(false);
  });

  it("rejects empty/falsy input", () => {
    expect(isPrivateOrLoopbackHost("")).toBe(false);
  });
});

describe("isContainerEnvironment", () => {
  afterEach(() => {
    __resetContainerCacheForTest();
    vi.restoreAllMocks();
  });

  it("returns false on a typical non-container host", () => {
    // Mock fs.accessSync to throw (no /.dockerenv) and fs.readFileSync to
    // return a cgroup file without container markers.
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => {
      throw new Error("ENOENT");
    });
    vi.spyOn(fs, "readFileSync").mockReturnValue("12:memory:/user.slice/user-1000.slice\n");
    expect(isContainerEnvironment()).toBe(false);
  });

  it("returns true when /.dockerenv exists", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => undefined);
    expect(isContainerEnvironment()).toBe(true);
  });

  it("returns true when /run/.containerenv exists", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation((filePath: unknown) => {
      if (filePath === "/run/.containerenv") {
        return undefined;
      }
      throw new Error("ENOENT");
    });
    expect(isContainerEnvironment()).toBe(true);
  });

  it("returns true when /proc/1/cgroup contains docker marker", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => {
      throw new Error("ENOENT");
    });
    vi.spyOn(fs, "readFileSync").mockReturnValue("12:memory:/docker/abc123def456\n");
    expect(isContainerEnvironment()).toBe(true);
  });

  it("returns true when /proc/1/cgroup contains kubepods marker", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => {
      throw new Error("ENOENT");
    });
    vi.spyOn(fs, "readFileSync").mockReturnValue("11:cpuset:/kubepods/besteffort/pod-abc\n");
    expect(isContainerEnvironment()).toBe(true);
  });

  it("returns true when /proc/1/cgroup contains containerd with container ID", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => {
      throw new Error("ENOENT");
    });
    vi.spyOn(fs, "readFileSync").mockReturnValue(
      "0::/system.slice/containerd/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\n",
    );
    expect(isContainerEnvironment()).toBe(true);
  });

  it("returns false when /proc/1/cgroup contains containerd.service (host machine)", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => {
      throw new Error("ENOENT");
    });
    vi.spyOn(fs, "readFileSync").mockReturnValue("0::/system.slice/containerd.service\n");
    expect(isContainerEnvironment()).toBe(false);
  });

  it("returns true for cgroup v2 kubepods.slice path", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => {
      throw new Error("ENOENT");
    });
    vi.spyOn(fs, "readFileSync").mockReturnValue(
      "0::/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod123.slice/cri-containerd-abc123.scope\n",
    );
    expect(isContainerEnvironment()).toBe(true);
  });

  it("returns true for cgroup v2 cri-containerd scope path", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => {
      throw new Error("ENOENT");
    });
    vi.spyOn(fs, "readFileSync").mockReturnValue(
      "0::/system.slice/cri-containerd-a1b2c3d4e5f6.scope\n",
    );
    expect(isContainerEnvironment()).toBe(true);
  });

  it("caches the result across calls", () => {
    const fs = require("node:fs");
    const accessSpy = vi.spyOn(fs, "accessSync").mockImplementation(() => undefined);
    expect(isContainerEnvironment()).toBe(true);
    expect(isContainerEnvironment()).toBe(true);
    // accessSync should only be called once due to caching
    expect(accessSpy).toHaveBeenCalledTimes(1);
  });
});

describe("resolveGatewayBindHost", () => {
  afterEach(() => {
    __resetContainerCacheForTest();
    vi.restoreAllMocks();
  });

  it("returns 127.0.0.1 for loopback mode", async () => {
    expect(await resolveGatewayBindHost("loopback")).toBe("127.0.0.1");
  });

  it("returns 0.0.0.0 for lan mode", async () => {
    expect(await resolveGatewayBindHost("lan")).toBe("0.0.0.0");
  });

  it("returns 127.0.0.1 for auto mode on non-container host", async () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => {
      throw new Error("ENOENT");
    });
    vi.spyOn(fs, "readFileSync").mockReturnValue("12:memory:/user.slice\n");
    expect(await resolveGatewayBindHost("auto")).toBe("127.0.0.1");
  });

  it("returns 0.0.0.0 for auto mode inside a container", async () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => undefined);
    expect(await resolveGatewayBindHost("auto")).toBe("0.0.0.0");
  });

  it("defaults to loopback when bind is undefined (non-container)", async () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => {
      throw new Error("ENOENT");
    });
    vi.spyOn(fs, "readFileSync").mockReturnValue("12:memory:/user.slice\n");
    expect(await resolveGatewayBindHost(undefined)).toBe("127.0.0.1");
  });
});

describe("defaultGatewayBindMode", () => {
  afterEach(() => {
    __resetContainerCacheForTest();
    vi.restoreAllMocks();
  });

  it("returns loopback on non-container host", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => {
      throw new Error("ENOENT");
    });
    vi.spyOn(fs, "readFileSync").mockReturnValue("12:memory:/user.slice\n");
    expect(defaultGatewayBindMode()).toBe("loopback");
  });

  it("returns auto inside a container", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => undefined);
    expect(defaultGatewayBindMode()).toBe("auto");
  });

  it("returns loopback inside a container when tailscale serve is active", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => undefined);
    expect(defaultGatewayBindMode("serve")).toBe("loopback");
  });

  it("returns loopback inside a container when tailscale funnel is active", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => undefined);
    expect(defaultGatewayBindMode("funnel")).toBe("loopback");
  });

  it("returns auto inside a container when tailscale is off", () => {
    const fs = require("node:fs");
    vi.spyOn(fs, "accessSync").mockImplementation(() => undefined);
    expect(defaultGatewayBindMode("off")).toBe("auto");
  });
});

describe("isSecureWebSocketUrl", () => {
  it.each([
    // wss:// always accepted
    { input: "wss://127.0.0.1:18789", expected: true },
    { input: "wss://localhost:18789", expected: true },
    { input: "wss://remote.example.com:18789", expected: true },
    { input: "wss://192.168.1.100:18789", expected: true },
    // ws:// loopback accepted
    { input: "ws://127.0.0.1:18789", expected: true },
    { input: "ws://localhost:18789", expected: true },
    { input: "ws://[::1]:18789", expected: true },
    { input: "ws://127.0.0.42:18789", expected: true },
    // ws:// private/public remote addresses rejected by default
    { input: "ws://10.0.0.5:18789", expected: false },
    { input: "ws://10.42.1.100:18789", expected: false },
    { input: "ws://172.16.0.1:18789", expected: false },
    { input: "ws://172.31.255.254:18789", expected: false },
    { input: "ws://192.168.1.100:18789", expected: false },
    { input: "ws://169.254.10.20:18789", expected: false },
    { input: "ws://100.64.0.1:18789", expected: false },
    { input: "ws://[fc00::1]:18789", expected: false },
    { input: "ws://[fd12:3456:789a::1]:18789", expected: false },
    { input: "ws://[fe80::1]:18789", expected: false },
    { input: "ws://[::]:18789", expected: false },
    { input: "ws://[ff02::1]:18789", expected: false },
    // ws:// public addresses rejected
    { input: "ws://remote.example.com:18789", expected: false },
    { input: "ws://1.1.1.1:18789", expected: false },
    { input: "ws://8.8.8.8:18789", expected: false },
    { input: "ws://203.0.113.10:18789", expected: false },
    // invalid URLs
    { input: "not-a-url", expected: false },
    { input: "", expected: false },
    { input: "http://127.0.0.1:18789", expected: true },
    { input: "https://127.0.0.1:18789", expected: true },
    { input: "https://remote.example.com:18789", expected: true },
    { input: "http://remote.example.com:18789", expected: false },
  ] as const)("defaults secure websocket behavior for $input", ({ input, expected }) => {
    expect(isSecureWebSocketUrl(input), input).toBe(expected);
  });

  it("allows private ws:// only when opt-in is enabled", () => {
    const allowedWhenOptedIn = [
      "ws://10.0.0.5:18789",
      "http://10.0.0.5:18789",
      "ws://172.16.0.1:18789",
      "ws://192.168.1.100:18789",
      "ws://100.64.0.1:18789",
      "ws://169.254.10.20:18789",
      "ws://[fc00::1]:18789",
      "ws://[fe80::1]:18789",
      "ws://gateway.private.example:18789",
    ];

    for (const input of allowedWhenOptedIn) {
      expect(isSecureWebSocketUrl(input, { allowPrivateWs: true }), input).toBe(true);
    }
  });

  it("still rejects ws:// public IP literals when opt-in is enabled", () => {
    const publicIpWsUrls = ["ws://1.1.1.1:18789", "ws://8.8.8.8:18789", "ws://203.0.113.10:18789"];

    for (const input of publicIpWsUrls) {
      expect(isSecureWebSocketUrl(input, { allowPrivateWs: true }), input).toBe(false);
    }
  });

  it("still rejects non-unicast IPv6 ws:// even when opt-in is enabled", () => {
    const disallowedWhenOptedIn = [
      "ws://[::]:18789",
      "ws://[0:0::0]:18789",
      "ws://[ff02::1]:18789",
    ];

    for (const input of disallowedWhenOptedIn) {
      expect(isSecureWebSocketUrl(input, { allowPrivateWs: true }), input).toBe(false);
    }
  });
});

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© 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.