fix(secrets): align SecretRef inspect/strict behavior across preload/runtime paths (#66818)

* Config: add inspect/strict SecretRef string resolver

* CLI: pass resolved/source config snapshots to plugin preload

* Slack: keep HTTP route registration config-only

* Providers: normalize SecretRef handling for auth and web tools

* Secrets: add Exa web search target to registry and docs

* Telegram: resolve env SecretRef tokens at runtime

* Agents: resolve custom provider env SecretRef ids

* Providers: fail closed on blocked SecretRef fallback

* Telegram: enforce env SecretRef policy for runtime token refs

* Status/Providers/Telegram: tighten SecretRef preload and fallback handling

* Providers: enforce env SecretRef policy checks in fallback auth paths

* fix: add SecretRef lifecycle changelog entry (#66818) (thanks @joshavant)
This commit is contained in:
Josh Avant
2026-04-14 17:59:28 -05:00
committed by GitHub
parent 4491bdad76
commit 1769fb2aa1
28 changed files with 1497 additions and 70 deletions

View File

@@ -14,6 +14,7 @@ const REGISTRY_IDS = [
"models.providers.openai.apiKey",
"messages.tts.providers.openai.apiKey",
"plugins.entries.firecrawl.config.webFetch.apiKey",
"plugins.entries.exa.config.webSearch.apiKey",
"skills.entries.demo.apiKey",
"tools.web.search.apiKey",
] as const;
@@ -77,6 +78,7 @@ describe("command secret target ids", () => {
expect(ids.has("agents.defaults.memorySearch.remote.apiKey")).toBe(true);
expect(ids.has("agents.list[].memorySearch.remote.apiKey")).toBe(true);
expect(ids.has("plugins.entries.firecrawl.config.webFetch.apiKey")).toBe(true);
expect(ids.has("plugins.entries.exa.config.webSearch.apiKey")).toBe(true);
expect(ids.has("channels.discord.token")).toBe(false);
});

View File

@@ -31,6 +31,7 @@ const STATIC_AGENT_RUNTIME_BASE_TARGET_IDS = [
"tools.web.search.apiKey",
"plugins.entries.brave.config.webSearch.apiKey",
"plugins.entries.google.config.webSearch.apiKey",
"plugins.entries.exa.config.webSearch.apiKey",
"plugins.entries.xai.config.webSearch.apiKey",
"plugins.entries.moonshot.config.webSearch.apiKey",
"plugins.entries.perplexity.config.webSearch.apiKey",

View File

@@ -60,6 +60,23 @@ describe("plugin-registry-loader", () => {
expect(loggingState.forceConsoleToStderr).toBe(false);
});
it("forwards explicit config snapshots to plugin loading", async () => {
const config = { channels: { telegram: { enabled: true } } } as never;
const activationSourceConfig = { channels: { telegram: { enabled: true } } } as never;
await ensureCliPluginRegistryLoaded({
scope: "configured-channels",
config,
activationSourceConfig,
});
expect(ensurePluginRegistryLoadedMock).toHaveBeenCalledWith({
scope: "configured-channels",
config,
activationSourceConfig,
});
});
it("maps command paths to plugin registry scopes", () => {
expect(resolvePluginRegistryScopeForCommandPath(["status"])).toBe("channels");
expect(resolvePluginRegistryScopeForCommandPath(["health"])).toBe("channels");

View File

@@ -1,3 +1,4 @@
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { loggingState } from "../logging/state.js";
import type { PluginRegistryScope } from "./plugin-registry.js";
@@ -17,6 +18,8 @@ export function resolvePluginRegistryScopeForCommandPath(
export async function ensureCliPluginRegistryLoaded(params: {
scope: PluginRegistryScope;
routeLogsToStderr?: boolean;
config?: OpenClawConfig;
activationSourceConfig?: OpenClawConfig;
}) {
const { ensurePluginRegistryLoaded } = await loadPluginRegistryModule();
const previousForceStderr = loggingState.forceConsoleToStderr;
@@ -24,7 +27,13 @@ export async function ensureCliPluginRegistryLoaded(params: {
loggingState.forceConsoleToStderr = true;
}
try {
ensurePluginRegistryLoaded({ scope: params.scope });
ensurePluginRegistryLoaded({
scope: params.scope,
...(params.config ? { config: params.config } : {}),
...(params.activationSourceConfig
? { activationSourceConfig: params.activationSourceConfig }
: {}),
});
} finally {
loggingState.forceConsoleToStderr = previousForceStderr;
}