From a142590331f5a794a87f75ab6bb91e4d5351d3ed Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 16 May 2026 05:50:23 +0100 Subject: [PATCH] fix: clean detached auth profile secrets --- src/agents/auth-profiles.store.save.test.ts | 42 +++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/agents/auth-profiles.store.save.test.ts b/src/agents/auth-profiles.store.save.test.ts index 2759424e8c8..00057adfbe5 100644 --- a/src/agents/auth-profiles.store.save.test.ts +++ b/src/agents/auth-profiles.store.save.test.ts @@ -1,3 +1,4 @@ +import { createHash } from "node:crypto"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -77,6 +78,17 @@ function readRawPersistedAuthProfiles(agentDir?: string): { }; } +function resolveExpectedOAuthProfileSecretPath( + agentDir: string | undefined, + profileId: string, +): string { + const id = createHash("sha256") + .update(`${authProfileStoreKey(agentDir)}\0${profileId}`) + .digest("hex") + .slice(0, 32); + return path.join(resolveOAuthDir(), "auth-profiles", `${id}.json`); +} + describe("saveAuthProfileStore", () => { let stateRoot = ""; @@ -353,6 +365,36 @@ describe("saveAuthProfileStore", () => { } }); + it("removes detached OAuth secret files when profiles are deleted", async () => { + const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-auth-save-secret-")); + const profileId = "openai-codex:default"; + try { + saveAuthProfileStore( + { + version: 1, + profiles: { + [profileId]: { + type: "oauth", + provider: "openai-codex", + access: "access-token", + refresh: "refresh-token", + expires: Date.now() + 60_000, + }, + }, + }, + agentDir, + ); + const secretPath = resolveExpectedOAuthProfileSecretPath(agentDir, profileId); + await expect(fs.stat(secretPath)).resolves.toBeTruthy(); + + saveAuthProfileStore({ version: 1, profiles: {} }, agentDir); + + await expect(fs.stat(secretPath)).rejects.toMatchObject({ code: "ENOENT" }); + } finally { + await fs.rm(agentDir, { recursive: true, force: true }); + } + }); + it("writes runtime scheduling state to SQLite only", async () => { const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-auth-save-state-")); try {