diff --git a/src/gateway/server-methods/skills.ts b/src/gateway/server-methods/skills.ts index 4fd8994098d..f3422bd6ce8 100644 --- a/src/gateway/server-methods/skills.ts +++ b/src/gateway/server-methods/skills.ts @@ -14,6 +14,7 @@ import { buildWorkspaceSkillStatus } from "../../agents/skills-status.js"; import { loadWorkspaceSkillEntries, type SkillEntry } from "../../agents/skills.js"; import { listAgentWorkspaceDirs } from "../../agents/workspace-dirs.js"; import { loadConfig, writeConfigFile } from "../../config/config.js"; +import { redactConfigObject } from "../../config/redact-snapshot.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { fetchClawHubSkillDetail } from "../../infra/clawhub.js"; import { formatErrorMessage } from "../../infra/errors.js"; @@ -341,6 +342,10 @@ export const skillsHandlers: GatewayRequestHandlers = { skills, }; await writeConfigFile(nextConfig); - respond(true, { ok: true, skillKey: p.skillKey, config: current }, undefined); + respond( + true, + { ok: true, skillKey: p.skillKey, config: redactConfigObject(current) }, + undefined, + ); }, }; diff --git a/src/gateway/server-methods/skills.update.normalizes-api-key.test.ts b/src/gateway/server-methods/skills.update.normalizes-api-key.test.ts index 6476ad671d0..fe4af5f507f 100644 --- a/src/gateway/server-methods/skills.update.normalizes-api-key.test.ts +++ b/src/gateway/server-methods/skills.update.normalizes-api-key.test.ts @@ -50,4 +50,50 @@ describe("skills.update", () => { }, }); }); + + it("redacts apiKey and secret env values from the response but writes full values to config", async () => { + writtenConfig = null; + + let responseResult: unknown = null; + await skillsHandlers["skills.update"]({ + params: { + skillKey: "demo-skill", + apiKey: "secret-api-key-123", + env: { + GEMINI_API_KEY: "secret-env-key-456", + BRAVE_REGION: "us", + }, + }, + req: {} as never, + client: null as never, + isWebchatConnect: () => false, + context: {} as never, + respond: (_success, result, _err) => { + responseResult = result; + }, + }); + + // Full values must be persisted to config + expect(writtenConfig).toMatchObject({ + skills: { + entries: { + "demo-skill": { + apiKey: "secret-api-key-123", + env: { + GEMINI_API_KEY: "secret-env-key-456", + BRAVE_REGION: "us", + }, + }, + }, + }, + }); + + // Response must not expose plaintext secrets + const config = (responseResult as { config: Record }).config; + expect(config.apiKey).not.toBe("secret-api-key-123"); + const env = config.env as Record; + expect(env.GEMINI_API_KEY).not.toBe("secret-env-key-456"); + // Non-secret env values should still be present + expect(env.BRAVE_REGION).toBe("us"); + }); });