mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-03 06:30:21 +00:00
fix(auth): strip line breaks from pasted keys
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { AuthProfileCredential, AuthProfileStore } from "./types.js";
|
||||
import { normalizeSecretInput } from "../../utils/normalize-secret-input.js";
|
||||
import { normalizeProviderId } from "../model-selection.js";
|
||||
import {
|
||||
ensureAuthProfileStore,
|
||||
@@ -49,8 +50,19 @@ export function upsertAuthProfile(params: {
|
||||
credential: AuthProfileCredential;
|
||||
agentDir?: string;
|
||||
}): void {
|
||||
const credential =
|
||||
params.credential.type === "api_key"
|
||||
? {
|
||||
...params.credential,
|
||||
...(typeof params.credential.key === "string"
|
||||
? { key: normalizeSecretInput(params.credential.key) }
|
||||
: {}),
|
||||
}
|
||||
: params.credential.type === "token"
|
||||
? { ...params.credential, token: normalizeSecretInput(params.credential.token) }
|
||||
: params.credential;
|
||||
const store = ensureAuthProfileStore(params.agentDir);
|
||||
store.profiles[params.profileId] = params.credential;
|
||||
store.profiles[params.profileId] = credential;
|
||||
saveAuthProfileStore(store, params.agentDir);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { normalizeSecretInput } from "../utils/normalize-secret-input.js";
|
||||
|
||||
type MinimaxBaseResp = {
|
||||
status_code?: number;
|
||||
status_msg?: string;
|
||||
@@ -44,7 +46,7 @@ export async function minimaxUnderstandImage(params: {
|
||||
apiHost?: string;
|
||||
modelBaseUrl?: string;
|
||||
}): Promise<string> {
|
||||
const apiKey = params.apiKey.trim();
|
||||
const apiKey = normalizeSecretInput(params.apiKey);
|
||||
if (!apiKey) {
|
||||
throw new Error("MiniMax VLM: apiKey required");
|
||||
}
|
||||
|
||||
@@ -511,4 +511,25 @@ describe("getApiKeyForModel", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("strips embedded CR/LF from ANTHROPIC_API_KEY", async () => {
|
||||
const previous = process.env.ANTHROPIC_API_KEY;
|
||||
|
||||
try {
|
||||
process.env.ANTHROPIC_API_KEY = "sk-ant-test-\r\nkey";
|
||||
|
||||
vi.resetModules();
|
||||
const { resolveEnvApiKey } = await import("./model-auth.js");
|
||||
|
||||
const resolved = resolveEnvApiKey("anthropic");
|
||||
expect(resolved?.apiKey).toBe("sk-ant-test-key");
|
||||
expect(resolved?.source).toContain("ANTHROPIC_API_KEY");
|
||||
} finally {
|
||||
if (previous === undefined) {
|
||||
delete process.env.ANTHROPIC_API_KEY;
|
||||
} else {
|
||||
process.env.ANTHROPIC_API_KEY = previous;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,10 @@ import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { ModelProviderAuthMode, ModelProviderConfig } from "../config/types.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { getShellEnvAppliedKeys } from "../infra/shell-env.js";
|
||||
import {
|
||||
normalizeOptionalSecretInput,
|
||||
normalizeSecretInput,
|
||||
} from "../utils/normalize-secret-input.js";
|
||||
import {
|
||||
type AuthProfileStore,
|
||||
ensureAuthProfileStore,
|
||||
@@ -48,8 +52,7 @@ export function getCustomProviderApiKey(
|
||||
provider: string,
|
||||
): string | undefined {
|
||||
const entry = resolveProviderConfig(cfg, provider);
|
||||
const key = entry?.apiKey?.trim();
|
||||
return key || undefined;
|
||||
return normalizeOptionalSecretInput(entry?.apiKey);
|
||||
}
|
||||
|
||||
function resolveProviderAuthOverride(
|
||||
@@ -236,7 +239,7 @@ export function resolveEnvApiKey(provider: string): EnvApiKeyResult | null {
|
||||
const normalized = normalizeProviderId(provider);
|
||||
const applied = new Set(getShellEnvAppliedKeys());
|
||||
const pick = (envVar: string): EnvApiKeyResult | null => {
|
||||
const value = process.env[envVar]?.trim();
|
||||
const value = normalizeOptionalSecretInput(process.env[envVar]);
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
@@ -387,7 +390,7 @@ export async function getApiKeyForModel(params: {
|
||||
}
|
||||
|
||||
export function requireApiKey(auth: ResolvedProviderAuth, provider: string): string {
|
||||
const key = auth.apiKey?.trim();
|
||||
const key = normalizeSecretInput(auth.apiKey);
|
||||
if (key) {
|
||||
return key;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { AnyAgentTool } from "./common.js";
|
||||
import { fetchWithSsrFGuard } from "../../infra/net/fetch-guard.js";
|
||||
import { SsrFBlockedError } from "../../infra/net/ssrf.js";
|
||||
import { wrapExternalContent, wrapWebContent } from "../../security/external-content.js";
|
||||
import { normalizeSecretInput } from "../../utils/normalize-secret-input.js";
|
||||
import { stringEnum } from "../schema/typebox.js";
|
||||
import { jsonResult, readNumberParam, readStringParam } from "./common.js";
|
||||
import {
|
||||
@@ -120,9 +121,9 @@ function resolveFirecrawlConfig(fetch?: WebFetchConfig): FirecrawlFetchConfig {
|
||||
function resolveFirecrawlApiKey(firecrawl?: FirecrawlFetchConfig): string | undefined {
|
||||
const fromConfig =
|
||||
firecrawl && "apiKey" in firecrawl && typeof firecrawl.apiKey === "string"
|
||||
? firecrawl.apiKey.trim()
|
||||
? normalizeSecretInput(firecrawl.apiKey)
|
||||
: "";
|
||||
const fromEnv = (process.env.FIRECRAWL_API_KEY ?? "").trim();
|
||||
const fromEnv = normalizeSecretInput(process.env.FIRECRAWL_API_KEY);
|
||||
return fromConfig || fromEnv || undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,8 +81,18 @@ describe("web_search grok config resolution", () => {
|
||||
});
|
||||
|
||||
it("returns undefined when no apiKey is available", () => {
|
||||
expect(resolveGrokApiKey({})).toBeUndefined();
|
||||
expect(resolveGrokApiKey(undefined)).toBeUndefined();
|
||||
const previous = process.env.XAI_API_KEY;
|
||||
try {
|
||||
delete process.env.XAI_API_KEY;
|
||||
expect(resolveGrokApiKey({})).toBeUndefined();
|
||||
expect(resolveGrokApiKey(undefined)).toBeUndefined();
|
||||
} finally {
|
||||
if (previous === undefined) {
|
||||
delete process.env.XAI_API_KEY;
|
||||
} else {
|
||||
process.env.XAI_API_KEY = previous;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("uses default model when not specified", () => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { AnyAgentTool } from "./common.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import { wrapWebContent } from "../../security/external-content.js";
|
||||
import { normalizeSecretInput } from "../../utils/normalize-secret-input.js";
|
||||
import { jsonResult, readNumberParam, readStringParam } from "./common.js";
|
||||
import {
|
||||
CacheEntry,
|
||||
@@ -142,8 +143,10 @@ function resolveSearchEnabled(params: { search?: WebSearchConfig; sandboxed?: bo
|
||||
|
||||
function resolveSearchApiKey(search?: WebSearchConfig): string | undefined {
|
||||
const fromConfig =
|
||||
search && "apiKey" in search && typeof search.apiKey === "string" ? search.apiKey.trim() : "";
|
||||
const fromEnv = (process.env.BRAVE_API_KEY ?? "").trim();
|
||||
search && "apiKey" in search && typeof search.apiKey === "string"
|
||||
? normalizeSecretInput(search.apiKey)
|
||||
: "";
|
||||
const fromEnv = normalizeSecretInput(process.env.BRAVE_API_KEY);
|
||||
return fromConfig || fromEnv || undefined;
|
||||
}
|
||||
|
||||
@@ -222,7 +225,7 @@ function resolvePerplexityApiKey(perplexity?: PerplexityConfig): {
|
||||
}
|
||||
|
||||
function normalizeApiKey(key: unknown): string {
|
||||
return typeof key === "string" ? key.trim() : "";
|
||||
return normalizeSecretInput(key);
|
||||
}
|
||||
|
||||
function inferPerplexityBaseUrlFromApiKey(apiKey?: string): PerplexityBaseUrlHint | undefined {
|
||||
|
||||
Reference in New Issue
Block a user