test(security): add trusted-proxy audit tests

- Test trusted-proxy mode flagged as critical
- Test missing trustedProxies finding
- Test missing userHeader finding
- Test empty allowUsers warning
- Fix env isolation for bind_no_auth test

Part of #1560

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Nick Taylor
2026-01-25 05:57:43 +00:00
committed by Peter Steinberger
parent b4f6e26ae9
commit 097a2f5bd1

View File

@@ -95,23 +95,36 @@ describe("security audit", () => {
});
it("flags non-loopback bind without auth as critical", async () => {
const cfg: OpenClawConfig = {
gateway: {
bind: "lan",
auth: {},
},
};
// Clear env tokens so resolveGatewayAuth defaults to mode=none
const prevToken = process.env.CLAWDBOT_GATEWAY_TOKEN;
const prevPassword = process.env.CLAWDBOT_GATEWAY_PASSWORD;
delete process.env.CLAWDBOT_GATEWAY_TOKEN;
delete process.env.CLAWDBOT_GATEWAY_PASSWORD;
const res = await runSecurityAudit({
config: cfg,
env: {},
includeFilesystem: false,
includeChannelSecurity: false,
});
try {
const cfg: ClawdbotConfig = {
gateway: {
bind: "lan",
auth: {},
},
};
expect(
res.findings.some((f) => f.checkId === "gateway.bind_no_auth" && f.severity === "critical"),
).toBe(true);
const res = await runSecurityAudit({
config: cfg,
includeFilesystem: false,
includeChannelSecurity: false,
});
expect(
res.findings.some((f) => f.checkId === "gateway.bind_no_auth" && f.severity === "critical"),
).toBe(true);
} finally {
// Restore env
if (prevToken === undefined) delete process.env.CLAWDBOT_GATEWAY_TOKEN;
else process.env.CLAWDBOT_GATEWAY_TOKEN = prevToken;
if (prevPassword === undefined) delete process.env.CLAWDBOT_GATEWAY_PASSWORD;
else process.env.CLAWDBOT_GATEWAY_PASSWORD = prevPassword;
}
});
it("warns when non-loopback bind has auth but no auth rate limit", async () => {
@@ -593,6 +606,125 @@ describe("security audit", () => {
);
});
it("flags trusted-proxy auth mode as critical warning", async () => {
const cfg: ClawdbotConfig = {
gateway: {
bind: "lan",
trustedProxies: ["10.0.0.1"],
auth: {
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-forwarded-user",
},
},
},
};
const res = await runSecurityAudit({
config: cfg,
includeFilesystem: false,
includeChannelSecurity: false,
});
expect(res.findings).toEqual(
expect.arrayContaining([
expect.objectContaining({
checkId: "gateway.trusted_proxy_auth",
severity: "critical",
}),
]),
);
});
it("flags trusted-proxy auth without trustedProxies configured", async () => {
const cfg: ClawdbotConfig = {
gateway: {
bind: "lan",
trustedProxies: [],
auth: {
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-forwarded-user",
},
},
},
};
const res = await runSecurityAudit({
config: cfg,
includeFilesystem: false,
includeChannelSecurity: false,
});
expect(res.findings).toEqual(
expect.arrayContaining([
expect.objectContaining({
checkId: "gateway.trusted_proxy_no_proxies",
severity: "critical",
}),
]),
);
});
it("flags trusted-proxy auth without userHeader configured", async () => {
const cfg: ClawdbotConfig = {
gateway: {
bind: "lan",
trustedProxies: ["10.0.0.1"],
auth: {
mode: "trusted-proxy",
trustedProxy: {} as never,
},
},
};
const res = await runSecurityAudit({
config: cfg,
includeFilesystem: false,
includeChannelSecurity: false,
});
expect(res.findings).toEqual(
expect.arrayContaining([
expect.objectContaining({
checkId: "gateway.trusted_proxy_no_user_header",
severity: "critical",
}),
]),
);
});
it("warns when trusted-proxy auth allows all users", async () => {
const cfg: ClawdbotConfig = {
gateway: {
bind: "lan",
trustedProxies: ["10.0.0.1"],
auth: {
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-forwarded-user",
allowUsers: [],
},
},
},
};
const res = await runSecurityAudit({
config: cfg,
includeFilesystem: false,
includeChannelSecurity: false,
});
expect(res.findings).toEqual(
expect.arrayContaining([
expect.objectContaining({
checkId: "gateway.trusted_proxy_no_allowlist",
severity: "warn",
}),
]),
);
});
it("warns when multiple DM senders share the main session", async () => {
const cfg: OpenClawConfig = { session: { dmScope: "main" } };
const plugins: ChannelPlugin[] = [