mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 00:53:28 +00:00
CI: restore main detect-secrets scan (#38438)
* Tests: stabilize detect-secrets fixtures * Tests: fix rebased detect-secrets false positives * Docs: keep snippets valid under detect-secrets * Tests: finalize detect-secrets false-positive fixes * Tests: reduce detect-secrets false positives * Tests: keep detect-secrets pragmas inline * Tests: remediate next detect-secrets batch * Tests: tighten detect-secrets allowlists * Tests: stabilize detect-secrets formatter drift
This commit is contained in:
@@ -23,8 +23,8 @@ vi.mock("@mariozechner/pi-ai", async () => {
|
||||
...actual,
|
||||
getOAuthApiKey: getOAuthApiKeyMock,
|
||||
getOAuthProviders: () => [
|
||||
{ id: "openai-codex", envApiKey: "OPENAI_API_KEY", oauthTokenEnv: "OPENAI_OAUTH_TOKEN" },
|
||||
{ id: "anthropic", envApiKey: "ANTHROPIC_API_KEY", oauthTokenEnv: "ANTHROPIC_OAUTH_TOKEN" },
|
||||
{ id: "openai-codex", envApiKey: "OPENAI_API_KEY", oauthTokenEnv: "OPENAI_OAUTH_TOKEN" }, // pragma: allowlist secret
|
||||
{ id: "anthropic", envApiKey: "ANTHROPIC_API_KEY", oauthTokenEnv: "ANTHROPIC_OAUTH_TOKEN" }, // pragma: allowlist secret
|
||||
],
|
||||
};
|
||||
});
|
||||
@@ -91,7 +91,7 @@ describe("resolveApiKeyForProfile openai-codex refresh fallback", () => {
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
apiKey: "cached-access-token",
|
||||
apiKey: "cached-access-token", // pragma: allowlist secret
|
||||
provider: "openai-codex",
|
||||
email: undefined,
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@ describe("compaction toolResult details stripping", () => {
|
||||
messages,
|
||||
// Minimal shape; compaction won't use these fields in our mocked generateSummary.
|
||||
model: { id: "mock", name: "mock", contextWindow: 10000, maxTokens: 1000 } as never,
|
||||
apiKey: "test",
|
||||
apiKey: "test", // pragma: allowlist secret
|
||||
signal: new AbortController().signal,
|
||||
reserveTokens: 100,
|
||||
maxChunkTokens: 5000,
|
||||
|
||||
@@ -188,7 +188,7 @@ describe("memory search config", () => {
|
||||
provider: "openai",
|
||||
remote: {
|
||||
baseUrl: "https://default.example/v1",
|
||||
apiKey: "default-key",
|
||||
apiKey: "default-key", // pragma: allowlist secret
|
||||
headers: { "X-Default": "on" },
|
||||
},
|
||||
},
|
||||
@@ -209,7 +209,7 @@ describe("memory search config", () => {
|
||||
const resolved = resolveMemorySearchConfig(cfg, "main");
|
||||
expect(resolved?.remote).toEqual({
|
||||
baseUrl: "https://agent.example/v1",
|
||||
apiKey: "default-key",
|
||||
apiKey: "default-key", // pragma: allowlist secret
|
||||
headers: { "X-Default": "on" },
|
||||
batch: {
|
||||
enabled: false,
|
||||
@@ -228,7 +228,7 @@ describe("memory search config", () => {
|
||||
memorySearch: {
|
||||
provider: "openai",
|
||||
remote: {
|
||||
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" }, // pragma: allowlist secret
|
||||
headers: { "X-Default": "on" },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("resolveModelAuthLabel", () => {
|
||||
"github-copilot:default": {
|
||||
type: "token",
|
||||
provider: "github-copilot",
|
||||
token: "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
token: "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // pragma: allowlist secret
|
||||
tokenRef: { source: "env", provider: "default", id: "GITHUB_TOKEN" },
|
||||
},
|
||||
},
|
||||
@@ -52,7 +52,7 @@ describe("resolveModelAuthLabel", () => {
|
||||
});
|
||||
|
||||
it("does not include api-key value in label for api-key profiles", () => {
|
||||
const shortSecret = "abc123";
|
||||
const shortSecret = "abc123"; // pragma: allowlist secret
|
||||
ensureAuthProfileStoreMock.mockReturnValue({
|
||||
version: 1,
|
||||
profiles: {
|
||||
|
||||
@@ -7,6 +7,8 @@ import { withEnvAsync } from "../test-utils/env.js";
|
||||
import { ensureAuthProfileStore } from "./auth-profiles.js";
|
||||
import { getApiKeyForModel, resolveApiKeyForProvider, resolveEnvApiKey } from "./model-auth.js";
|
||||
|
||||
const envVar = (...parts: string[]) => parts.join("_");
|
||||
|
||||
const oauthFixture = {
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
@@ -191,7 +193,7 @@ describe("getApiKeyForModel", () => {
|
||||
await withEnvAsync(
|
||||
{
|
||||
ZAI_API_KEY: undefined,
|
||||
Z_AI_API_KEY: "zai-test-key",
|
||||
Z_AI_API_KEY: "zai-test-key", // pragma: allowlist secret
|
||||
},
|
||||
async () => {
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
@@ -205,7 +207,8 @@ describe("getApiKeyForModel", () => {
|
||||
});
|
||||
|
||||
it("resolves Synthetic API key from env", async () => {
|
||||
await withEnvAsync({ SYNTHETIC_API_KEY: "synthetic-test-key" }, async () => {
|
||||
await withEnvAsync({ [envVar("SYNTHETIC", "API", "KEY")]: "synthetic-test-key" }, async () => {
|
||||
// pragma: allowlist secret
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "synthetic",
|
||||
store: { version: 1, profiles: {} },
|
||||
@@ -216,7 +219,8 @@ describe("getApiKeyForModel", () => {
|
||||
});
|
||||
|
||||
it("resolves Qianfan API key from env", async () => {
|
||||
await withEnvAsync({ QIANFAN_API_KEY: "qianfan-test-key" }, async () => {
|
||||
await withEnvAsync({ [envVar("QIANFAN", "API", "KEY")]: "qianfan-test-key" }, async () => {
|
||||
// pragma: allowlist secret
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "qianfan",
|
||||
store: { version: 1, profiles: {} },
|
||||
@@ -250,7 +254,8 @@ describe("getApiKeyForModel", () => {
|
||||
});
|
||||
|
||||
it("prefers explicit OLLAMA_API_KEY over synthetic local key", async () => {
|
||||
await withEnvAsync({ OLLAMA_API_KEY: "env-ollama-key" }, async () => {
|
||||
await withEnvAsync({ [envVar("OLLAMA", "API", "KEY")]: "env-ollama-key" }, async () => {
|
||||
// pragma: allowlist secret
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "ollama",
|
||||
store: { version: 1, profiles: {} },
|
||||
@@ -283,7 +288,8 @@ describe("getApiKeyForModel", () => {
|
||||
});
|
||||
|
||||
it("resolves Vercel AI Gateway API key from env", async () => {
|
||||
await withEnvAsync({ AI_GATEWAY_API_KEY: "gateway-test-key" }, async () => {
|
||||
await withEnvAsync({ [envVar("AI_GATEWAY", "API", "KEY")]: "gateway-test-key" }, async () => {
|
||||
// pragma: allowlist secret
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "vercel-ai-gateway",
|
||||
store: { version: 1, profiles: {} },
|
||||
@@ -296,9 +302,9 @@ describe("getApiKeyForModel", () => {
|
||||
it("prefers Bedrock bearer token over access keys and profile", async () => {
|
||||
await expectBedrockAuthSource({
|
||||
env: {
|
||||
AWS_BEARER_TOKEN_BEDROCK: "bedrock-token",
|
||||
AWS_BEARER_TOKEN_BEDROCK: "bedrock-token", // pragma: allowlist secret
|
||||
AWS_ACCESS_KEY_ID: "access-key",
|
||||
AWS_SECRET_ACCESS_KEY: "secret-key",
|
||||
[envVar("AWS", "SECRET", "ACCESS", "KEY")]: "secret-key", // pragma: allowlist secret
|
||||
AWS_PROFILE: "profile",
|
||||
},
|
||||
expectedSource: "AWS_BEARER_TOKEN_BEDROCK",
|
||||
@@ -310,7 +316,7 @@ describe("getApiKeyForModel", () => {
|
||||
env: {
|
||||
AWS_BEARER_TOKEN_BEDROCK: undefined,
|
||||
AWS_ACCESS_KEY_ID: "access-key",
|
||||
AWS_SECRET_ACCESS_KEY: "secret-key",
|
||||
[envVar("AWS", "SECRET", "ACCESS", "KEY")]: "secret-key", // pragma: allowlist secret
|
||||
AWS_PROFILE: "profile",
|
||||
},
|
||||
expectedSource: "AWS_ACCESS_KEY_ID",
|
||||
@@ -330,7 +336,8 @@ describe("getApiKeyForModel", () => {
|
||||
});
|
||||
|
||||
it("accepts VOYAGE_API_KEY for voyage", async () => {
|
||||
await withEnvAsync({ VOYAGE_API_KEY: "voyage-test-key" }, async () => {
|
||||
await withEnvAsync({ [envVar("VOYAGE", "API", "KEY")]: "voyage-test-key" }, async () => {
|
||||
// pragma: allowlist secret
|
||||
const voyage = await resolveApiKeyForProvider({
|
||||
provider: "voyage",
|
||||
store: { version: 1, profiles: {} },
|
||||
@@ -341,7 +348,8 @@ describe("getApiKeyForModel", () => {
|
||||
});
|
||||
|
||||
it("strips embedded CR/LF from ANTHROPIC_API_KEY", async () => {
|
||||
await withEnvAsync({ ANTHROPIC_API_KEY: "sk-ant-test-\r\nkey" }, async () => {
|
||||
await withEnvAsync({ [envVar("ANTHROPIC", "API", "KEY")]: "sk-ant-test-\r\nkey" }, async () => {
|
||||
// pragma: allowlist secret
|
||||
const resolved = resolveEnvApiKey("anthropic");
|
||||
expect(resolved?.apiKey).toBe("sk-ant-test-key");
|
||||
expect(resolved?.source).toContain("ANTHROPIC_API_KEY");
|
||||
|
||||
@@ -95,6 +95,7 @@ const makeAttempt = (overrides: Partial<EmbeddedRunAttemptResult>): EmbeddedRunA
|
||||
});
|
||||
|
||||
function makeConfig(): OpenClawConfig {
|
||||
const apiKeyField = ["api", "Key"].join("");
|
||||
return {
|
||||
agents: {
|
||||
defaults: {
|
||||
@@ -108,7 +109,7 @@ function makeConfig(): OpenClawConfig {
|
||||
providers: {
|
||||
openai: {
|
||||
api: "openai-responses",
|
||||
apiKey: "sk-openai",
|
||||
[apiKeyField]: "openai-test-key", // pragma: allowlist secret
|
||||
baseUrl: "https://example.com/openai",
|
||||
models: [
|
||||
{
|
||||
@@ -124,7 +125,7 @@ function makeConfig(): OpenClawConfig {
|
||||
},
|
||||
groq: {
|
||||
api: "openai-responses",
|
||||
apiKey: "sk-groq",
|
||||
[apiKeyField]: "groq-test-key", // pragma: allowlist secret
|
||||
baseUrl: "https://example.com/groq",
|
||||
models: [
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ async function writeAgentModelsJson(content: unknown): Promise<void> {
|
||||
function createMergeConfigProvider() {
|
||||
return {
|
||||
baseUrl: "https://config.example/v1",
|
||||
apiKey: "CONFIG_KEY",
|
||||
apiKey: "CONFIG_KEY", // pragma: allowlist secret
|
||||
api: "openai-responses" as const,
|
||||
models: [
|
||||
{
|
||||
@@ -115,7 +115,7 @@ describe("models-config", () => {
|
||||
providers: {
|
||||
anthropic: {
|
||||
baseUrl: "https://relay.example.com/api",
|
||||
apiKey: "cr_xxxx",
|
||||
apiKey: "cr_xxxx", // pragma: allowlist secret
|
||||
models: [{ id: "claude-opus-4-6", name: "Claude Opus 4.6" }],
|
||||
},
|
||||
},
|
||||
@@ -179,7 +179,7 @@ describe("models-config", () => {
|
||||
providers: {
|
||||
existing: {
|
||||
baseUrl: "http://localhost:1234/v1",
|
||||
apiKey: "EXISTING_KEY",
|
||||
apiKey: "EXISTING_KEY", // pragma: allowlist secret
|
||||
api: "openai-completions",
|
||||
models: [
|
||||
{
|
||||
@@ -212,7 +212,7 @@ describe("models-config", () => {
|
||||
await withTempHome(async () => {
|
||||
const parsed = await runCustomProviderMergeTest({
|
||||
baseUrl: "https://agent.example/v1",
|
||||
apiKey: "AGENT_KEY",
|
||||
apiKey: "AGENT_KEY", // pragma: allowlist secret
|
||||
api: "openai-responses",
|
||||
models: [{ id: "agent-model", name: "Agent model", input: ["text"] }],
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ describe("models-config", () => {
|
||||
providers: {
|
||||
google: {
|
||||
baseUrl: "https://generativelanguage.googleapis.com/v1beta",
|
||||
apiKey: "GEMINI_KEY",
|
||||
apiKey: "GEMINI_KEY", // pragma: allowlist secret
|
||||
api: "google-generative-ai",
|
||||
models: [
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ function buildProvider(modelIds: string[]): ProviderConfig {
|
||||
return {
|
||||
baseUrl: "https://example.invalid/v1",
|
||||
api: "openai-completions",
|
||||
apiKey: "EXAMPLE_KEY",
|
||||
apiKey: "EXAMPLE_KEY", // pragma: allowlist secret
|
||||
models: modelIds.map((id) => buildModel(id)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ describe("Kilo Gateway implicit provider", () => {
|
||||
it("should include kilocode when KILOCODE_API_KEY is configured", async () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const envSnapshot = captureEnv(["KILOCODE_API_KEY"]);
|
||||
process.env.KILOCODE_API_KEY = "test-key";
|
||||
process.env.KILOCODE_API_KEY = "test-key"; // pragma: allowlist secret
|
||||
|
||||
try {
|
||||
const providers = await resolveImplicitProviders({ agentDir });
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("kimi-coding implicit provider (#22409)", () => {
|
||||
it("should include kimi-coding when KIMI_API_KEY is configured", async () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const envSnapshot = captureEnv(["KIMI_API_KEY"]);
|
||||
process.env.KIMI_API_KEY = "test-key";
|
||||
process.env.KIMI_API_KEY = "test-key"; // pragma: allowlist secret
|
||||
|
||||
try {
|
||||
const providers = await resolveImplicitProviders({ agentDir });
|
||||
|
||||
@@ -14,7 +14,7 @@ describe("normalizeProviders", () => {
|
||||
" dashscope-vision ": {
|
||||
baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
api: "openai-completions",
|
||||
apiKey: "DASHSCOPE_API_KEY",
|
||||
apiKey: "DASHSCOPE_API_KEY", // pragma: allowlist secret
|
||||
models: [
|
||||
{
|
||||
id: "qwen-vl-max",
|
||||
@@ -44,13 +44,13 @@ describe("normalizeProviders", () => {
|
||||
openai: {
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
api: "openai-completions",
|
||||
apiKey: "OPENAI_API_KEY",
|
||||
apiKey: "OPENAI_API_KEY", // pragma: allowlist secret
|
||||
models: [],
|
||||
},
|
||||
" openai ": {
|
||||
baseUrl: "https://example.com/v1",
|
||||
api: "openai-completions",
|
||||
apiKey: "CUSTOM_OPENAI_API_KEY",
|
||||
apiKey: "CUSTOM_OPENAI_API_KEY", // pragma: allowlist secret
|
||||
models: [
|
||||
{
|
||||
id: "gpt-4.1-mini",
|
||||
|
||||
@@ -51,7 +51,7 @@ describe("Ollama provider", () => {
|
||||
};
|
||||
|
||||
async function withOllamaApiKey<T>(run: () => Promise<T>): Promise<T> {
|
||||
process.env.OLLAMA_API_KEY = "test-key";
|
||||
process.env.OLLAMA_API_KEY = "test-key"; // pragma: allowlist secret
|
||||
try {
|
||||
return await run();
|
||||
} finally {
|
||||
@@ -245,7 +245,7 @@ describe("Ollama provider", () => {
|
||||
ollama: {
|
||||
baseUrl: "http://remote-ollama:11434/v1",
|
||||
models: explicitModels,
|
||||
apiKey: "config-ollama-key",
|
||||
apiKey: "config-ollama-key", // pragma: allowlist secret
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -271,7 +271,7 @@ describe("Ollama provider", () => {
|
||||
baseUrl: "http://remote-ollama:11434/v1",
|
||||
api: "openai-completions",
|
||||
models: [],
|
||||
apiKey: "config-ollama-key",
|
||||
apiKey: "config-ollama-key", // pragma: allowlist secret
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -5,10 +5,14 @@ import { describe, expect, it } from "vitest";
|
||||
import { withEnvAsync } from "../test-utils/env.js";
|
||||
import { resolveImplicitProviders } from "./models-config.providers.js";
|
||||
|
||||
const qianfanApiKeyEnv = ["QIANFAN_API", "KEY"].join("_");
|
||||
|
||||
describe("Qianfan provider", () => {
|
||||
it("should include qianfan when QIANFAN_API_KEY is configured", async () => {
|
||||
// pragma: allowlist secret
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
await withEnvAsync({ QIANFAN_API_KEY: "test-key" }, async () => {
|
||||
const qianfanApiKey = "test-key"; // pragma: allowlist secret
|
||||
await withEnvAsync({ [qianfanApiKeyEnv]: qianfanApiKey }, async () => {
|
||||
const providers = await resolveImplicitProviders({ agentDir });
|
||||
expect(providers?.qianfan).toBeDefined();
|
||||
expect(providers?.qianfan?.apiKey).toBe("QIANFAN_API_KEY");
|
||||
|
||||
@@ -10,7 +10,7 @@ describe("Volcengine and BytePlus providers", () => {
|
||||
it("includes volcengine and volcengine-plan when VOLCANO_ENGINE_API_KEY is configured", async () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const envSnapshot = captureEnv(["VOLCANO_ENGINE_API_KEY"]);
|
||||
process.env.VOLCANO_ENGINE_API_KEY = "test-key";
|
||||
process.env.VOLCANO_ENGINE_API_KEY = "test-key"; // pragma: allowlist secret
|
||||
|
||||
try {
|
||||
const providers = await resolveImplicitProviders({ agentDir });
|
||||
@@ -26,7 +26,7 @@ describe("Volcengine and BytePlus providers", () => {
|
||||
it("includes byteplus and byteplus-plan when BYTEPLUS_API_KEY is configured", async () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const envSnapshot = captureEnv(["BYTEPLUS_API_KEY"]);
|
||||
process.env.BYTEPLUS_API_KEY = "test-key";
|
||||
process.env.BYTEPLUS_API_KEY = "test-key"; // pragma: allowlist secret
|
||||
|
||||
try {
|
||||
const providers = await resolveImplicitProviders({ agentDir });
|
||||
|
||||
@@ -97,7 +97,7 @@ describe("models-config", () => {
|
||||
envValue: "sk-minimax-test",
|
||||
providerKey: "minimax",
|
||||
expectedBaseUrl: "https://api.minimax.io/anthropic",
|
||||
expectedApiKeyRef: "MINIMAX_API_KEY",
|
||||
expectedApiKeyRef: "MINIMAX_API_KEY", // pragma: allowlist secret
|
||||
expectedModelIds: ["MiniMax-M2.5", "MiniMax-VL-01"],
|
||||
});
|
||||
});
|
||||
@@ -110,7 +110,7 @@ describe("models-config", () => {
|
||||
envValue: "sk-synthetic-test",
|
||||
providerKey: "synthetic",
|
||||
expectedBaseUrl: "https://api.synthetic.new/anthropic",
|
||||
expectedApiKeyRef: "SYNTHETIC_API_KEY",
|
||||
expectedApiKeyRef: "SYNTHETIC_API_KEY", // pragma: allowlist secret
|
||||
expectedModelIds: ["hf:MiniMaxAI/MiniMax-M2.5"],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ describe("resolveOwnerDisplaySetting", () => {
|
||||
|
||||
expect(resolveOwnerDisplaySetting(cfg)).toEqual({
|
||||
ownerDisplay: "hash",
|
||||
ownerDisplaySecret: "owner-secret",
|
||||
ownerDisplaySecret: "owner-secret", // pragma: allowlist secret
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ describe("resolveOwnerDisplaySetting", () => {
|
||||
const cfg = {
|
||||
commands: {
|
||||
ownerDisplay: "raw",
|
||||
ownerDisplaySecret: "owner-secret",
|
||||
ownerDisplaySecret: "owner-secret", // pragma: allowlist secret
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
@@ -67,7 +67,7 @@ describe("ensureOwnerDisplaySecret", () => {
|
||||
const cfg = {
|
||||
commands: {
|
||||
ownerDisplay: "hash",
|
||||
ownerDisplaySecret: "existing-owner-secret",
|
||||
ownerDisplaySecret: "existing-owner-secret", // pragma: allowlist secret
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
|
||||
@@ -1072,7 +1072,7 @@ describe("applyExtraParamsToAgent", () => {
|
||||
|
||||
// Simulate pi-agent-core passing apiKey in options (API key, not OAuth token)
|
||||
void agent.streamFn?.(model, context, {
|
||||
apiKey: "sk-ant-api03-test",
|
||||
apiKey: "sk-ant-api03-test", // pragma: allowlist secret
|
||||
headers: { "X-Custom": "1" },
|
||||
});
|
||||
|
||||
@@ -1130,7 +1130,7 @@ describe("applyExtraParamsToAgent", () => {
|
||||
|
||||
// Simulate pi-agent-core passing an OAuth token (sk-ant-oat-*) as apiKey
|
||||
void agent.streamFn?.(model, context, {
|
||||
apiKey: "sk-ant-oat01-test-oauth-token",
|
||||
apiKey: "sk-ant-oat01-test-oauth-token", // pragma: allowlist secret
|
||||
headers: { "X-Custom": "1" },
|
||||
});
|
||||
|
||||
@@ -1151,7 +1151,7 @@ describe("applyExtraParamsToAgent", () => {
|
||||
cfg,
|
||||
modelId: "claude-sonnet-4-5",
|
||||
options: {
|
||||
apiKey: "sk-ant-api03-test",
|
||||
apiKey: "sk-ant-api03-test", // pragma: allowlist secret
|
||||
headers: { "anthropic-beta": "prompt-caching-2024-07-31" },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -156,7 +156,7 @@ const makeAgentOverrideOnlyFallbackConfig = (agentId: string): OpenClawConfig =>
|
||||
providers: {
|
||||
openai: {
|
||||
api: "openai-responses",
|
||||
apiKey: "sk-test",
|
||||
apiKey: "sk-test", // pragma: allowlist secret
|
||||
baseUrl: "https://example.com",
|
||||
models: [
|
||||
{
|
||||
|
||||
@@ -697,7 +697,7 @@ describe("compaction-safeguard recent-turn preservation", () => {
|
||||
"Track id a1b2c3d4e5f6 plus A1B2C3D4E5F6 and URL https://example.com/a and /tmp/x.log plus port host.local:18789",
|
||||
);
|
||||
expect(identifiers.length).toBeGreaterThan(0);
|
||||
expect(identifiers).toContain("A1B2C3D4E5F6");
|
||||
expect(identifiers).toContain("A1B2C3D4E5F6"); // pragma: allowlist secret
|
||||
|
||||
const summary = [
|
||||
"## Decisions",
|
||||
@@ -724,7 +724,7 @@ describe("compaction-safeguard recent-turn preservation", () => {
|
||||
const identifiers = extractOpaqueIdentifiers(
|
||||
"Track id a1b2c3d4e5f6 plus A1B2C3D4E5F6 and again a1b2c3d4e5f6",
|
||||
);
|
||||
expect(identifiers.filter((id) => id === "A1B2C3D4E5F6")).toHaveLength(1);
|
||||
expect(identifiers.filter((id) => id === "A1B2C3D4E5F6")).toHaveLength(1); // pragma: allowlist secret
|
||||
});
|
||||
|
||||
it("dedupes identifiers before applying the result cap", () => {
|
||||
@@ -843,9 +843,9 @@ describe("compaction-safeguard recent-turn preservation", () => {
|
||||
"## Pending user asks",
|
||||
"Provide status.",
|
||||
"## Exact identifiers",
|
||||
"a1b2c3d4e5f6",
|
||||
"a1b2c3d4e5f6", // pragma: allowlist secret
|
||||
].join("\n"),
|
||||
identifiers: ["A1B2C3D4E5F6"],
|
||||
identifiers: ["A1B2C3D4E5F6"], // pragma: allowlist secret
|
||||
latestAsk: "Provide status.",
|
||||
identifierPolicy: "strict",
|
||||
});
|
||||
@@ -1522,7 +1522,7 @@ describe("compaction-safeguard double-compaction guard", () => {
|
||||
const { result, getApiKeyMock } = await runCompactionScenario({
|
||||
sessionManager,
|
||||
event: mockEvent,
|
||||
apiKey: "sk-test",
|
||||
apiKey: "sk-test", // pragma: allowlist secret
|
||||
});
|
||||
expect(result).toEqual({ cancel: true });
|
||||
expect(getApiKeyMock).not.toHaveBeenCalled();
|
||||
|
||||
@@ -9,13 +9,16 @@ import {
|
||||
resetNoVncObserverTokensForTests,
|
||||
} from "./novnc-auth.js";
|
||||
|
||||
const passwordKey = ["pass", "word"].join("");
|
||||
|
||||
describe("noVNC auth helpers", () => {
|
||||
it("builds the default observer URL without password", () => {
|
||||
expect(buildNoVncDirectUrl(45678)).toBe("http://127.0.0.1:45678/vnc.html");
|
||||
});
|
||||
|
||||
it("builds a fragment-based observer target URL with password", () => {
|
||||
expect(buildNoVncObserverTargetUrl({ port: 45678, password: "a+b c&d" })).toBe(
|
||||
const observerPassword = "a+b c&d"; // pragma: allowlist secret
|
||||
expect(buildNoVncObserverTargetUrl({ port: 45678, [passwordKey]: observerPassword })).toBe(
|
||||
"http://127.0.0.1:45678/vnc.html#autoconnect=1&resize=remote&password=a%2Bb+c%26d",
|
||||
);
|
||||
});
|
||||
@@ -24,7 +27,7 @@ describe("noVNC auth helpers", () => {
|
||||
resetNoVncObserverTokensForTests();
|
||||
const token = issueNoVncObserverToken({
|
||||
noVncPort: 50123,
|
||||
password: "abcd1234",
|
||||
[passwordKey]: "abcd1234", // pragma: allowlist secret
|
||||
nowMs: 1000,
|
||||
ttlMs: 100,
|
||||
});
|
||||
@@ -33,7 +36,7 @@ describe("noVNC auth helpers", () => {
|
||||
);
|
||||
expect(consumeNoVncObserverToken(token, 1050)).toEqual({
|
||||
noVncPort: 50123,
|
||||
password: "abcd1234",
|
||||
[passwordKey]: "abcd1234", // pragma: allowlist secret
|
||||
});
|
||||
expect(consumeNoVncObserverToken(token, 1050)).toBeNull();
|
||||
});
|
||||
@@ -42,7 +45,7 @@ describe("noVNC auth helpers", () => {
|
||||
resetNoVncObserverTokensForTests();
|
||||
const token = issueNoVncObserverToken({
|
||||
noVncPort: 50123,
|
||||
password: "abcd1234",
|
||||
password: "abcd1234", // pragma: allowlist secret
|
||||
nowMs: 1000,
|
||||
ttlMs: 100,
|
||||
});
|
||||
|
||||
@@ -5,9 +5,9 @@ describe("sanitizeEnvVars", () => {
|
||||
it("keeps normal env vars and blocks obvious credentials", () => {
|
||||
const result = sanitizeEnvVars({
|
||||
NODE_ENV: "test",
|
||||
OPENAI_API_KEY: "sk-live-xxx",
|
||||
OPENAI_API_KEY: "sk-live-xxx", // pragma: allowlist secret
|
||||
FOO: "bar",
|
||||
GITHUB_TOKEN: "gh-token",
|
||||
GITHUB_TOKEN: "gh-token", // pragma: allowlist secret
|
||||
});
|
||||
|
||||
expect(result.allowed).toEqual({
|
||||
|
||||
@@ -29,7 +29,7 @@ function mkSessionsSpawnToolCall(content: string): AgentMessage {
|
||||
|
||||
describe("sanitizeToolCallInputs redacts sessions_spawn attachments", () => {
|
||||
it("replaces attachments[].content with __OPENCLAW_REDACTED__", () => {
|
||||
const secret = "SUPER_SECRET_SHOULD_NOT_PERSIST";
|
||||
const secret = "SUPER_SECRET_SHOULD_NOT_PERSIST"; // pragma: allowlist secret
|
||||
const input = [mkSessionsSpawnToolCall(secret)];
|
||||
const out = sanitizeToolCallInputs(input);
|
||||
expect(out).toHaveLength(1);
|
||||
@@ -44,7 +44,7 @@ describe("sanitizeToolCallInputs redacts sessions_spawn attachments", () => {
|
||||
});
|
||||
|
||||
it("redacts attachments content from tool input payloads too", () => {
|
||||
const secret = "INPUT_SECRET_SHOULD_NOT_PERSIST";
|
||||
const secret = "INPUT_SECRET_SHOULD_NOT_PERSIST"; // pragma: allowlist secret
|
||||
const input = castAgentMessages([
|
||||
{
|
||||
role: "assistant",
|
||||
|
||||
@@ -48,7 +48,7 @@ const ZIP_SLIP_BUFFER = Buffer.from(
|
||||
);
|
||||
const TAR_GZ_TRAVERSAL_BUFFER = Buffer.from(
|
||||
// Prebuilt archive containing ../outside-write/pwned.txt.
|
||||
"H4sIAK4xm2kAA+2VvU7DMBDH3UoIUWaYLXbcS5PYZegQEKhBRUBbIT4GZBpXCqJNSFySlSdgZed1eCgcUvFRaMsQgVD9k05nW3eWz8nfR0g1GMnY98RmEvlSVMllmAyFR2QqUUEAALUsnHlG7VcPtXwO+djEhm1YlJpAbYrBYAYDhKGoA8xiFEseqaPEUvihkGJanArr92fsk5eC3/x/YWl9GZUROuA9fNjBp3hMtoZWlNWU3SrL5k8/29LpdtvjYZbxqGx1IqT0vr7WCwaEh+GNIGEU3IkhH/YEKpXRxv3FQznsPxdQpGYaZFL/RzxtCu6JqFrYOzBX/wZ81n8NmEERTosocB4Lrn8T8ED6A9EwmHp0Wd1idQK2ZVIAm1ZshlvuttPeabonuyTlUkbkO7k2nGPXcYO9q+tkPzmPk4q1hTsqqXU2K+mDxit/fQ+Lyhf9F9795+tf/WoT/Z8yi+n+/xuoz+1p8Wk0Gs3i8QJSs3VlABAAAA==",
|
||||
"H4sIAK4xm2kAA+2VvU7DMBDH3UoIUWaYLXbcS5PYZegQEKhBRUBbIT4GZBpXCqJNSFySlSdgZed1eCgcUvFRaMsQgVD9k05nW3eWz8nfR0g1GMnY98RmEvlSVMllmAyFR2QqUUEAALUsnHlG7VcPtXwO+djEhm1YlJpAbYrBYAYDhKGoA8xiFEseqaPEUvihkGJanArr92fsk5eC3/x/YWl9GZUROuA9fNjBp3hMtoZWlNWU3SrL5k8/29LpdtvjYZbxqGx1IqT0vr7WCwaEh+GNIGEU3IkhH/YEKpXRxv3FQznsPxdQpGYaZFL/RzxtCu6JqFrYOzBX/wZ81n8NmEERTosocB4Lrn8T8ED6A9EwmHp0Wd1idQK2ZVIAm1ZshlvuttPeabonuyTlUkbkO7k2nGPXcYO9q+tkPzmPk4q1hTsqqXU2K+mDxit/fQ+Lyhf9F9795+tf/WoT/Z8yi+n+/xuoz+1p8Wk0Gs3i8QJSs3VlABAAAA==", // pragma: allowlist secret
|
||||
"base64",
|
||||
);
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
managedSkillsDir,
|
||||
config: {
|
||||
browser: { enabled: false },
|
||||
skills: { entries: { "env-skill": { apiKey: "ok" } } },
|
||||
skills: { entries: { "env-skill": { apiKey: "ok" } } }, // pragma: allowlist secret
|
||||
},
|
||||
eligibility: {
|
||||
remote: {
|
||||
|
||||
@@ -178,7 +178,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
const enabledPrompt = buildPrompt(workspaceDir, {
|
||||
managedSkillsDir: path.join(workspaceDir, ".managed"),
|
||||
config: {
|
||||
skills: { entries: { "nano-banana-pro": { apiKey: "test-key" } } },
|
||||
skills: { entries: { "nano-banana-pro": { apiKey: "test-key" } } }, // pragma: allowlist secret
|
||||
},
|
||||
});
|
||||
expect(enabledPrompt).toContain("nano-banana-pro");
|
||||
|
||||
@@ -23,6 +23,7 @@ const resolveTestSkillDirs = (workspaceDir: string) => ({
|
||||
});
|
||||
|
||||
const makeWorkspace = async () => await fixtureSuite.createCaseDir("workspace");
|
||||
const apiKeyField = ["api", "Key"].join("");
|
||||
|
||||
const withClearedEnv = <T>(
|
||||
keys: string[],
|
||||
@@ -252,7 +253,7 @@ describe("applySkillEnvOverrides", () => {
|
||||
withClearedEnv(["ENV_KEY"], () => {
|
||||
const restore = applySkillEnvOverrides({
|
||||
skills: entries,
|
||||
config: { skills: { entries: { "env-skill": { apiKey: "injected" } } } },
|
||||
config: { skills: { entries: { "env-skill": { apiKey: "injected" } } } }, // pragma: allowlist secret
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -279,7 +280,7 @@ describe("applySkillEnvOverrides", () => {
|
||||
const entries = loadWorkspaceSkillEntries(workspaceDir, resolveTestSkillDirs(workspaceDir));
|
||||
|
||||
withClearedEnv(["ENV_KEY"], () => {
|
||||
const config = { skills: { entries: { "env-skill": { apiKey: "injected" } } } };
|
||||
const config = { skills: { entries: { "env-skill": { [apiKeyField]: "injected" } } } }; // pragma: allowlist secret
|
||||
const restoreFirst = applySkillEnvOverrides({ skills: entries, config });
|
||||
const restoreSecond = applySkillEnvOverrides({ skills: entries, config });
|
||||
|
||||
@@ -310,13 +311,13 @@ describe("applySkillEnvOverrides", () => {
|
||||
|
||||
const snapshot = buildWorkspaceSkillSnapshot(workspaceDir, {
|
||||
...resolveTestSkillDirs(workspaceDir),
|
||||
config: { skills: { entries: { "env-skill": { apiKey: "snap-key" } } } },
|
||||
config: { skills: { entries: { "env-skill": { apiKey: "snap-key" } } } }, // pragma: allowlist secret
|
||||
});
|
||||
|
||||
withClearedEnv(["ENV_KEY"], () => {
|
||||
const restore = applySkillEnvOverridesFromSnapshot({
|
||||
snapshot,
|
||||
config: { skills: { entries: { "env-skill": { apiKey: "snap-key" } } } },
|
||||
config: { skills: { entries: { "env-skill": { apiKey: "snap-key" } } } }, // pragma: allowlist secret
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -349,7 +350,7 @@ describe("applySkillEnvOverrides", () => {
|
||||
entries: {
|
||||
"unsafe-env-skill": {
|
||||
env: {
|
||||
OPENAI_API_KEY: "sk-test",
|
||||
OPENAI_API_KEY: "sk-test", // pragma: allowlist secret
|
||||
NODE_OPTIONS: "--require /tmp/evil.js",
|
||||
},
|
||||
},
|
||||
@@ -424,7 +425,7 @@ describe("applySkillEnvOverrides", () => {
|
||||
entries: {
|
||||
"snapshot-env-skill": {
|
||||
env: {
|
||||
OPENAI_API_KEY: "snap-secret",
|
||||
OPENAI_API_KEY: "snap-secret", // pragma: allowlist secret
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -73,14 +73,14 @@ describe("buildAgentSystemPrompt", () => {
|
||||
workspaceDir: "/tmp/openclaw",
|
||||
ownerNumbers: ["+123"],
|
||||
ownerDisplay: "hash",
|
||||
ownerDisplaySecret: "secret-key-A",
|
||||
ownerDisplaySecret: "secret-key-A", // pragma: allowlist secret
|
||||
});
|
||||
|
||||
const secretB = buildAgentSystemPrompt({
|
||||
workspaceDir: "/tmp/openclaw",
|
||||
ownerNumbers: ["+123"],
|
||||
ownerDisplay: "hash",
|
||||
ownerDisplaySecret: "secret-key-B",
|
||||
ownerDisplaySecret: "secret-key-B", // pragma: allowlist secret
|
||||
});
|
||||
|
||||
const lineA = secretA.split("## Authorized Senders")[1]?.split("\n")[1];
|
||||
|
||||
@@ -71,7 +71,7 @@ function makeAnthropicAnalyzeParams(
|
||||
}> = {},
|
||||
) {
|
||||
return {
|
||||
apiKey: "test-key",
|
||||
apiKey: "test-key", // pragma: allowlist secret
|
||||
modelId: "claude-opus-4-6",
|
||||
prompt: "test",
|
||||
pdfs: [TEST_PDF_INPUT],
|
||||
@@ -89,7 +89,7 @@ function makeGeminiAnalyzeParams(
|
||||
}> = {},
|
||||
) {
|
||||
return {
|
||||
apiKey: "test-key",
|
||||
apiKey: "test-key", // pragma: allowlist secret
|
||||
modelId: "gemini-2.5-pro",
|
||||
prompt: "test",
|
||||
pdfs: [TEST_PDF_INPUT],
|
||||
@@ -156,7 +156,7 @@ async function stubPdfToolInfra(
|
||||
});
|
||||
|
||||
const modelAuth = await import("../model-auth.js");
|
||||
vi.spyOn(modelAuth, "getApiKeyForModel").mockResolvedValue({ apiKey: "test-key" } as never);
|
||||
vi.spyOn(modelAuth, "getApiKeyForModel").mockResolvedValue({ apiKey: "test-key" } as never); // pragma: allowlist secret
|
||||
vi.spyOn(modelAuth, "requireApiKey").mockReturnValue("test-key");
|
||||
|
||||
return { loadSpy };
|
||||
|
||||
@@ -81,7 +81,7 @@ describe("web_fetch SSRF protection", () => {
|
||||
it("blocks localhost hostnames before fetch/firecrawl", async () => {
|
||||
const fetchSpy = setMockFetch();
|
||||
const tool = await createWebFetchToolForTest({
|
||||
firecrawl: { apiKey: "firecrawl-test" },
|
||||
firecrawl: { apiKey: "firecrawl-test" }, // pragma: allowlist secret
|
||||
});
|
||||
|
||||
await expectBlockedUrl(tool, "http://localhost/test", /Blocked hostname/i);
|
||||
@@ -123,7 +123,7 @@ describe("web_fetch SSRF protection", () => {
|
||||
redirectResponse("http://127.0.0.1/secret"),
|
||||
);
|
||||
const tool = await createWebFetchToolForTest({
|
||||
firecrawl: { apiKey: "firecrawl-test" },
|
||||
firecrawl: { apiKey: "firecrawl-test" }, // pragma: allowlist secret
|
||||
});
|
||||
|
||||
await expectBlockedUrl(tool, "https://example.com", /private|internal|blocked/i);
|
||||
|
||||
@@ -17,6 +17,9 @@ const {
|
||||
extractKimiCitations,
|
||||
} = __testing;
|
||||
|
||||
const kimiApiKeyEnv = ["KIMI_API", "KEY"].join("_");
|
||||
const moonshotApiKeyEnv = ["MOONSHOT_API", "KEY"].join("_");
|
||||
|
||||
describe("web_search brave language param normalization", () => {
|
||||
it("normalizes and auto-corrects swapped Brave language params", () => {
|
||||
expect(normalizeBraveLanguageParams({ search_lang: "tr-TR", ui_lang: "tr" })).toEqual({
|
||||
@@ -102,7 +105,7 @@ describe("web_search date normalization", () => {
|
||||
|
||||
describe("web_search grok config resolution", () => {
|
||||
it("uses config apiKey when provided", () => {
|
||||
expect(resolveGrokApiKey({ apiKey: "xai-test-key" })).toBe("xai-test-key");
|
||||
expect(resolveGrokApiKey({ apiKey: "xai-test-key" })).toBe("xai-test-key"); // pragma: allowlist secret
|
||||
});
|
||||
|
||||
it("returns undefined when no apiKey is available", () => {
|
||||
@@ -221,15 +224,17 @@ describe("web_search grok response parsing", () => {
|
||||
|
||||
describe("web_search kimi config resolution", () => {
|
||||
it("uses config apiKey when provided", () => {
|
||||
expect(resolveKimiApiKey({ apiKey: "kimi-test-key" })).toBe("kimi-test-key");
|
||||
expect(resolveKimiApiKey({ apiKey: "kimi-test-key" })).toBe("kimi-test-key"); // pragma: allowlist secret
|
||||
});
|
||||
|
||||
it("falls back to KIMI_API_KEY, then MOONSHOT_API_KEY", () => {
|
||||
withEnv({ KIMI_API_KEY: "kimi-env", MOONSHOT_API_KEY: "moonshot-env" }, () => {
|
||||
expect(resolveKimiApiKey({})).toBe("kimi-env");
|
||||
const kimiEnvValue = "kimi-env"; // pragma: allowlist secret
|
||||
const moonshotEnvValue = "moonshot-env"; // pragma: allowlist secret
|
||||
withEnv({ [kimiApiKeyEnv]: kimiEnvValue, [moonshotApiKeyEnv]: moonshotEnvValue }, () => {
|
||||
expect(resolveKimiApiKey({})).toBe(kimiEnvValue);
|
||||
});
|
||||
withEnv({ KIMI_API_KEY: undefined, MOONSHOT_API_KEY: "moonshot-env" }, () => {
|
||||
expect(resolveKimiApiKey({})).toBe("moonshot-env");
|
||||
withEnv({ [kimiApiKeyEnv]: undefined, [moonshotApiKeyEnv]: moonshotEnvValue }, () => {
|
||||
expect(resolveKimiApiKey({})).toBe(moonshotEnvValue);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -50,14 +50,14 @@ function createKimiSearchTool(kimiConfig?: { apiKey?: string; baseUrl?: string;
|
||||
function createProviderSearchTool(provider: "brave" | "perplexity" | "grok" | "gemini" | "kimi") {
|
||||
const searchConfig =
|
||||
provider === "perplexity"
|
||||
? { provider, perplexity: { apiKey: "pplx-config-test" } }
|
||||
? { provider, perplexity: { apiKey: "pplx-config-test" } } // pragma: allowlist secret
|
||||
: provider === "grok"
|
||||
? { provider, grok: { apiKey: "xai-config-test" } }
|
||||
? { provider, grok: { apiKey: "xai-config-test" } } // pragma: allowlist secret
|
||||
: provider === "gemini"
|
||||
? { provider, gemini: { apiKey: "gemini-config-test" } }
|
||||
? { provider, gemini: { apiKey: "gemini-config-test" } } // pragma: allowlist secret
|
||||
: provider === "kimi"
|
||||
? { provider, kimi: { apiKey: "moonshot-config-test" } }
|
||||
: { provider, apiKey: "brave-config-test" };
|
||||
? { provider, kimi: { apiKey: "moonshot-config-test" } } // pragma: allowlist secret
|
||||
: { provider, apiKey: "brave-config-test" }; // pragma: allowlist secret
|
||||
return createWebSearchTool({
|
||||
config: {
|
||||
tools: {
|
||||
@@ -458,7 +458,7 @@ describe("web_search kimi provider", () => {
|
||||
global.fetch = withFetchPreconnect(mockFetch);
|
||||
|
||||
const tool = createKimiSearchTool({
|
||||
apiKey: "kimi-config-key",
|
||||
apiKey: "kimi-config-key", // pragma: allowlist secret
|
||||
baseUrl: "https://api.moonshot.ai/v1",
|
||||
model: "moonshot-v1-128k",
|
||||
});
|
||||
|
||||
@@ -29,6 +29,8 @@ function htmlResponse(html: string, url = "https://example.com/"): MockResponse
|
||||
};
|
||||
}
|
||||
|
||||
const apiKeyField = ["api", "Key"].join("");
|
||||
|
||||
function firecrawlResponse(markdown: string, url = "https://example.com/"): MockResponse {
|
||||
return {
|
||||
ok: true,
|
||||
@@ -130,8 +132,12 @@ function installPlainTextFetch(text: string) {
|
||||
);
|
||||
}
|
||||
|
||||
function createFirecrawlTool(apiKey = "firecrawl-test") {
|
||||
return createFetchTool({ firecrawl: { apiKey } });
|
||||
function createFirecrawlTool(apiKey = defaultFirecrawlApiKey()) {
|
||||
return createFetchTool({ firecrawl: { [apiKeyField]: apiKey } });
|
||||
}
|
||||
|
||||
function defaultFirecrawlApiKey() {
|
||||
return "firecrawl-test"; // pragma: allowlist secret
|
||||
}
|
||||
|
||||
async function executeFetch(
|
||||
@@ -385,7 +391,7 @@ describe("web_fetch extraction fallbacks", () => {
|
||||
});
|
||||
|
||||
const tool = createFetchTool({
|
||||
firecrawl: { apiKey: "firecrawl-test" },
|
||||
firecrawl: { apiKey: "firecrawl-test" }, // pragma: allowlist secret
|
||||
});
|
||||
|
||||
const result = await tool?.execute?.("call", { url: "https://example.com/blocked" });
|
||||
@@ -477,7 +483,7 @@ describe("web_fetch extraction fallbacks", () => {
|
||||
});
|
||||
|
||||
const tool = createFetchTool({
|
||||
firecrawl: { apiKey: "firecrawl-test" },
|
||||
firecrawl: { apiKey: "firecrawl-test" }, // pragma: allowlist secret
|
||||
});
|
||||
|
||||
const message = await captureToolErrorMessage({
|
||||
|
||||
Reference in New Issue
Block a user