From 4e7992cc9b48cf9ff8c524bcfa68ded75cc4c6ce Mon Sep 17 00:00:00 2001 From: zw-xysk Date: Mon, 29 Jun 2026 09:40:41 +0800 Subject: [PATCH] fix(feishu): keep top-level appSecret SecretRef active for the implicit default account (#96965) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a Feishu channel has top-level appId/appSecret using SecretRef format and sub-accounts with their own inline appSecret, the secrets resolver marks the top-level SecretRef as inactive because isBaseFieldActiveForChannelSurface only checks whether any explicit account inherits the field. Feishu account listing always creates an implicit default account from top-level credentials. The fix detects this implicit default account and keeps the top-level appSecret active accordingly, without changing the shared channel secret helper semantics for other channels. Fixes #96929 Signed-off-by: 赵旺0668001248 <0668001248@duomai.com> --- extensions/feishu/src/secret-contract.ts | 50 ++++++++++++++++--- .../runtime-external-channel-audit.test.ts | 36 ++++++++++++- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/extensions/feishu/src/secret-contract.ts b/extensions/feishu/src/secret-contract.ts index da55e45291d..0f2b871dd7b 100644 --- a/extensions/feishu/src/secret-contract.ts +++ b/extensions/feishu/src/secret-contract.ts @@ -1,9 +1,11 @@ // Feishu plugin module implements secret contract behavior. import { collectConditionalChannelFieldAssignments, - collectSimpleChannelFieldAssignments, + collectSecretInputAssignment, getChannelSurface, + hasConfiguredSecretInputValue, hasOwnProperty, + isBaseFieldActiveForChannelSurface, normalizeSecretStringValue, type ResolverContext, type SecretDefaults, @@ -89,16 +91,48 @@ export function collectRuntimeConfigAssignments(params: { return; } const { channel: feishu, surface } = resolved; - collectSimpleChannelFieldAssignments({ - channelKey: "feishu", - field: "appSecret", - channel: feishu, - surface, + // Feishu account listing starts an implicit default account from top-level + // appId+appSecret even when every named account overrides appSecret. The + // shared helper's isBaseFieldActiveForChannelSurface only checks whether any + // explicit account inherits the field, so top-level appSecret refs would be + // skipped when all accounts override. Account for the implicit default here. + const hasImplicitDefaultAccount = + surface.channelEnabled && + hasConfiguredSecretInputValue(feishu.appId, params.defaults) && + hasConfiguredSecretInputValue(feishu.appSecret, params.defaults); + const topLevelAppSecretActive = + hasImplicitDefaultAccount || isBaseFieldActiveForChannelSurface(surface, "appSecret"); + collectSecretInputAssignment({ + value: feishu.appSecret, + path: "channels.feishu.appSecret", + expected: "string", defaults: params.defaults, context: params.context, - topInactiveReason: "no enabled account inherits this top-level Feishu appSecret.", - accountInactiveReason: "Feishu account is disabled.", + active: topLevelAppSecretActive, + inactiveReason: "no enabled account inherits this top-level Feishu appSecret.", + apply: (value) => { + feishu.appSecret = value; + }, }); + if (surface.hasExplicitAccounts) { + for (const { accountId, account, enabled } of surface.accounts) { + if (!hasOwnProperty(account, "appSecret")) { + continue; + } + collectSecretInputAssignment({ + value: account.appSecret, + path: `channels.feishu.accounts.${accountId}.appSecret`, + expected: "string", + defaults: params.defaults, + context: params.context, + active: enabled, + inactiveReason: "Feishu account is disabled.", + apply: (value) => { + account.appSecret = value; + }, + }); + } + } const baseConnectionMode = normalizeSecretStringValue(feishu.connectionMode) === "webhook" ? "webhook" : "websocket"; const resolveAccountMode = (account: Record) => diff --git a/src/secrets/runtime-external-channel-audit.test.ts b/src/secrets/runtime-external-channel-audit.test.ts index f8cf3cb5087..8012efe02cd 100644 --- a/src/secrets/runtime-external-channel-audit.test.ts +++ b/src/secrets/runtime-external-channel-audit.test.ts @@ -416,7 +416,9 @@ describe("secrets runtime externalized channel SecretRef audit", () => { enabled: true, tts: { providers: { - openai: { apiKey: inactiveExecRef("DISCORD_DISABLED_VOICE_TTS_API_KEY") }, + openai: { + apiKey: inactiveExecRef("DISCORD_DISABLED_VOICE_TTS_API_KEY"), + }, }, }, }, @@ -541,4 +543,36 @@ describe("secrets runtime externalized channel SecretRef audit", () => { ]); expectMetadataBackedContractsWereUsed(); }); + + it("resolves Feishu top-level appSecret SecretRef for the implicit default account", async () => { + const records = configureExternalChannelRecords(["feishu"]); + const snapshot = await prepareSecretsRuntimeSnapshot({ + config: asConfig({ + channels: { + feishu: { + enabled: true, + appId: "cli_default", + appSecret: ref("FEISHU_APP_SECRET"), + accounts: { + "resource-shrimp": { + enabled: true, + appId: "cli_resource", + appSecret: "inline-secret-here", // pragma: allowlist secret + }, + }, + }, + }, + }), + env: { FEISHU_APP_SECRET: "default-secret" }, + includeAuthStoreRefs: false, + loadablePluginOrigins: externalChannelOrigins(records), + }); + + expectResolvedPaths(snapshot.config, { + "channels.feishu.appSecret": "default-secret", + "channels.feishu.accounts.resource-shrimp.appSecret": "inline-secret-here", + }); + expect(snapshot.warnings).toStrictEqual([]); + expectMetadataBackedContractsWereUsed(["feishu"]); + }); });