Skip to content

Commit 1b00f70

Browse files
committed
fix(doctor): redact gateway health targets
1 parent 777c8bf commit 1b00f70

2 files changed

Lines changed: 33 additions & 3 deletions

File tree

src/flows/doctor-core-checks.runtime.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,31 @@ describe("doctor gateway runtime checks", () => {
579579
});
580580
});
581581

582+
it("redacts sensitive remote gateway URLs from health finding targets", async () => {
583+
mocks.buildGatewayProbeConnectionDetails.mockResolvedValueOnce({
584+
url: "wss://user:[email protected]/rpc?token=secret&safe=value",
585+
});
586+
mocks.probeGatewayStatus.mockResolvedValueOnce({
587+
ok: false,
588+
error: "remote gateway did not answer",
589+
});
590+
591+
const findings = await collectGatewayHealthFindings({
592+
cfg: { gateway: { mode: "remote", remote: { url: "wss://gateway.example.test/rpc" } } },
593+
});
594+
595+
expect(findings).toContainEqual({
596+
checkId: "core/doctor/gateway-health",
597+
severity: "warning",
598+
message: "Gateway is not reachable: remote gateway did not answer",
599+
path: "gateway.remote.url",
600+
target: "wss://***:***@gateway.example.test/rpc?token=***&safe=value",
601+
fixHint: "Verify the remote Gateway URL, network path, TLS settings, and credentials.",
602+
});
603+
expect(JSON.stringify(findings)).not.toContain("user:pass");
604+
expect(JSON.stringify(findings)).not.toContain("token=secret");
605+
});
606+
582607
it("reports missing local gateway daemon service", async () => {
583608
mocks.readGatewayServiceState.mockResolvedValueOnce({
584609
installed: false,

src/flows/doctor-core-checks.runtime.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Doctor runtime checks inspect tool names, browser residue, and runtime state.
2+
import { redactSensitiveUrlLikeString } from "@openclaw/net-policy/redact-sensitive-url";
23
import { TOOL_NAME_SEPARATOR } from "../agents/agent-bundle-mcp-names.js";
34
import {
45
type McpToolCatalogDiagnostic,
@@ -52,6 +53,10 @@ type BundleMcpToolRuntime = Awaited<ReturnType<typeof createBundleMcpToolRuntime
5253
const PROVIDER_CATALOG_ORDERS = ["simple", "profile", "paired", "late"] as const;
5354
const PROVIDER_CATALOG_ORDER_SET = new Set<ProviderCatalogOrder>(PROVIDER_CATALOG_ORDERS);
5455

56+
function formatGatewayHealthTarget(url: string): string {
57+
return redactSensitiveUrlLikeString(url);
58+
}
59+
5560
export function detectUnavailableSkills(cfg: OpenClawConfig): SkillStatusEntry[] {
5661
const agentId = resolveDefaultAgentId(cfg);
5762
const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
@@ -77,7 +82,7 @@ export async function collectGatewayHealthFindings(
7782
checkId: "core/doctor/gateway-health",
7883
severity: "warning",
7984
message: `Gateway health probe could not be prepared: ${formatErrorMessage(error)}`,
80-
path: ctx.cfg.gateway?.mode === "remote" ? "gateway.url" : "gateway",
85+
path: ctx.cfg.gateway?.mode === "remote" ? "gateway.remote.url" : "gateway",
8186
fixHint:
8287
"Fix Gateway connection configuration, then rerun `openclaw doctor --lint --only core/doctor/gateway-health`.",
8388
},
@@ -101,8 +106,8 @@ export async function collectGatewayHealthFindings(
101106
checkId: "core/doctor/gateway-health",
102107
severity: "warning",
103108
message: `Gateway is not reachable: ${probe.error ?? "status probe failed"}`,
104-
path: mode === "remote" ? "gateway.url" : "gateway.mode",
105-
target: probeDetails.url,
109+
path: mode === "remote" ? "gateway.remote.url" : "gateway.mode",
110+
target: formatGatewayHealthTarget(probeDetails.url),
106111
fixHint:
107112
mode === "remote"
108113
? "Verify the remote Gateway URL, network path, TLS settings, and credentials."

0 commit comments

Comments
 (0)