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

Quelle  doctor-security.test.ts

  Sprache: JAVA
 

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

import fs from "node:fs/promises";
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";

const note = vi.hoisted(() => vi.fn());
const pluginRegistry = vi.hoisted(() => ({ list: [] as unknown[] }));

vi.mock("../terminal/note.js", () => ({
  note,
}));

vi.mock("../channels/plugins/index.js", () => ({
  listChannelPlugins: () => pluginRegistry.list,
}));

vi.mock("../channels/read-only-account-inspect.js", () => ({
  inspectReadOnlyChannelAccount: vi.fn(async () => null),
}));

import { noteSecurityWarnings } from "./doctor-security.js";

describe("noteSecurityWarnings gateway exposure", () => {
  let prevToken: string | undefined;
  let prevPassword: string | undefined;
  let prevHome: string | undefined;

  beforeEach(() => {
    note.mockClear();
    pluginRegistry.list = [];
    prevToken = process.env.OPENCLAW_GATEWAY_TOKEN;
    prevPassword = process.env.OPENCLAW_GATEWAY_PASSWORD;
    prevHome = process.env.HOME;
    delete process.env.OPENCLAW_GATEWAY_TOKEN;
    delete process.env.OPENCLAW_GATEWAY_PASSWORD;
  });

  afterEach(() => {
    if (prevToken === undefined) {
      delete process.env.OPENCLAW_GATEWAY_TOKEN;
    } else {
      process.env.OPENCLAW_GATEWAY_TOKEN = prevToken;
    }
    if (prevPassword === undefined) {
      delete process.env.OPENCLAW_GATEWAY_PASSWORD;
    } else {
      process.env.OPENCLAW_GATEWAY_PASSWORD = prevPassword;
    }
    if (prevHome === undefined) {
      delete process.env.HOME;
    } else {
      process.env.HOME = prevHome;
    }
  });

  const lastMessage = () => String(note.mock.calls.at(-1)?.[0] ?? "");

  async function withExecApprovalsFile(
    file: Record<string, unknown>,
    run: () => Promise<void>,
  ): Promise<void> {
    const home = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-doctor-security-"));
    process.env.HOME = home;
    await fs.mkdir(path.join(home, ".openclaw"), { recursive: true });
    await fs.writeFile(
      path.join(home, ".openclaw", "exec-approvals.json"),
      JSON.stringify(file, null, 2),
    );
    await run();
  }

  async function expectAgentExecHostPolicyWarning(agentKey: "*" | "runner") {
    await withExecApprovalsFile(
      {
        version: 1,
        defaults:
          agentKey === "*"
            ? {
                security: "full",
                ask: "off",
              }
            : undefined,
        agents: {
          [agentKey]: {
            security: "allowlist",
            ask: "always",
          },
        },
      },
      async () => {
        await noteSecurityWarnings({
          agents: {
            list: [
              {
                id: "runner",
                tools: {
                  exec: {
                    security: "full",
                    ask: "off",
                  },
                },
              },
            ],
          },
        } as OpenClawConfig);
      },
    );

    const message = lastMessage();
    expect(message).toContain("agents.list.runner.tools.exec is broader than the host exec policy");
    expect(message).toContain(`agents.${agentKey}.security="allowlist"`);
    expect(message).toContain(`agents.${agentKey}.ask="always"`);
  }

  it("warns when exposed without auth", async () => {
    const cfg = { gateway: { bind: "lan" } } as OpenClawConfig;
    await noteSecurityWarnings(cfg);
    const message = lastMessage();
    expect(message).toContain("CRITICAL");
    expect(message).toContain("without authentication");
    expect(message).toContain("Safer remote access");
    expect(message).toContain("ssh -N -L 18789:127.0.0.1:18789");
  });

  it("uses env token to avoid critical warning", async () => {
    process.env.OPENCLAW_GATEWAY_TOKEN = "token-123";
    const cfg = { gateway: { bind: "lan" } } as OpenClawConfig;
    await noteSecurityWarnings(cfg);
    const message = lastMessage();
    expect(message).toContain("WARNING");
    expect(message).not.toContain("CRITICAL");
  });

  it("treats SecretRef token config as authenticated for exposure warning level", async () => {
    const cfg = {
      gateway: {
        bind: "lan",
        auth: {
          mode: "token",
          token: { source: "env", provider: "default", id: "OPENCLAW_GATEWAY_TOKEN" },
        },
      },
    } as OpenClawConfig;
    await noteSecurityWarnings(cfg);
    const message = lastMessage();
    expect(message).toContain("WARNING");
    expect(message).not.toContain("CRITICAL");
  });

  it("treats whitespace token as missing", async () => {
    const cfg = {
      gateway: { bind: "lan", auth: { mode: "token", token: "   " } },
    } as OpenClawConfig;
    await noteSecurityWarnings(cfg);
    const message = lastMessage();
    expect(message).toContain("CRITICAL");
  });

  it("skips warning for loopback bind", async () => {
    const cfg = { gateway: { bind: "loopback" } } as OpenClawConfig;
    await noteSecurityWarnings(cfg);
    const message = lastMessage();
    expect(message).toContain("No channel security warnings detected");
    expect(message).not.toContain("Gateway bound");
  });

  it("treats unset bind as loopback for host-side doctor checks", async () => {
    const cfg = { gateway: {} } as OpenClawConfig;
    await noteSecurityWarnings(cfg);
    const message = lastMessage();
    expect(message).toContain("No channel security warnings detected");
    expect(message).not.toContain("Gateway bound");
  });

  it("shows explicit dmScope config command for multi-user DMs", async () => {
    pluginRegistry.list = [
      {
        id: "test-channel",
        meta: { label: "Test Channel" },
        config: {
          listAccountIds: () => ["default"],
          inspectAccount: () => ({ enabled: true, configured: true }),
          resolveAccount: () => ({}),
          isEnabled: () => true,
          isConfigured: () => true,
        },
        security: {
          resolveDmPolicy: () => ({
            policy: "allowlist",
            allowFrom: ["alice", "bob"],
            allowFromPath: "channels.whatsapp.",
            approveHint: "approve",
          }),
        },
      },
    ];
    const cfg = { session: { dmScope: "main" } } as OpenClawConfig;
    await noteSecurityWarnings(cfg);
    const message = lastMessage();
    expect(message).toContain('config set session.dmScope "per-channel-peer"');
  });

  it("clarifies approvals.exec forwarding-only behavior", async () => {
    const cfg = {
      approvals: {
        exec: {
          enabled: false,
        },
      },
    } as OpenClawConfig;
    await noteSecurityWarnings(cfg);
    const message = lastMessage();
    expect(message).toContain("disables approval forwarding only");
    expect(message).toContain("exec-approvals.json");
    expect(message).toContain("openclaw approvals get --gateway");
  });

  it("warns when tools.exec is broader than host exec defaults", async () => {
    await withExecApprovalsFile(
      {
        version: 1,
        defaults: {
          security: "allowlist",
          ask: "on-miss",
        },
      },
      async () => {
        await noteSecurityWarnings({
          tools: {
            exec: {
              security: "full",
              ask: "off",
            },
          },
        } as OpenClawConfig);
      },
    );

    const message = lastMessage();
    expect(message).toContain("tools.exec is broader than the host exec policy");
    expect(message).toContain('security="full"');
    expect(message).toContain('defaults.security="allowlist"');
    expect(message).toContain("stricter side wins");
  });

  it("attributes broader host policy warnings to wildcard agent entries", async () => {
    await expectAgentExecHostPolicyWarning("*");
  });

  it("does not invent a deny host policy when exec-approvals defaults.security is unset", async () => {
    await withExecApprovalsFile(
      {
        version: 1,
        agents: {},
      },
      async () => {
        await noteSecurityWarnings({
          tools: {
            exec: {
              security: "allowlist",
              ask: "on-miss",
            },
          },
        } as OpenClawConfig);
      },
    );

    const message = lastMessage();
    expect(message).toContain("No channel security warnings detected");
    expect(message).not.toContain('security="deny"');
  });

  it("does not invent an on-miss host ask policy when exec-approvals defaults.ask is unset", async () => {
    await withExecApprovalsFile(
      {
        version: 1,
        agents: {},
      },
      async () => {
        await noteSecurityWarnings({
          tools: {
            exec: {
              ask: "always",
            },
          },
        } as OpenClawConfig);
      },
    );

    const message = lastMessage();
    expect(message).toContain("No channel security warnings detected");
    expect(message).not.toContain('ask="on-miss"');
  });

  it("warns when a per-agent exec policy is broader than the matching host agent policy", async () => {
    await expectAgentExecHostPolicyWarning("runner");
  });

  it("warns when an agent inherits broader global tools.exec policy than the matching host agent policy", async () => {
    await withExecApprovalsFile(
      {
        version: 1,
        agents: {
          runner: {
            security: "allowlist",
            ask: "always",
          },
        },
      },
      async () => {
        await noteSecurityWarnings({
          tools: {
            exec: {
              security: "full",
              ask: "off",
            },
          },
          agents: {
            list: [{ id: "runner" }],
          },
        } as OpenClawConfig);
      },
    );

    const message = lastMessage();
    expect(message).toContain("agents.list.runner.tools.exec is broader than the host exec policy");
    expect(message).toContain('tools.exec.security="full"');
    expect(message).toContain('tools.exec.ask="off"');
    expect(message).toContain('agents.runner.security="allowlist"');
    expect(message).toContain('agents.runner.ask="always"');
  });

  it("ignores malformed host policy fields when attributing doctor conflicts", async () => {
    await withExecApprovalsFile(
      {
        version: 1,
        defaults: {
          ask: "always",
        },
        agents: {
          runner: {
            ask: "foo",
          },
        },
      },
      async () => {
        await noteSecurityWarnings({
          tools: {
            exec: {
              ask: "off",
            },
          },
          agents: {
            list: [{ id: "runner" }],
          },
        } as OpenClawConfig);
      },
    );

    const message = lastMessage();
    expect(message).toContain("agents.list.runner.tools.exec is broader than the host exec policy");
    expect(message).toContain('defaults.ask="always"');
    expect(message).not.toContain('agents.runner.ask="foo"');
  });

  it('does not warn about durable allow-always trust when ask="always" is enforced', async () => {
    await withExecApprovalsFile(
      {
        version: 1,
        defaults: {
          ask: "always",
        },
        agents: {
          main: {
            allowlist: [
              {
                pattern: "/usr/bin/echo",
                source: "allow-always",
              },
            ],
          },
        },
      },
      async () => {
        await noteSecurityWarnings({
          tools: {
            exec: {
              ask: "always",
            },
          },
        } as OpenClawConfig);
      },
    );

    const message = lastMessage();
    expect(message).not.toContain('tools.exec: ask="always" still bypasses future prompts');
  });

  it("warns when heartbeat delivery relies on implicit directPolicy defaults", async () => {
    const cfg = {
      agents: {
        defaults: {
          heartbeat: {
            target: "last",
          },
        },
      },
    } as OpenClawConfig;
    await noteSecurityWarnings(cfg);
    const message = lastMessage();
    expect(message).toContain("Heartbeat defaults");
    expect(message).toContain("agents.defaults.heartbeat.directPolicy");
    expect(message).toContain("direct/DM targets by default");
  });

  it("warns when a per-agent heartbeat relies on implicit directPolicy", async () => {
    const cfg = {
      agents: {
        list: [
          {
            id: "ops",
            heartbeat: {
              target: "last",
            },
          },
        ],
      },
    } as OpenClawConfig;
    await noteSecurityWarnings(cfg);
    const message = lastMessage();
    expect(message).toContain('Heartbeat agent "ops"');
    expect(message).toContain('heartbeat.directPolicy for agent "ops"');
    expect(message).toContain("direct/DM targets by default");
  });

  it("degrades safely when channel account resolution fails in read-only security checks", async () => {
    pluginRegistry.list = [
      {
        id: "whatsapp",
        meta: { label: "WhatsApp" },
        config: {
          listAccountIds: () => ["default"],
          resolveAccount: () => {
            throw new Error("missing secret");
          },
          isEnabled: () => true,
          isConfigured: () => true,
        },
        security: {
          resolveDmPolicy: () => null,
        },
      },
    ];

    await noteSecurityWarnings({} as OpenClawConfig);
    const message = lastMessage();
    expect(message).toContain("[secrets]");
    expect(message).toContain("failed to resolve account");
    expect(message).toContain("Run: openclaw security audit --deep");
  });

  it("skips heartbeat directPolicy warning when delivery is internal-only or explicit", async () => {
    const cfg = {
      agents: {
        defaults: {
          heartbeat: {
            target: "none",
          },
        },
        list: [
          {
            id: "ops",
            heartbeat: {
              target: "last",
              directPolicy: "block",
            },
          },
        ],
      },
    } as OpenClawConfig;
    await noteSecurityWarnings(cfg);
    const message = lastMessage();
    expect(message).not.toContain("Heartbeat defaults");
    expect(message).not.toContain('Heartbeat agent "ops"');
  });
});

¤ Dauer der Verarbeitung: 0.14 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.