diff --git a/CHANGELOG.md b/CHANGELOG.md index 9700bd44114..c59f714f14a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -167,6 +167,7 @@ Docs: https://docs.openclaw.ai - OpenAI-compatible providers: honor `compat.supportsTools=false` by stripping tool payload fields before dispatch to chat-only endpoints. Fixes #74664. - OpenAI-compatible providers: apply model-declared unsupported tool-schema keyword stripping to native OpenAI transport payloads and mark Fireworks Kimi K2.5 as rejecting `not` schemas. Fixes #75467. - OpenAI-compatible gateway: sanitize images supplied through request content even when the prompt text contains no image file references, preventing oversized attachment payloads from bypassing the resize/drop pipeline. Fixes #59913. +- Auth profiles: normalize inline API keys and tokens loaded from `auth-profiles.json` so masked or rich-text credential artifacts fail as auth errors instead of crashing HTTP header construction. Fixes #77624. - llm-task: resolve configured model aliases before embedded dispatch so `model="gemini-flash"` and other aliases route to the intended provider instead of the agent default. Fixes #54166. - Media generation: resolve slash-containing model-only overrides like `fal-ai/flux/dev` through registered provider model metadata so FAL image/video models do not get misparsed as provider `fal-ai`. Fixes #77444. - Commands/BTW: show the `/btw` missing-question usage placeholder with brackets so outbound channel sanitization keeps it visible. Fixes #62877. Thanks @RajvardhanPatil07. diff --git a/src/agents/auth-profiles/oauth.test.ts b/src/agents/auth-profiles/oauth.test.ts index bcbd977518d..ec88712eb37 100644 --- a/src/agents/auth-profiles/oauth.test.ts +++ b/src/agents/auth-profiles/oauth.test.ts @@ -357,6 +357,30 @@ describe("resolveApiKeyForProfile secret refs", () => { } }); + it("normalizes inline api_key values from auth profiles before header use", async () => { + const profileId = "openrouter:masked"; + const result = await resolveApiKeyForProfile({ + cfg: cfgFor(profileId, "openrouter", "api_key"), + store: { + version: 1, + profiles: { + [profileId]: { + type: "api_key", + provider: "openrouter", + key: " sk-or-\u202650ec ", + }, + }, + }, + profileId, + }); + + expect(result).toEqual({ + apiKey: "sk-or-50ec", // pragma: allowlist secret + provider: "openrouter", + email: undefined, + }); + }); + it("resolves token tokenRef from env", async () => { const profileId = "github-copilot:default"; await withEnvVar("GITHUB_TOKEN", "gh-ref-token", async () => { diff --git a/src/agents/auth-profiles/oauth.ts b/src/agents/auth-profiles/oauth.ts index 9ce4157542d..6703114ee4d 100644 --- a/src/agents/auth-profiles/oauth.ts +++ b/src/agents/auth-profiles/oauth.ts @@ -14,6 +14,7 @@ import { } from "../../plugins/provider-runtime.runtime.js"; import { resolveSecretRefString, type SecretRefResolveCache } from "../../secrets/resolve.js"; import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js"; +import { normalizeOptionalSecretInput } from "../../utils/normalize-secret-input.js"; import { refreshChutesTokens } from "../chutes-oauth.js"; import { log } from "./constants.js"; import { resolveTokenExpiryState } from "./credential-state.js"; @@ -260,7 +261,7 @@ async function resolveProfileSecretString(params: { } } - return resolvedValue; + return normalizeOptionalSecretInput(resolvedValue); } export async function resolveApiKeyForProfile(