fix(gateway): preserve runtime-backed health state#72417
Conversation
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟠 Sensitive data exposure in non-admin health snapshots via unsanitized plugin health summaries
Description
Vulnerable code: const record = summary && typeof summary === "object"
? ({ ...snapshot, ...summary } as ChannelAccountHealthSummary)
: ({ ...snapshot, accountId, configured } satisfies ChannelAccountHealthSummary);
...
if (!includeSensitive) {
delete record.probe;
}Because gateway health responses set RecommendationWhen Options:
const safeSummary = includeSensitive
? summary
: projectSafeChannelAccountSnapshotFields(summary as Record<string, unknown>);
const record = summary && typeof summary === "object"
? ({ ...snapshot, ...safeSummary } as ChannelAccountHealthSummary)
: ({ ...snapshot, accountId, configured } satisfies ChannelAccountHealthSummary);
if (!includeSensitive) {
// also defensively drop any known-sensitive keys that might still exist
delete record.probe;
delete (record as any).token;
delete (record as any).secret;
delete (record as any).webhookUrl;
}
Additionally, consider preventing plugin summaries from overwriting core snapshot fields (e.g., merge as Analyzed PR: #72417 at commit Last updated on: 2026-04-27T17:52:03Z |
Greptile SummaryThis PR fixes gateway health snapshots to preserve live runtime-backed channel/account state (
Confidence Score: 3/5Not safe to merge as-is: the else branch in health.ts drops runtime fields for any plugin whose summary is falsy, defeating part of the fix's stated goal. One P1 finding: the no-summary fallback branch in getHealthSnapshot reconstructs the record without ...snapshot, silently discarding runtime fields from the runtimeSnapshot for plugins that either omit buildChannelSummary or return a non-object from it. The rest of the change — DI wiring, coalescing, error guard, and new tests — is well-structured. src/commands/health.ts — the else branch in the record assignment around line 391
|
2397a25 to
f9f55bb
Compare
|
ProjectClownfish follow-up pushed: f9f55bb01e What changed:
Blacksmith validation:
|
* fix(gateway): preserve runtime-backed health state * fix(clownfish): address review for ghcrawl-207035-agentic-merge (1) * fix(gateway): harden health snapshot exposure
* fix(gateway): preserve runtime-backed health state * fix(clownfish): address review for ghcrawl-207035-agentic-merge (1) * fix(gateway): harden health snapshot exposure
* fix(gateway): preserve runtime-backed health state * fix(clownfish): address review for ghcrawl-207035-agentic-merge (1) * fix(gateway): harden health snapshot exposure
* fix(gateway): preserve runtime-backed health state * fix(clownfish): address review for ghcrawl-207035-agentic-merge (1) * fix(gateway): harden health snapshot exposure
* fix(gateway): preserve runtime-backed health state * fix(clownfish): address review for ghcrawl-207035-agentic-merge (1) * fix(gateway): harden health snapshot exposure
* fix(gateway): preserve runtime-backed health state * fix(clownfish): address review for ghcrawl-207035-agentic-merge (1) * fix(gateway): harden health snapshot exposure
Summary
Credit
This repair is based on #39921 by FAL1989, with useful ideas and tests carried forward from #42586 by rstar327, #46527 by 0xble, and #52770/#42543 by ajayr.
Validation
Do not close duplicates or superseded PRs until this repair is pushed, /review is clean, and validation passes.
ProjectClownfish replacement details:
╭─[�[38;2;92;157;255;1msrc/gateway/server/health-state.test.ts�[0m:88:12]
�[2m87�[0m │ expect(getHealthSnapshotMock).toHaveBeenCalledTimes(2);
�[2m88�[0m │ expect(getHealthSnapshotMock.mock.calls.map((call) => call[0]?.probe).toSorted()).toEqual([
· �[38;2;246;87;248m ─────────────────────────────────────────────────────────────────────────�[0m
�[2m89�[0m │ false,
╰────
Found 0 warnings and 1 error.
Finished in 1.2s on 6 files with 136 rules using 4 threads.