From 08b5206b19b7d5517ca2aef81cf45db36a73494d Mon Sep 17 00:00:00 2001 From: Vignesh Natarajan Date: Sun, 29 Mar 2026 00:47:39 -0700 Subject: [PATCH] chore(test): harden channel plugin registry against malformed runtime state --- src/channels/plugins/registry.test.ts | 24 ++++++++++++++++++++++++ src/channels/plugins/registry.ts | 11 ++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/channels/plugins/registry.test.ts diff --git a/src/channels/plugins/registry.test.ts b/src/channels/plugins/registry.test.ts new file mode 100644 index 00000000000..647a8409e8b --- /dev/null +++ b/src/channels/plugins/registry.test.ts @@ -0,0 +1,24 @@ +import { afterEach, describe, expect, it } from "vitest"; +import { createEmptyPluginRegistry } from "../../plugins/registry-empty.js"; +import type { PluginRegistry } from "../../plugins/registry.js"; +import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../../plugins/runtime.js"; +import { listChannelPlugins } from "./registry.js"; + +function withMalformedChannels(registry: PluginRegistry): PluginRegistry { + const malformed = { ...registry } as PluginRegistry; + (malformed as { channels?: unknown }).channels = undefined; + return malformed; +} + +afterEach(() => { + resetPluginRuntimeStateForTest(); +}); + +describe("listChannelPlugins", () => { + it("returns an empty list when runtime registry has no channels field", () => { + const malformedRegistry = withMalformedChannels(createEmptyPluginRegistry()); + setActivePluginRegistry(malformedRegistry); + + expect(listChannelPlugins()).toEqual([]); + }); +}); diff --git a/src/channels/plugins/registry.ts b/src/channels/plugins/registry.ts index b0c3bac9469..1f91236e872 100644 --- a/src/channels/plugins/registry.ts +++ b/src/channels/plugins/registry.ts @@ -41,7 +41,16 @@ function resolveCachedChannelPlugins(): CachedChannelPlugins { return cached; } - const sorted = dedupeChannels(registry.channels.map((entry) => entry.plugin)).toSorted((a, b) => { + const channelPlugins: ChannelPlugin[] = []; + if (Array.isArray(registry.channels)) { + for (const entry of registry.channels) { + if (entry?.plugin) { + channelPlugins.push(entry.plugin); + } + } + } + + const sorted = dedupeChannels(channelPlugins).toSorted((a, b) => { const indexA = CHAT_CHANNEL_ORDER.indexOf(a.id as ChatChannelId); const indexB = CHAT_CHANNEL_ORDER.indexOf(b.id as ChatChannelId); const orderA = a.meta.order ?? (indexA === -1 ? 999 : indexA);