test: expand gateway auth probe coverage
This commit is contained in:
@@ -91,6 +91,18 @@ describe("callGateway url resolution", () => {
|
|||||||
|
|
||||||
expect(lastClientOptions?.url).toBe("ws://127.0.0.1:18800");
|
expect(lastClientOptions?.url).toBe("ws://127.0.0.1:18800");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses url override in remote mode even when remote url is missing", async () => {
|
||||||
|
loadConfig.mockReturnValue({
|
||||||
|
gateway: { mode: "remote", bind: "loopback", remote: {} },
|
||||||
|
});
|
||||||
|
resolveGatewayPort.mockReturnValue(18789);
|
||||||
|
pickPrimaryTailnetIPv4.mockReturnValue(undefined);
|
||||||
|
|
||||||
|
await callGateway({ method: "health", url: "wss://override.example/ws" });
|
||||||
|
|
||||||
|
expect(lastClientOptions?.url).toBe("wss://override.example/ws");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("buildGatewayConnectionDetails", () => {
|
describe("buildGatewayConnectionDetails", () => {
|
||||||
@@ -313,3 +325,43 @@ describe("callGateway password resolution", () => {
|
|||||||
expect(lastClientOptions?.password).toBe("from-env");
|
expect(lastClientOptions?.password).toBe("from-env");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("callGateway token resolution", () => {
|
||||||
|
const originalEnvToken = process.env.CLAWDBOT_GATEWAY_TOKEN;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
loadConfig.mockReset();
|
||||||
|
resolveGatewayPort.mockReset();
|
||||||
|
pickPrimaryTailnetIPv4.mockReset();
|
||||||
|
lastClientOptions = null;
|
||||||
|
startMode = "hello";
|
||||||
|
closeCode = 1006;
|
||||||
|
closeReason = "";
|
||||||
|
delete process.env.CLAWDBOT_GATEWAY_TOKEN;
|
||||||
|
resolveGatewayPort.mockReturnValue(18789);
|
||||||
|
pickPrimaryTailnetIPv4.mockReturnValue(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (originalEnvToken == null) {
|
||||||
|
delete process.env.CLAWDBOT_GATEWAY_TOKEN;
|
||||||
|
} else {
|
||||||
|
process.env.CLAWDBOT_GATEWAY_TOKEN = originalEnvToken;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses remote token when remote mode uses url override", async () => {
|
||||||
|
process.env.CLAWDBOT_GATEWAY_TOKEN = "env-token";
|
||||||
|
loadConfig.mockReturnValue({
|
||||||
|
gateway: {
|
||||||
|
mode: "remote",
|
||||||
|
remote: { token: "remote-token" },
|
||||||
|
auth: { token: "local-token" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await callGateway({ method: "health", url: "wss://override.example/ws" });
|
||||||
|
|
||||||
|
expect(lastClientOptions?.token).toBe("remote-token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import type { ClawdbotConfig } from "../config/config.js";
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
import type { ChannelPlugin } from "../channels/plugins/types.js";
|
import type { ChannelPlugin } from "../channels/plugins/types.js";
|
||||||
@@ -251,6 +251,29 @@ describe("security audit", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("adds a warning when deep probe throws", async () => {
|
||||||
|
const cfg: ClawdbotConfig = { gateway: { mode: "local" } };
|
||||||
|
|
||||||
|
const res = await runSecurityAudit({
|
||||||
|
config: cfg,
|
||||||
|
deep: true,
|
||||||
|
deepTimeoutMs: 50,
|
||||||
|
includeFilesystem: false,
|
||||||
|
includeChannelSecurity: false,
|
||||||
|
probeGatewayFn: async () => {
|
||||||
|
throw new Error("probe boom");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.deep?.gateway.ok).toBe(false);
|
||||||
|
expect(res.deep?.gateway.error).toContain("probe boom");
|
||||||
|
expect(res.findings).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ checkId: "gateway.probe_failed", severity: "warn" }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("warns on legacy model configuration", async () => {
|
it("warns on legacy model configuration", async () => {
|
||||||
const cfg: ClawdbotConfig = {
|
const cfg: ClawdbotConfig = {
|
||||||
agents: { defaults: { model: { primary: "openai/gpt-3.5-turbo" } } },
|
agents: { defaults: { model: { primary: "openai/gpt-3.5-turbo" } } },
|
||||||
@@ -403,6 +426,27 @@ describe("security audit", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("maybeProbeGateway auth selection", () => {
|
describe("maybeProbeGateway auth selection", () => {
|
||||||
|
const originalEnvToken = process.env.CLAWDBOT_GATEWAY_TOKEN;
|
||||||
|
const originalEnvPassword = process.env.CLAWDBOT_GATEWAY_PASSWORD;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
delete process.env.CLAWDBOT_GATEWAY_TOKEN;
|
||||||
|
delete process.env.CLAWDBOT_GATEWAY_PASSWORD;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (originalEnvToken == null) {
|
||||||
|
delete process.env.CLAWDBOT_GATEWAY_TOKEN;
|
||||||
|
} else {
|
||||||
|
process.env.CLAWDBOT_GATEWAY_TOKEN = originalEnvToken;
|
||||||
|
}
|
||||||
|
if (originalEnvPassword == null) {
|
||||||
|
delete process.env.CLAWDBOT_GATEWAY_PASSWORD;
|
||||||
|
} else {
|
||||||
|
process.env.CLAWDBOT_GATEWAY_PASSWORD = originalEnvPassword;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it("uses local auth when gateway.mode is local", async () => {
|
it("uses local auth when gateway.mode is local", async () => {
|
||||||
let capturedAuth: { token?: string; password?: string } | undefined;
|
let capturedAuth: { token?: string; password?: string } | undefined;
|
||||||
const cfg: ClawdbotConfig = {
|
const cfg: ClawdbotConfig = {
|
||||||
@@ -437,6 +481,41 @@ describe("security audit", () => {
|
|||||||
expect(capturedAuth?.token).toBe("local-token-abc123");
|
expect(capturedAuth?.token).toBe("local-token-abc123");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("prefers env token over local config token", async () => {
|
||||||
|
process.env.CLAWDBOT_GATEWAY_TOKEN = "env-token";
|
||||||
|
let capturedAuth: { token?: string; password?: string } | undefined;
|
||||||
|
const cfg: ClawdbotConfig = {
|
||||||
|
gateway: {
|
||||||
|
mode: "local",
|
||||||
|
auth: { token: "local-token" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await runSecurityAudit({
|
||||||
|
config: cfg,
|
||||||
|
deep: true,
|
||||||
|
deepTimeoutMs: 50,
|
||||||
|
includeFilesystem: false,
|
||||||
|
includeChannelSecurity: false,
|
||||||
|
probeGatewayFn: async (opts) => {
|
||||||
|
capturedAuth = opts.auth;
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
url: opts.url,
|
||||||
|
connectLatencyMs: 10,
|
||||||
|
error: null,
|
||||||
|
close: null,
|
||||||
|
health: null,
|
||||||
|
status: null,
|
||||||
|
presence: null,
|
||||||
|
configSnapshot: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(capturedAuth?.token).toBe("env-token");
|
||||||
|
});
|
||||||
|
|
||||||
it("uses local auth when gateway.mode is undefined (default)", async () => {
|
it("uses local auth when gateway.mode is undefined (default)", async () => {
|
||||||
let capturedAuth: { token?: string; password?: string } | undefined;
|
let capturedAuth: { token?: string; password?: string } | undefined;
|
||||||
const cfg: ClawdbotConfig = {
|
const cfg: ClawdbotConfig = {
|
||||||
@@ -508,6 +587,120 @@ describe("security audit", () => {
|
|||||||
expect(capturedAuth?.token).toBe("remote-token-xyz789");
|
expect(capturedAuth?.token).toBe("remote-token-xyz789");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("ignores env token when gateway.mode is remote", async () => {
|
||||||
|
process.env.CLAWDBOT_GATEWAY_TOKEN = "env-token";
|
||||||
|
let capturedAuth: { token?: string; password?: string } | undefined;
|
||||||
|
const cfg: ClawdbotConfig = {
|
||||||
|
gateway: {
|
||||||
|
mode: "remote",
|
||||||
|
auth: { token: "local-token-should-not-use" },
|
||||||
|
remote: {
|
||||||
|
url: "ws://remote.example.com:18789",
|
||||||
|
token: "remote-token",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await runSecurityAudit({
|
||||||
|
config: cfg,
|
||||||
|
deep: true,
|
||||||
|
deepTimeoutMs: 50,
|
||||||
|
includeFilesystem: false,
|
||||||
|
includeChannelSecurity: false,
|
||||||
|
probeGatewayFn: async (opts) => {
|
||||||
|
capturedAuth = opts.auth;
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
url: opts.url,
|
||||||
|
connectLatencyMs: 10,
|
||||||
|
error: null,
|
||||||
|
close: null,
|
||||||
|
health: null,
|
||||||
|
status: null,
|
||||||
|
presence: null,
|
||||||
|
configSnapshot: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(capturedAuth?.token).toBe("remote-token");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses remote password when env is unset", async () => {
|
||||||
|
let capturedAuth: { token?: string; password?: string } | undefined;
|
||||||
|
const cfg: ClawdbotConfig = {
|
||||||
|
gateway: {
|
||||||
|
mode: "remote",
|
||||||
|
remote: {
|
||||||
|
url: "ws://remote.example.com:18789",
|
||||||
|
password: "remote-pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await runSecurityAudit({
|
||||||
|
config: cfg,
|
||||||
|
deep: true,
|
||||||
|
deepTimeoutMs: 50,
|
||||||
|
includeFilesystem: false,
|
||||||
|
includeChannelSecurity: false,
|
||||||
|
probeGatewayFn: async (opts) => {
|
||||||
|
capturedAuth = opts.auth;
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
url: opts.url,
|
||||||
|
connectLatencyMs: 10,
|
||||||
|
error: null,
|
||||||
|
close: null,
|
||||||
|
health: null,
|
||||||
|
status: null,
|
||||||
|
presence: null,
|
||||||
|
configSnapshot: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(capturedAuth?.password).toBe("remote-pass");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prefers env password over remote password", async () => {
|
||||||
|
process.env.CLAWDBOT_GATEWAY_PASSWORD = "env-pass";
|
||||||
|
let capturedAuth: { token?: string; password?: string } | undefined;
|
||||||
|
const cfg: ClawdbotConfig = {
|
||||||
|
gateway: {
|
||||||
|
mode: "remote",
|
||||||
|
remote: {
|
||||||
|
url: "ws://remote.example.com:18789",
|
||||||
|
password: "remote-pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await runSecurityAudit({
|
||||||
|
config: cfg,
|
||||||
|
deep: true,
|
||||||
|
deepTimeoutMs: 50,
|
||||||
|
includeFilesystem: false,
|
||||||
|
includeChannelSecurity: false,
|
||||||
|
probeGatewayFn: async (opts) => {
|
||||||
|
capturedAuth = opts.auth;
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
url: opts.url,
|
||||||
|
connectLatencyMs: 10,
|
||||||
|
error: null,
|
||||||
|
close: null,
|
||||||
|
health: null,
|
||||||
|
status: null,
|
||||||
|
presence: null,
|
||||||
|
configSnapshot: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(capturedAuth?.password).toBe("env-pass");
|
||||||
|
});
|
||||||
|
|
||||||
it("falls back to local auth when gateway.mode is remote but URL is missing", async () => {
|
it("falls back to local auth when gateway.mode is remote but URL is missing", async () => {
|
||||||
let capturedAuth: { token?: string; password?: string } | undefined;
|
let capturedAuth: { token?: string; password?: string } | undefined;
|
||||||
const cfg: ClawdbotConfig = {
|
const cfg: ClawdbotConfig = {
|
||||||
|
|||||||
Reference in New Issue
Block a user