Skip to content

Commit db25de1

Browse files
authored
Merge pull request #292
Direct merge requested by repository maintainer
2 parents 0bb467a + 41b4398 commit db25de1

4 files changed

Lines changed: 65 additions & 3 deletions

File tree

services/keeper/src/status-server.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,38 @@ test("GET /healthz returns 503 when drand is down", async () => {
180180
assert.equal(res.status, 503);
181181
const body = res.body as Record<string, unknown>;
182182
assert.equal(body.ok, false);
183+
assert.equal(body.reason, "health check failed");
184+
assert.doesNotMatch(JSON.stringify(body), /drand down/);
185+
},
186+
);
187+
});
188+
189+
test("GET /healthz redacts secret-bearing upstream errors from the response body", async () => {
190+
const secretRpc = "https://rpc.example.internal/secret-token-abc123";
191+
await withServer(
192+
makeSource({
193+
reader: {
194+
getRound: async () => {
195+
throw new Error(`connection refused to ${secretRpc}`);
196+
},
197+
getBidState: async () => ({ revealed_value: null }) as never,
198+
},
199+
drand: {
200+
chain: () => ({
201+
info: async () => {
202+
throw new Error(`connection refused to ${secretRpc}`);
203+
},
204+
}),
205+
} as never,
206+
}),
207+
async (server) => {
208+
const res = await get(server, "/healthz");
209+
assert.equal(res.status, 503);
210+
const body = res.body as Record<string, unknown>;
211+
assert.equal(body.reason, "health check failed");
212+
const serialized = JSON.stringify(body);
213+
assert.doesNotMatch(serialized, /secret-token-abc123/);
214+
assert.doesNotMatch(serialized, /rpc\.example\.internal/);
183215
},
184216
);
185217
});

services/keeper/src/status-server.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,13 @@ function healthzHandler(
140140
},
141141
};
142142
} catch (e) {
143+
const detail = e instanceof Error ? e.message : String(e);
144+
console.error("[keeper-healthz] health check failed:", detail);
143145
return {
144146
status: 503,
145147
body: {
146148
ok: false,
147-
reason: e instanceof Error ? e.message : String(e),
149+
reason: "health check failed",
148150
now: clock.toISOString(),
149151
},
150152
};

services/keeper/src/status.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,31 @@ describe("buildKeeperStatus — upstream failure", () => {
282282
assert.match(String(health.reason ?? ""), /drand/);
283283
});
284284

285+
it("redacts upstream error details from health reason", async () => {
286+
const secretUrl = "https://rpc.internal.example/secret-key-xyz";
287+
const reader = {
288+
getRound: async () => {
289+
throw new Error(`connection refused: ${secretUrl}`);
290+
},
291+
getBidState: async () => ({ revealed_value: null }) as never,
292+
};
293+
const drand = {
294+
chain: () => ({
295+
info: async () => {
296+
throw new Error(`timeout contacting ${secretUrl}`);
297+
},
298+
}),
299+
} as never;
300+
301+
const health = await checkHealth(reader, drand);
302+
assert.equal(health.rpc, "down");
303+
assert.equal(health.drand, "down");
304+
assert.equal(health.reason, "rpc: unavailable; drand: unavailable");
305+
const serialized = JSON.stringify(health);
306+
assert.doesNotMatch(serialized, /secret-key-xyz/);
307+
assert.doesNotMatch(serialized, /rpc\.internal\.example/);
308+
});
309+
285310
it("does not crash when a tracked round is missing on-chain", async () => {
286311
const source = makeSource({
287312
reader: readerNotFound(),

services/keeper/src/status.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,15 +293,18 @@ export async function checkHealth(
293293
// healthy-enough: reachable
294294
} else {
295295
rpc = "down";
296-
reasons.push(`rpc: ${msg}`);
296+
console.error("[keeper-health] rpc probe failed:", msg);
297+
reasons.push("rpc: unavailable");
297298
}
298299
}
299300

300301
try {
301302
await drand.chain().info();
302303
} catch (e) {
303304
drandStatus = "down";
304-
reasons.push(`drand: ${e instanceof Error ? e.message : String(e)}`);
305+
const msg = e instanceof Error ? e.message : String(e);
306+
console.error("[keeper-health] drand probe failed:", msg);
307+
reasons.push("drand: unavailable");
305308
}
306309

307310
const worst = rpc === "down" || drandStatus === "down" ? "down" : "ok";

0 commit comments

Comments
 (0)