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 () => { it("flags non-loopback bind without auth as critical", async () => {
const cfg: OpenClawConfig = { // Clear env tokens so resolveGatewayAuth defaults to mode=none
gateway: { const prevToken = process.env.CLAWDBOT_GATEWAY_TOKEN;
bind: "lan", const prevPassword = process.env.CLAWDBOT_GATEWAY_PASSWORD;
auth: {}, delete process.env.CLAWDBOT_GATEWAY_TOKEN;
}, delete process.env.CLAWDBOT_GATEWAY_PASSWORD;
};
const res = await runSecurityAudit({ try {
config: cfg, const cfg: ClawdbotConfig = {
env: {}, gateway: {
includeFilesystem: false, bind: "lan",
includeChannelSecurity: false, auth: {},
}); },
};
expect( const res = await runSecurityAudit({
res.findings.some((f) => f.checkId === "gateway.bind_no_auth" && f.severity === "critical"), config: cfg,
).toBe(true); 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 () => { 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 () => { it("warns when multiple DM senders share the main session", async () => {
const cfg: OpenClawConfig = { session: { dmScope: "main" } }; const cfg: OpenClawConfig = { session: { dmScope: "main" } };
const plugins: ChannelPlugin[] = [ const plugins: ChannelPlugin[] = [