diff --git a/.detect-secrets.cfg b/.detect-secrets.cfg index 38912567c9b..e40a4a1689e 100644 --- a/.detect-secrets.cfg +++ b/.detect-secrets.cfg @@ -7,10 +7,6 @@ [exclude-files] # pnpm lockfiles contain lots of high-entropy package integrity blobs. pattern = (^|/)pnpm-lock\.yaml$ -# Generated output and vendored assets. -pattern = (^|/)(dist|vendor)/ -# Local config file with allowlist patterns. -pattern = (^|/)\.detect-secrets\.cfg$ [exclude-lines] # Fastlane checks for private key marker; not a real key. @@ -28,3 +24,5 @@ pattern = "talk\.apiKey" pattern = === "string" # specific optional-chaining password check that didn't match the line above. pattern = typeof remote\?\.password === "string" +# Docker apt signing key fingerprint constant; not a secret. +pattern = OPENCLAW_DOCKER_GPG_FINGERPRINT= diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 30b6363a34d..296660d1014 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,7 +30,7 @@ repos: - --baseline - .secrets.baseline - --exclude-files - - '(^|/)(dist/|vendor/|pnpm-lock\.yaml$|\.detect-secrets\.cfg$)' + - '(^|/)pnpm-lock\.yaml$' - --exclude-lines - 'key_content\.include\?\("BEGIN PRIVATE KEY"\)' - --exclude-lines @@ -47,6 +47,8 @@ repos: - '=== "string"' - --exclude-lines - 'typeof remote\?\.password === "string"' + - --exclude-lines + - "OPENCLAW_DOCKER_GPG_FINGERPRINT=" # Shell script linting - repo: https://github.com/koalaman/shellcheck-precommit rev: v0.11.0 diff --git a/.secrets.baseline b/.secrets.baseline index 089515fe250..fbfbf368f21 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -141,7 +141,8 @@ "\"gateway\\.auth\\.password\"", "\"talk\\.apiKey\"", "=== \"string\"", - "typeof remote\\?\\.password === \"string\"" + "typeof remote\\?\\.password === \"string\"", + "OPENCLAW_DOCKER_GPG_FINGERPRINT=" ] } ], @@ -152,14 +153,14 @@ "filename": ".detect-secrets.cfg", "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", "is_verified": false, - "line_number": 17 + "line_number": 13 }, { "type": "Secret Keyword", "filename": ".detect-secrets.cfg", "hashed_secret": "fe88fceb47e040ba1bfafa4ac639366188df2f6d", "is_verified": false, - "line_number": 19 + "line_number": 15 } ], "appcast.xml": [ @@ -12387,21 +12388,14 @@ "filename": "src/config/schema.help.ts", "hashed_secret": "9f4cda226d3868676ac7f86f59e4190eb94bd208", "is_verified": false, - "line_number": 109 + "line_number": 647 }, { "type": "Secret Keyword", "filename": "src/config/schema.help.ts", "hashed_secret": "01822c8bbf6a8b136944b14182cb885100ec2eae", "is_verified": false, - "line_number": 130 - }, - { - "type": "Secret Keyword", - "filename": "src/config/schema.help.ts", - "hashed_secret": "bb7dfd9746e660e4a4374951ec5938ef0e343255", - "is_verified": false, - "line_number": 187 + "line_number": 678 } ], "src/config/schema.irc.ts": [ @@ -12720,21 +12714,21 @@ "filename": "src/infra/provider-usage.auth.normalizes-keys.test.ts", "hashed_secret": "45c7365e3b542cdb4fae6ec10c2ff149224d7656", "is_verified": false, - "line_number": 80 + "line_number": 123 }, { "type": "Secret Keyword", "filename": "src/infra/provider-usage.auth.normalizes-keys.test.ts", "hashed_secret": "b67074884ab7ef7c7a8cd6a3da9565d96c792248", "is_verified": false, - "line_number": 81 + "line_number": 124 }, { "type": "Secret Keyword", "filename": "src/infra/provider-usage.auth.normalizes-keys.test.ts", "hashed_secret": "d4d8027e64f9cf4180d3aecfe31ea409368022ee", "is_verified": false, - "line_number": 82 + "line_number": 125 } ], "src/infra/shell-env.test.ts": [ @@ -12900,7 +12894,7 @@ "filename": "src/media-understanding/runner.auto-audio.test.ts", "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", "is_verified": false, - "line_number": 40 + "line_number": 23 } ], "src/media-understanding/runner.deepgram.test.ts": [ @@ -12934,21 +12928,21 @@ "filename": "src/memory/embeddings.test.ts", "hashed_secret": "a47110e348a3063541fb1f1f640d635d457181a0", "is_verified": false, - "line_number": 45 + "line_number": 47 }, { "type": "Secret Keyword", "filename": "src/memory/embeddings.test.ts", "hashed_secret": "c734e47630dda71619c696d88381f06f7511bd78", "is_verified": false, - "line_number": 160 + "line_number": 195 }, { "type": "Secret Keyword", "filename": "src/memory/embeddings.test.ts", "hashed_secret": "56e1d57b8db262b08bc73c60ed08d8c92e59503f", "is_verified": false, - "line_number": 189 + "line_number": 291 } ], "src/pairing/pairing-store.ts": [ @@ -13060,7 +13054,7 @@ "filename": "src/tui/gateway-chat.test.ts", "hashed_secret": "6255675480f681df08c1704b7b3cd2c49917f0e2", "is_verified": false, - "line_number": 85 + "line_number": 60 } ], "src/web/login.test.ts": [ @@ -13100,5 +13094,5 @@ } ] }, - "generated_at": "2026-02-17T13:34:38Z" + "generated_at": "2026-03-07T00:11:03Z" } diff --git a/appcast.xml b/appcast.xml index 22e4df0b698..f1e626843dc 100644 --- a/appcast.xml +++ b/appcast.xml @@ -219,7 +219,7 @@

View full changelog

]]> - + 2026.3.1 @@ -357,7 +357,7 @@

View full changelog

]]> - +
- \ No newline at end of file + diff --git a/apps/android/app/src/test/java/ai/openclaw/android/voice/TalkModeConfigParsingTest.kt b/apps/android/app/src/test/java/ai/openclaw/android/voice/TalkModeConfigParsingTest.kt index 5daa62080d7..dbc40f3c22b 100644 --- a/apps/android/app/src/test/java/ai/openclaw/android/voice/TalkModeConfigParsingTest.kt +++ b/apps/android/app/src/test/java/ai/openclaw/android/voice/TalkModeConfigParsingTest.kt @@ -1,8 +1,10 @@ package ai.openclaw.android.voice import kotlinx.serialization.json.Json +import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.put import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue @@ -38,16 +40,12 @@ class TalkModeConfigParsingTest { @Test fun fallsBackToLegacyTalkFieldsWhenNormalizedPayloadMissing() { + val legacyApiKey = "legacy-key" // pragma: allowlist secret val talk = - json.parseToJsonElement( - """ - { - "voiceId": "voice-legacy", - "apiKey": "legacy-key" - } - """.trimIndent(), - ) - .jsonObject + buildJsonObject { + put("voiceId", "voice-legacy") + put("apiKey", legacyApiKey) // pragma: allowlist secret + } val selection = TalkModeManager.selectTalkProviderConfig(talk) assertNotNull(selection) diff --git a/apps/ios/Sources/Gateway/GatewaySettingsStore.swift b/apps/ios/Sources/Gateway/GatewaySettingsStore.swift index e467659a451..d91d2217741 100644 --- a/apps/ios/Sources/Gateway/GatewaySettingsStore.swift +++ b/apps/ios/Sources/Gateway/GatewaySettingsStore.swift @@ -26,7 +26,7 @@ enum GatewaySettingsStore { private static let preferredGatewayStableIDAccount = "preferredStableID" private static let lastDiscoveredGatewayStableIDAccount = "lastDiscoveredStableID" private static let lastGatewayConnectionAccount = "lastConnection" - private static let talkProviderApiKeyAccountPrefix = "provider.apiKey." + private static let talkProviderApiKeyAccountPrefix = "provider.apiKey." // pragma: allowlist secret static func bootstrapPersistence() { self.ensureStableInstanceID() diff --git a/apps/ios/Tests/TalkModeConfigParsingTests.swift b/apps/ios/Tests/TalkModeConfigParsingTests.swift index a09f095a233..dc4a29548e0 100644 --- a/apps/ios/Tests/TalkModeConfigParsingTests.swift +++ b/apps/ios/Tests/TalkModeConfigParsingTests.swift @@ -23,7 +23,7 @@ import Testing @Test func ignoresLegacyTalkFieldsWhenNormalizedPayloadMissing() { let talk: [String: Any] = [ "voiceId": "voice-legacy", - "apiKey": "legacy-key", + "apiKey": "legacy-key", // pragma: allowlist secret ] let selection = TalkModeManager.selectTalkProviderConfig(talk) diff --git a/docs/channels/telegram.md b/docs/channels/telegram.md index e975db4c357..e50590c8427 100644 --- a/docs/channels/telegram.md +++ b/docs/channels/telegram.md @@ -804,7 +804,7 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ ```yaml channels: telegram: - proxy: socks5://user:pass@proxy-host:1080 + proxy: socks5://:@proxy-host:1080 ``` - Node 22+ defaults to `autoSelectFamily=true` (except WSL2) and `dnsResultOrder=ipv4first`. diff --git a/docs/gateway/secrets.md b/docs/gateway/secrets.md index db4be160cd7..2956d53133e 100644 --- a/docs/gateway/secrets.md +++ b/docs/gateway/secrets.md @@ -179,8 +179,8 @@ Request payload (stdin): Response payload (stdout): -```json -{ "protocolVersion": 1, "values": { "providers/openai/apiKey": "sk-..." } } +```jsonc +{ "protocolVersion": 1, "values": { "providers/openai/apiKey": "" } } // pragma: allowlist secret ``` Optional per-id errors: diff --git a/docs/platforms/raspberry-pi.md b/docs/platforms/raspberry-pi.md index 79c9c34fd0d..e46076e869d 100644 --- a/docs/platforms/raspberry-pi.md +++ b/docs/platforms/raspberry-pi.md @@ -197,7 +197,7 @@ See [Pi USB boot guide](https://www.raspberrypi.com/documentation/computers/rasp On lower-power Pi hosts, enable Node's module compile cache so repeated CLI runs are faster: ```bash -grep -q 'NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache' ~/.bashrc || cat >> ~/.bashrc <<'EOF' +grep -q 'NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache' ~/.bashrc || cat >> ~/.bashrc <<'EOF' # pragma: allowlist secret export NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache mkdir -p /var/tmp/openclaw-compile-cache export OPENCLAW_NO_RESPAWN=1 diff --git a/docs/providers/kilocode.md b/docs/providers/kilocode.md index 146e22932c4..009f4d83812 100644 --- a/docs/providers/kilocode.md +++ b/docs/providers/kilocode.md @@ -25,14 +25,14 @@ openclaw onboard --kilocode-api-key Or set the environment variable: ```bash -export KILOCODE_API_KEY="your-api-key" +export KILOCODE_API_KEY="" # pragma: allowlist secret ``` ## Config snippet ```json5 { - env: { KILOCODE_API_KEY: "sk-..." }, + env: { KILOCODE_API_KEY: "" }, // pragma: allowlist secret agents: { defaults: { model: { primary: "kilocode/anthropic/claude-opus-4.6" }, diff --git a/extensions/bluebubbles/src/config-schema.test.ts b/extensions/bluebubbles/src/config-schema.test.ts index 5bf66704d35..308ee9732b5 100644 --- a/extensions/bluebubbles/src/config-schema.test.ts +++ b/extensions/bluebubbles/src/config-schema.test.ts @@ -5,7 +5,7 @@ describe("BlueBubblesConfigSchema", () => { it("accepts account config when serverUrl and password are both set", () => { const parsed = BlueBubblesConfigSchema.safeParse({ serverUrl: "http://localhost:1234", - password: "secret", + password: "secret", // pragma: allowlist secret }); expect(parsed.success).toBe(true); }); diff --git a/extensions/feishu/src/client.test.ts b/extensions/feishu/src/client.test.ts index 00c4d0aafd8..a5855fa0745 100644 --- a/extensions/feishu/src/client.test.ts +++ b/extensions/feishu/src/client.test.ts @@ -192,7 +192,7 @@ describe("createFeishuClient HTTP timeout", () => { ); }); - it("uses env timeout override when provided", async () => { + it("uses env timeout override when provided and no direct timeout is set", async () => { process.env[FEISHU_HTTP_TIMEOUT_ENV_VAR] = "60000"; createFeishuClient({ @@ -214,6 +214,29 @@ describe("createFeishuClient HTTP timeout", () => { ); }); + it("prefers direct timeout over env override", async () => { + process.env[FEISHU_HTTP_TIMEOUT_ENV_VAR] = "60000"; + + createFeishuClient({ + appId: "app_10", + appSecret: "secret_10", + accountId: "timeout-direct-override", + httpTimeoutMs: 120_000, + config: { httpTimeoutMs: 45_000 }, + }); + + const calls = (LarkClient as unknown as ReturnType).mock.calls; + const lastCall = calls[calls.length - 1][0] as { + httpInstance: { get: (...args: unknown[]) => Promise }; + }; + await lastCall.httpInstance.get("https://example.com/api"); + + expect(mockBaseHttpInstance.get).toHaveBeenCalledWith( + "https://example.com/api", + expect.objectContaining({ timeout: 120_000 }), + ); + }); + it("clamps env timeout override to max bound", async () => { process.env[FEISHU_HTTP_TIMEOUT_ENV_VAR] = String(FEISHU_HTTP_TIMEOUT_MAX_MS + 123_456); diff --git a/extensions/feishu/src/client.ts b/extensions/feishu/src/client.ts index 26da3c9bfdd..d9fdde7f059 100644 --- a/extensions/feishu/src/client.ts +++ b/extensions/feishu/src/client.ts @@ -79,6 +79,15 @@ function resolveConfiguredHttpTimeoutMs(creds: FeishuClientCredentials): number return Math.min(Math.max(rounded, 1), FEISHU_HTTP_TIMEOUT_MAX_MS); }; + const fromDirectField = creds.httpTimeoutMs; + if ( + typeof fromDirectField === "number" && + Number.isFinite(fromDirectField) && + fromDirectField > 0 + ) { + return clampTimeout(fromDirectField); + } + const envRaw = process.env[FEISHU_HTTP_TIMEOUT_ENV_VAR]; if (envRaw) { const envValue = Number(envRaw); @@ -88,8 +97,7 @@ function resolveConfiguredHttpTimeoutMs(creds: FeishuClientCredentials): number } const fromConfig = creds.config?.httpTimeoutMs; - const fromDirectField = creds.httpTimeoutMs; - const timeout = fromDirectField ?? fromConfig; + const timeout = fromConfig; if (typeof timeout !== "number" || !Number.isFinite(timeout) || timeout <= 0) { return FEISHU_HTTP_TIMEOUT_MS; } diff --git a/extensions/feishu/src/monitor.reaction.test.ts b/extensions/feishu/src/monitor.reaction.test.ts index f69ac647376..06eb0e37a97 100644 --- a/extensions/feishu/src/monitor.reaction.test.ts +++ b/extensions/feishu/src/monitor.reaction.test.ts @@ -77,7 +77,7 @@ function buildDebounceAccount(): ResolvedFeishuAccount { enabled: true, configured: true, appId: "cli_test", - appSecret: "secret_test", + appSecret: "secret_test", // pragma: allowlist secret domain: "feishu", config: { enabled: true, diff --git a/extensions/feishu/src/monitor.startup.test.ts b/extensions/feishu/src/monitor.startup.test.ts index 29b00fab200..7e1c2c60e5d 100644 --- a/extensions/feishu/src/monitor.startup.test.ts +++ b/extensions/feishu/src/monitor.startup.test.ts @@ -41,7 +41,7 @@ function buildMultiAccountWebsocketConfig(accountIds: string[]): ClawdbotConfig { enabled: true, appId: `cli_${accountId}`, - appSecret: `secret_${accountId}`, + appSecret: `secret_${accountId}`, // pragma: allowlist secret connectionMode: "websocket", }, ]), diff --git a/extensions/nextcloud-talk/src/inbound.authz.test.ts b/extensions/nextcloud-talk/src/inbound.authz.test.ts index 188820eeb6d..f19fa73e020 100644 --- a/extensions/nextcloud-talk/src/inbound.authz.test.ts +++ b/extensions/nextcloud-talk/src/inbound.authz.test.ts @@ -45,7 +45,7 @@ describe("nextcloud-talk inbound authz", () => { enabled: true, baseUrl: "", secret: "", - secretSource: "none", + secretSource: "none", // pragma: allowlist secret config: { dmPolicy: "pairing", allowFrom: [], diff --git a/extensions/nextcloud-talk/src/monitor.test-fixtures.ts b/extensions/nextcloud-talk/src/monitor.test-fixtures.ts index 21d41976c98..1a65a1b25e6 100644 --- a/extensions/nextcloud-talk/src/monitor.test-fixtures.ts +++ b/extensions/nextcloud-talk/src/monitor.test-fixtures.ts @@ -16,7 +16,7 @@ export function createSignedCreateMessageRequest(params?: { backend?: string }) const body = JSON.stringify(payload); const { random, signature } = generateNextcloudTalkSignature({ body, - secret: "nextcloud-secret", + secret: "nextcloud-secret", // pragma: allowlist secret }); return { body, diff --git a/src/agents/auth-profiles.ensureauthprofilestore.test.ts b/src/agents/auth-profiles.ensureauthprofilestore.test.ts index 537cb9512d4..10655a9f502 100644 --- a/src/agents/auth-profiles.ensureauthprofilestore.test.ts +++ b/src/agents/auth-profiles.ensureauthprofilestore.test.ts @@ -130,7 +130,7 @@ describe("ensureAuthProfileStore", () => { profile: { provider: "anthropic", mode: "api_key", - apiKey: "sk-ant-alias", + apiKey: "sk-ant-alias", // pragma: allowlist secret }, expected: { type: "api_key", @@ -156,7 +156,7 @@ describe("ensureAuthProfileStore", () => { provider: "anthropic", type: "api_key", key: "sk-ant-canonical", - apiKey: "sk-ant-alias", + apiKey: "sk-ant-alias", // pragma: allowlist secret }, expected: { type: "api_key", @@ -210,7 +210,7 @@ describe("ensureAuthProfileStore", () => { anthropic: { provider: "anthropic", mode: "api_key", - apiKey: "sk-ant-legacy", + apiKey: "sk-ant-legacy", // pragma: allowlist secret }, }, null, diff --git a/src/agents/auth-profiles.runtime-snapshot-save.test.ts b/src/agents/auth-profiles.runtime-snapshot-save.test.ts index 3cb3d238975..d9146a7b1ee 100644 --- a/src/agents/auth-profiles.runtime-snapshot-save.test.ts +++ b/src/agents/auth-profiles.runtime-snapshot-save.test.ts @@ -37,7 +37,7 @@ describe("auth profile runtime snapshot persistence", () => { const snapshot = await prepareSecretsRuntimeSnapshot({ config: {}, - env: { OPENAI_API_KEY: "sk-runtime-openai" }, + env: { OPENAI_API_KEY: "sk-runtime-openai" }, // pragma: allowlist secret agentDirs: [agentDir], }); activateSecretsRuntimeSnapshot(snapshot); diff --git a/src/agents/auth-profiles/oauth.test.ts b/src/agents/auth-profiles/oauth.test.ts index f5c29fe3c2a..05ccdb5af04 100644 --- a/src/agents/auth-profiles/oauth.test.ts +++ b/src/agents/auth-profiles/oauth.test.ts @@ -65,7 +65,7 @@ describe("resolveApiKeyForProfile config compatibility", () => { profileId, }); expect(result).toEqual({ - apiKey: "tok-123", + apiKey: "tok-123", // pragma: allowlist secret provider: "anthropic", email: undefined, }); @@ -124,7 +124,7 @@ describe("resolveApiKeyForProfile config compatibility", () => { }); // token ↔ oauth are bidirectionally compatible bearer-token auth paths. expect(result).toEqual({ - apiKey: "access-123", + apiKey: "access-123", // pragma: allowlist secret provider: "anthropic", email: undefined, }); @@ -145,7 +145,7 @@ describe("resolveApiKeyForProfile token expiry handling", () => { }), }); expect(result).toEqual({ - apiKey: "tok-123", + apiKey: "tok-123", // pragma: allowlist secret provider: "anthropic", email: undefined, }); @@ -165,7 +165,7 @@ describe("resolveApiKeyForProfile token expiry handling", () => { }), }); expect(result).toEqual({ - apiKey: "tok-123", + apiKey: "tok-123", // pragma: allowlist secret provider: "anthropic", email: undefined, }); @@ -231,7 +231,7 @@ describe("resolveApiKeyForProfile secret refs", () => { it("resolves api_key keyRef from env", async () => { const profileId = "openai:default"; const previous = process.env.OPENAI_API_KEY; - process.env.OPENAI_API_KEY = "sk-openai-ref"; + process.env.OPENAI_API_KEY = "sk-openai-ref"; // pragma: allowlist secret try { const result = await resolveApiKeyForProfile({ cfg: cfgFor(profileId, "openai", "api_key"), @@ -248,7 +248,7 @@ describe("resolveApiKeyForProfile secret refs", () => { profileId, }); expect(result).toEqual({ - apiKey: "sk-openai-ref", + apiKey: "sk-openai-ref", // pragma: allowlist secret provider: "openai", email: undefined, }); @@ -282,7 +282,7 @@ describe("resolveApiKeyForProfile secret refs", () => { profileId, }); expect(result).toEqual({ - apiKey: "gh-ref-token", + apiKey: "gh-ref-token", // pragma: allowlist secret provider: "github-copilot", email: undefined, }); @@ -315,7 +315,7 @@ describe("resolveApiKeyForProfile secret refs", () => { profileId, }); expect(result).toEqual({ - apiKey: "gh-ref-token", + apiKey: "gh-ref-token", // pragma: allowlist secret provider: "github-copilot", email: undefined, }); @@ -331,7 +331,7 @@ describe("resolveApiKeyForProfile secret refs", () => { it("resolves inline ${ENV} api_key values", async () => { const profileId = "openai:inline-env"; const previous = process.env.OPENAI_API_KEY; - process.env.OPENAI_API_KEY = "sk-openai-inline"; + process.env.OPENAI_API_KEY = "sk-openai-inline"; // pragma: allowlist secret try { const result = await resolveApiKeyForProfile({ cfg: cfgFor(profileId, "openai", "api_key"), @@ -348,7 +348,7 @@ describe("resolveApiKeyForProfile secret refs", () => { profileId, }); expect(result).toEqual({ - apiKey: "sk-openai-inline", + apiKey: "sk-openai-inline", // pragma: allowlist secret provider: "openai", email: undefined, }); @@ -381,7 +381,7 @@ describe("resolveApiKeyForProfile secret refs", () => { profileId, }); expect(result).toEqual({ - apiKey: "gh-inline-token", + apiKey: "gh-inline-token", // pragma: allowlist secret provider: "github-copilot", email: undefined, }); diff --git a/src/agents/compaction.identifier-preservation.test.ts b/src/agents/compaction.identifier-preservation.test.ts index cdf742e1489..139c4923b27 100644 --- a/src/agents/compaction.identifier-preservation.test.ts +++ b/src/agents/compaction.identifier-preservation.test.ts @@ -31,7 +31,7 @@ describe("compaction identifier-preservation instructions", () => { } as unknown as NonNullable; const summarizeBase: Omit = { model: testModel, - apiKey: "test-key", + apiKey: "test-key", // pragma: allowlist secret reserveTokens: 4000, maxChunkTokens: 8000, contextWindow: 200_000, diff --git a/src/agents/model-auth.test.ts b/src/agents/model-auth.test.ts index 86bc6bba5a0..943070960d3 100644 --- a/src/agents/model-auth.test.ts +++ b/src/agents/model-auth.test.ts @@ -7,7 +7,7 @@ describe("resolveAwsSdkEnvVarName", () => { const env = { AWS_BEARER_TOKEN_BEDROCK: "bearer", AWS_ACCESS_KEY_ID: "access", - AWS_SECRET_ACCESS_KEY: "secret", + AWS_SECRET_ACCESS_KEY: "secret", // pragma: allowlist secret AWS_PROFILE: "default", } as NodeJS.ProcessEnv; @@ -17,7 +17,7 @@ describe("resolveAwsSdkEnvVarName", () => { it("uses access keys when bearer token is missing", () => { const env = { AWS_ACCESS_KEY_ID: "access", - AWS_SECRET_ACCESS_KEY: "secret", + AWS_SECRET_ACCESS_KEY: "secret", // pragma: allowlist secret AWS_PROFILE: "default", } as NodeJS.ProcessEnv; diff --git a/src/agents/model-auth.ts b/src/agents/model-auth.ts index 734cd7b2666..68a117c96a9 100644 --- a/src/agents/model-auth.ts +++ b/src/agents/model-auth.ts @@ -90,7 +90,7 @@ function resolveSyntheticLocalProviderAuth(params: { } return { - apiKey: "ollama-local", + apiKey: "ollama-local", // pragma: allowlist secret source: "models.providers.ollama (synthetic local key)", mode: "api-key", }; diff --git a/src/agents/pi-model-discovery.ts b/src/agents/pi-model-discovery.ts index c283a653310..6ed1fc0b338 100644 --- a/src/agents/pi-model-discovery.ts +++ b/src/agents/pi-model-discovery.ts @@ -119,9 +119,10 @@ function createAuthStorage(AuthStorageLike: unknown, path: string, creds: PiCred ? withFactory.create(path) : new (AuthStorageLike as { new (path: string): unknown })(path) ) as PiAuthStorage & { - setRuntimeApiKey?: (provider: string, apiKey: string) => void; + setRuntimeApiKey?: (provider: string, apiKey: string) => void; // pragma: allowlist secret }; - if (typeof withRuntimeOverride.setRuntimeApiKey === "function") { + const hasRuntimeApiKeyOverride = typeof withRuntimeOverride.setRuntimeApiKey === "function"; // pragma: allowlist secret + if (hasRuntimeApiKeyOverride) { for (const [provider, credential] of Object.entries(creds)) { if (credential.type === "api_key") { withRuntimeOverride.setRuntimeApiKey(provider, credential.key); diff --git a/src/agents/sandbox/novnc-auth.ts b/src/agents/sandbox/novnc-auth.ts index ef1e78334b0..ee46617a840 100644 --- a/src/agents/sandbox/novnc-auth.ts +++ b/src/agents/sandbox/novnc-auth.ts @@ -1,6 +1,6 @@ import crypto from "node:crypto"; -export const NOVNC_PASSWORD_ENV_KEY = "OPENCLAW_BROWSER_NOVNC_PASSWORD"; +export const NOVNC_PASSWORD_ENV_KEY = "OPENCLAW_BROWSER_NOVNC_PASSWORD"; // pragma: allowlist secret const NOVNC_TOKEN_TTL_MS = 60 * 1000; const NOVNC_PASSWORD_LENGTH = 8; const NOVNC_PASSWORD_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; diff --git a/src/cli/command-secret-gateway.ts b/src/cli/command-secret-gateway.ts index dfbb425a49d..b1eb174a512 100644 --- a/src/cli/command-secret-gateway.ts +++ b/src/cli/command-secret-gateway.ts @@ -25,7 +25,7 @@ type ResolveCommandSecretsResult = { hadUnresolvedTargets: boolean; }; -export type CommandSecretResolutionMode = "strict" | "summary" | "operational_readonly"; +export type CommandSecretResolutionMode = "strict" | "summary" | "operational_readonly"; // pragma: allowlist secret export type CommandSecretTargetState = | "resolved_gateway" diff --git a/src/commands/auth-choice.apply-helpers.ts b/src/commands/auth-choice.apply-helpers.ts index f753aa557bf..122be392153 100644 --- a/src/commands/auth-choice.apply-helpers.ts +++ b/src/commands/auth-choice.apply-helpers.ts @@ -20,7 +20,7 @@ import type { SecretInputMode } from "./onboard-types.js"; const ENV_SOURCE_LABEL_RE = /(?:^|:\s)([A-Z][A-Z0-9_]*)$/; -type SecretRefChoice = "env" | "provider"; +type SecretRefChoice = "env" | "provider"; // pragma: allowlist secret export type SecretInputModePromptCopy = { modeMessage?: string; @@ -101,7 +101,7 @@ export async function promptSecretRefForOnboarding(params: { const defaultEnvVar = params.preferredEnvVar ?? resolveDefaultProviderEnvVar(params.provider) ?? ""; const defaultFilePointer = resolveDefaultFilePointerId(params.provider); - let sourceChoice: SecretRefChoice = "env"; + let sourceChoice: SecretRefChoice = "env"; // pragma: allowlist secret while (true) { const sourceRaw: SecretRefChoice = await params.prompter.select({ diff --git a/src/commands/models/list.status.test.ts b/src/commands/models/list.status.test.ts index a2563b09f08..7a792ac042d 100644 --- a/src/commands/models/list.status.test.ts +++ b/src/commands/models/list.status.test.ts @@ -9,14 +9,14 @@ const mocks = vi.hoisted(() => { type: "oauth", provider: "anthropic", access: "sk-ant-oat01-ACCESS-TOKEN-1234567890", - refresh: "sk-ant-ort01-REFRESH-TOKEN-1234567890", + refresh: "sk-ant-ort01-REFRESH-TOKEN-1234567890", // pragma: allowlist secret expires: Date.now() + 60_000, email: "peter@example.com", }, "anthropic:work": { type: "api_key", provider: "anthropic", - key: "sk-ant-api-0123456789abcdefghijklmnopqrstuvwxyz", + key: "sk-ant-api-0123456789abcdefghijklmnopqrstuvwxyz", // pragma: allowlist secret }, "openai-codex:default": { type: "oauth", @@ -49,13 +49,13 @@ const mocks = vi.hoisted(() => { resolveEnvApiKey: vi.fn((provider: string) => { if (provider === "openai") { return { - apiKey: "sk-openai-0123456789abcdefghijklmnopqrstuvwxyz", + apiKey: "sk-openai-0123456789abcdefghijklmnopqrstuvwxyz", // pragma: allowlist secret source: "shell env: OPENAI_API_KEY", }; } if (provider === "anthropic") { return { - apiKey: "sk-ant-oat01-ACCESS-TOKEN-1234567890", + apiKey: "sk-ant-oat01-ACCESS-TOKEN-1234567890", // pragma: allowlist secret source: "env: ANTHROPIC_OAUTH_TOKEN", }; } @@ -231,7 +231,7 @@ describe("modelsStatusCommand auth overview", () => { it("does not emit raw short api-key values in JSON labels", async () => { const localRuntime = createRuntime(); - const shortSecret = "abc123"; + const shortSecret = "abc123"; // pragma: allowlist secret const originalProfiles = { ...mocks.store.profiles }; mocks.store.profiles = { ...mocks.store.profiles, diff --git a/src/commands/onboard-auth.credentials.ts b/src/commands/onboard-auth.credentials.ts index 2cf9c25b689..c32a3ea9ae6 100644 --- a/src/commands/onboard-auth.credentials.ts +++ b/src/commands/onboard-auth.credentials.ts @@ -63,7 +63,8 @@ function resolveApiKeySecretInput( if (inlineEnvRef) { return inlineEnvRef; } - if (options?.secretInputMode === "ref") { + const useSecretRefMode = options?.secretInputMode === "ref"; // pragma: allowlist secret + if (useSecretRefMode) { return resolveProviderDefaultEnvSecretRef(provider); } return normalized; diff --git a/src/commands/onboard-custom.test.ts b/src/commands/onboard-custom.test.ts index 374f188dc62..b04f7bc08ab 100644 --- a/src/commands/onboard-custom.test.ts +++ b/src/commands/onboard-custom.test.ts @@ -429,7 +429,7 @@ describe("parseNonInteractiveCustomApiFlags", () => { baseUrl: "https://llm.example.com/v1", modelId: "foo-large", compatibility: "openai", - apiKey: "custom-test-key", + apiKey: "custom-test-key", // pragma: allowlist secret providerId: "my-custom", }); }); diff --git a/src/commands/onboard-non-interactive.provider-auth.test.ts b/src/commands/onboard-non-interactive.provider-auth.test.ts index 077b2c6d672..390d19b0154 100644 --- a/src/commands/onboard-non-interactive.provider-auth.test.ts +++ b/src/commands/onboard-non-interactive.provider-auth.test.ts @@ -184,7 +184,7 @@ describe("onboard (non-interactive): provider auth", () => { await withOnboardEnv("openclaw-onboard-minimax-", async (env) => { const cfg = await runOnboardingAndReadConfig(env, { authChoice: "minimax-api", - minimaxApiKey: "sk-minimax-test", + minimaxApiKey: "sk-minimax-test", // pragma: allowlist secret }); expect(cfg.auth?.profiles?.["minimax:default"]?.provider).toBe("minimax"); @@ -203,7 +203,7 @@ describe("onboard (non-interactive): provider auth", () => { await withOnboardEnv("openclaw-onboard-minimax-cn-", async (env) => { const cfg = await runOnboardingAndReadConfig(env, { authChoice: "minimax-api-key-cn", - minimaxApiKey: "sk-minimax-test", + minimaxApiKey: "sk-minimax-test", // pragma: allowlist secret }); expect(cfg.auth?.profiles?.["minimax-cn:default"]?.provider).toBe("minimax-cn"); @@ -222,7 +222,7 @@ describe("onboard (non-interactive): provider auth", () => { await withOnboardEnv("openclaw-onboard-zai-", async (env) => { const cfg = await runOnboardingAndReadConfig(env, { authChoice: "zai-api-key", - zaiApiKey: "zai-test-key", + zaiApiKey: "zai-test-key", // pragma: allowlist secret }); expect(cfg.auth?.profiles?.["zai:default"]?.provider).toBe("zai"); @@ -237,7 +237,7 @@ describe("onboard (non-interactive): provider auth", () => { await withOnboardEnv("openclaw-onboard-zai-cn-", async (env) => { const cfg = await runOnboardingAndReadConfig(env, { authChoice: "zai-coding-cn", - zaiApiKey: "zai-test-key", + zaiApiKey: "zai-test-key", // pragma: allowlist secret }); expect(cfg.models?.providers?.zai?.baseUrl).toBe( @@ -264,7 +264,7 @@ describe("onboard (non-interactive): provider auth", () => { it("infers Mistral auth choice from --mistral-api-key and sets default model", async () => { await withOnboardEnv("openclaw-onboard-mistral-infer-", async (env) => { const cfg = await runOnboardingAndReadConfig(env, { - mistralApiKey: "mistral-test-key", + mistralApiKey: "mistral-test-key", // pragma: allowlist secret }); expect(cfg.auth?.profiles?.["mistral:default"]?.provider).toBe("mistral"); @@ -282,7 +282,7 @@ describe("onboard (non-interactive): provider auth", () => { await withOnboardEnv("openclaw-onboard-volcengine-", async (env) => { const cfg = await runOnboardingAndReadConfig(env, { authChoice: "volcengine-api-key", - volcengineApiKey: "volcengine-test-key", + volcengineApiKey: "volcengine-test-key", // pragma: allowlist secret }); expect(cfg.agents?.defaults?.model?.primary).toBe("volcengine-plan/ark-code-latest"); @@ -292,7 +292,7 @@ describe("onboard (non-interactive): provider auth", () => { it("infers BytePlus auth choice from --byteplus-api-key and sets default model", async () => { await withOnboardEnv("openclaw-onboard-byteplus-infer-", async (env) => { const cfg = await runOnboardingAndReadConfig(env, { - byteplusApiKey: "byteplus-test-key", + byteplusApiKey: "byteplus-test-key", // pragma: allowlist secret }); expect(cfg.agents?.defaults?.model?.primary).toBe("byteplus-plan/ark-code-latest"); @@ -303,7 +303,7 @@ describe("onboard (non-interactive): provider auth", () => { await withOnboardEnv("openclaw-onboard-ai-gateway-", async (env) => { const cfg = await runOnboardingAndReadConfig(env, { authChoice: "ai-gateway-api-key", - aiGatewayApiKey: "gateway-test-key", + aiGatewayApiKey: "gateway-test-key", // pragma: allowlist secret }); expect(cfg.auth?.profiles?.["vercel-ai-gateway:default"]?.provider).toBe("vercel-ai-gateway"); @@ -350,7 +350,7 @@ describe("onboard (non-interactive): provider auth", () => { await withOnboardEnv("openclaw-onboard-openai-", async (env) => { const cfg = await runOnboardingAndReadConfig(env, { authChoice: "openai-api-key", - openaiApiKey: "sk-openai-test", + openaiApiKey: "sk-openai-test", // pragma: allowlist secret }); expect(cfg.agents?.defaults?.model?.primary).toBe(OPENAI_DEFAULT_MODEL); @@ -410,10 +410,10 @@ describe("onboard (non-interactive): provider auth", () => { "fails fast for $name when --secret-input-mode ref uses explicit key without env and does not leak the key", async ({ prefix, authChoice, optionKey, flagName, envVar }) => { await withOnboardEnv(prefix, async ({ runtime }) => { - const providedSecret = `${envVar.toLowerCase()}-should-not-leak`; + const providedSecret = `${envVar.toLowerCase()}-should-not-leak`; // pragma: allowlist secret const options: Record = { authChoice, - secretInputMode: "ref", + secretInputMode: "ref", // pragma: allowlist secret [optionKey]: providedSecret, skipSkills: true, }; @@ -447,12 +447,12 @@ describe("onboard (non-interactive): provider auth", () => { await withEnvAsync( { OPENCODE_API_KEY: undefined, - OPENCODE_ZEN_API_KEY: "opencode-zen-env-key", + OPENCODE_ZEN_API_KEY: "opencode-zen-env-key", // pragma: allowlist secret }, async () => { await runNonInteractiveOnboardingWithDefaults(runtime, { authChoice: "opencode-zen", - secretInputMode: "ref", + secretInputMode: "ref", // pragma: allowlist secret skipSkills: true, }); @@ -487,7 +487,7 @@ describe("onboard (non-interactive): provider auth", () => { await withOnboardEnv("openclaw-onboard-litellm-", async (env) => { const cfg = await runOnboardingAndReadConfig(env, { authChoice: "litellm-api-key", - litellmApiKey: "litellm-test-key", + litellmApiKey: "litellm-test-key", // pragma: allowlist secret }); expect(cfg.auth?.profiles?.["litellm:default"]?.provider).toBe("litellm"); @@ -519,7 +519,7 @@ describe("onboard (non-interactive): provider auth", () => { await runNonInteractiveOnboardingWithDefaults(runtime, { cloudflareAiGatewayAccountId: "cf-account-id", cloudflareAiGatewayGatewayId: "cf-gateway-id", - cloudflareAiGatewayApiKey: "cf-gateway-test-key", + cloudflareAiGatewayApiKey: "cf-gateway-test-key", // pragma: allowlist secret skipSkills: true, ...options, }); @@ -543,7 +543,7 @@ describe("onboard (non-interactive): provider auth", () => { it("infers Together auth choice from --together-api-key and sets default model", async () => { await withOnboardEnv("openclaw-onboard-together-infer-", async (env) => { const cfg = await runOnboardingAndReadConfig(env, { - togetherApiKey: "together-test-key", + togetherApiKey: "together-test-key", // pragma: allowlist secret }); expect(cfg.auth?.profiles?.["together:default"]?.provider).toBe("together"); @@ -560,7 +560,7 @@ describe("onboard (non-interactive): provider auth", () => { it("infers QIANFAN auth choice from --qianfan-api-key and sets default model", async () => { await withOnboardEnv("openclaw-onboard-qianfan-infer-", async (env) => { const cfg = await runOnboardingAndReadConfig(env, { - qianfanApiKey: "qianfan-test-key", + qianfanApiKey: "qianfan-test-key", // pragma: allowlist secret }); expect(cfg.auth?.profiles?.["qianfan:default"]?.provider).toBe("qianfan"); @@ -579,7 +579,7 @@ describe("onboard (non-interactive): provider auth", () => { await runNonInteractiveOnboardingWithDefaults(runtime, { authChoice: "custom-api-key", customBaseUrl: "https://llm.example.com/v1", - customApiKey: "custom-test-key", + customApiKey: "custom-test-key", // pragma: allowlist secret customModelId: "foo-large", customCompatibility: "anthropic", skipSkills: true, @@ -603,7 +603,7 @@ describe("onboard (non-interactive): provider auth", () => { await runNonInteractiveOnboardingWithDefaults(runtime, { customBaseUrl: "https://models.custom.local/v1", customModelId: "local-large", - customApiKey: "custom-test-key", + customApiKey: "custom-test-key", // pragma: allowlist secret skipSkills: true, }); @@ -624,7 +624,7 @@ describe("onboard (non-interactive): provider auth", () => { await withOnboardEnv( "openclaw-onboard-custom-provider-env-fallback-", async ({ configPath, runtime }) => { - process.env.CUSTOM_API_KEY = "custom-env-key"; + process.env.CUSTOM_API_KEY = "custom-env-key"; // pragma: allowlist secret await runCustomLocalNonInteractive(runtime); expect(await readCustomLocalProviderApiKey(configPath)).toBe("custom-env-key"); }, @@ -635,9 +635,9 @@ describe("onboard (non-interactive): provider auth", () => { await withOnboardEnv( "openclaw-onboard-custom-provider-env-ref-", async ({ configPath, runtime }) => { - process.env.CUSTOM_API_KEY = "custom-env-key"; + process.env.CUSTOM_API_KEY = "custom-env-key"; // pragma: allowlist secret await runCustomLocalNonInteractive(runtime, { - secretInputMode: "ref", + secretInputMode: "ref", // pragma: allowlist secret }); expect(await readCustomLocalProviderApiKeyInput(configPath)).toEqual({ source: "env", @@ -650,12 +650,12 @@ describe("onboard (non-interactive): provider auth", () => { it("fails fast for custom provider ref mode when --custom-api-key is set but CUSTOM_API_KEY env is missing", async () => { await withOnboardEnv("openclaw-onboard-custom-provider-ref-flag-", async ({ runtime }) => { - const providedSecret = "custom-inline-key-should-not-leak"; + const providedSecret = "custom-inline-key-should-not-leak"; // pragma: allowlist secret await withEnvAsync({ CUSTOM_API_KEY: undefined }, async () => { let thrown: Error | undefined; try { await runCustomLocalNonInteractive(runtime, { - secretInputMode: "ref", + secretInputMode: "ref", // pragma: allowlist secret customApiKey: providedSecret, }); } catch (error) { @@ -731,7 +731,7 @@ describe("onboard (non-interactive): provider auth", () => { async ({ runtime }) => { await expect( runNonInteractiveOnboardingWithDefaults(runtime, { - customApiKey: "custom-test-key", + customApiKey: "custom-test-key", // pragma: allowlist secret skipSkills: true, }), ).rejects.toThrow('Auth choice "custom-api-key" requires a base URL and model ID.'); diff --git a/src/commands/onboard-non-interactive/api-keys.ts b/src/commands/onboard-non-interactive/api-keys.ts index e55943e22d5..1ee88e678dd 100644 --- a/src/commands/onboard-non-interactive/api-keys.ts +++ b/src/commands/onboard-non-interactive/api-keys.ts @@ -70,7 +70,8 @@ export async function resolveNonInteractiveApiKey(params: { const resolvedEnvKey = envResolved?.apiKey ?? explicitEnvKey; const resolvedEnvVarName = parseEnvVarNameFromSourceLabel(envResolved?.source) ?? explicitEnvVar; - if (params.secretInputMode === "ref") { + const useSecretRefMode = params.secretInputMode === "ref"; // pragma: allowlist secret + if (useSecretRefMode) { if (!resolvedEnvKey && flagKey) { params.runtime.error( [ diff --git a/src/commands/onboard-non-interactive/local/auth-choice.ts b/src/commands/onboard-non-interactive/local/auth-choice.ts index 88710fa1b63..98eef51dd20 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice.ts @@ -91,7 +91,8 @@ export async function applyNonInteractiveAuthChoice(params: { ? { secretInputMode: requestedSecretInputMode } : undefined; const toStoredSecretInput = (resolved: ResolvedNonInteractiveApiKey): SecretInput | null => { - if (requestedSecretInputMode !== "ref") { + const storePlaintextSecret = requestedSecretInputMode !== "ref"; // pragma: allowlist secret + if (storePlaintextSecret) { return resolved.key; } if (resolved.source !== "env") { @@ -948,7 +949,8 @@ export async function applyNonInteractiveAuthChoice(params: { }); let customApiKeyInput: SecretInput | undefined; if (resolvedCustomApiKey) { - if (requestedSecretInputMode === "ref") { + const storeCustomApiKeyAsRef = requestedSecretInputMode === "ref"; // pragma: allowlist secret + if (storeCustomApiKeyAsRef) { const stored = toStoredSecretInput(resolvedCustomApiKey); if (!stored) { return null; diff --git a/src/commands/onboard-search.test.ts b/src/commands/onboard-search.test.ts index d8ed9e8ce6f..e1f77bfff68 100644 --- a/src/commands/onboard-search.test.ts +++ b/src/commands/onboard-search.test.ts @@ -121,7 +121,7 @@ describe("setupSearch", () => { web: { search: { provider: "perplexity", - perplexity: { apiKey: "existing-key" }, + perplexity: { apiKey: "existing-key" }, // pragma: allowlist secret }, }, }, @@ -142,7 +142,7 @@ describe("setupSearch", () => { search: { provider: "perplexity", enabled: false, - perplexity: { apiKey: "existing-key" }, + perplexity: { apiKey: "existing-key" }, // pragma: allowlist secret }, }, }, @@ -162,7 +162,7 @@ describe("setupSearch", () => { web: { search: { provider: "perplexity", - perplexity: { apiKey: "stored-pplx-key" }, + perplexity: { apiKey: "stored-pplx-key" }, // pragma: allowlist secret }, }, }, @@ -184,7 +184,7 @@ describe("setupSearch", () => { search: { provider: "perplexity", enabled: false, - perplexity: { apiKey: "stored-pplx-key" }, + perplexity: { apiKey: "stored-pplx-key" }, // pragma: allowlist secret }, }, }, @@ -212,7 +212,7 @@ describe("setupSearch", () => { it("quickstart skips key prompt when env var is available", async () => { const orig = process.env.BRAVE_API_KEY; - process.env.BRAVE_API_KEY = "env-brave-key"; + process.env.BRAVE_API_KEY = "env-brave-key"; // pragma: allowlist secret try { const cfg: OpenClawConfig = {}; const { prompter } = createPrompter({ selectValue: "brave" }); @@ -235,13 +235,13 @@ describe("setupSearch", () => { const cfg: OpenClawConfig = {}; const { prompter } = createPrompter({ selectValue: "perplexity" }); const result = await setupSearch(cfg, runtime, prompter, { - secretInputMode: "ref", + secretInputMode: "ref", // pragma: allowlist secret }); expect(result.tools?.web?.search?.provider).toBe("perplexity"); expect(result.tools?.web?.search?.perplexity?.apiKey).toEqual({ source: "env", provider: "default", - id: "PERPLEXITY_API_KEY", + id: "PERPLEXITY_API_KEY", // pragma: allowlist secret }); expect(prompter.text).not.toHaveBeenCalled(); }); @@ -250,7 +250,7 @@ describe("setupSearch", () => { const cfg: OpenClawConfig = {}; const { prompter } = createPrompter({ selectValue: "brave" }); const result = await setupSearch(cfg, runtime, prompter, { - secretInputMode: "ref", + secretInputMode: "ref", // pragma: allowlist secret }); expect(result.tools?.web?.search?.provider).toBe("brave"); expect(result.tools?.web?.search?.apiKey).toEqual({ diff --git a/src/commands/onboard-search.ts b/src/commands/onboard-search.ts index fa12720a25f..f5e06a44f96 100644 --- a/src/commands/onboard-search.ts +++ b/src/commands/onboard-search.ts @@ -115,7 +115,8 @@ function resolveSearchSecretInput( key: string, secretInputMode?: SecretInputMode, ): SecretInput { - if (secretInputMode === "ref") { + const useSecretRefMode = secretInputMode === "ref"; // pragma: allowlist secret + if (useSecretRefMode) { return buildSearchEnvRef(provider); } return key; @@ -254,7 +255,8 @@ export async function setupSearch( return preserveDisabledState(config, result); } - if (opts?.secretInputMode === "ref") { + const useSecretRefMode = opts?.secretInputMode === "ref"; // pragma: allowlist secret + if (useSecretRefMode) { if (keyConfigured) { return preserveDisabledState(config, applyProviderOnly(config, choice)); } diff --git a/src/commands/onboard-types.ts b/src/commands/onboard-types.ts index 9e664b9a66d..7e938430517 100644 --- a/src/commands/onboard-types.ts +++ b/src/commands/onboard-types.ts @@ -87,7 +87,7 @@ export type NodeManagerChoice = "npm" | "pnpm" | "bun"; export type ChannelChoice = ChannelId; // Legacy alias (pre-rename). export type ProviderChoice = ChannelChoice; -export type SecretInputMode = "plaintext" | "ref"; +export type SecretInputMode = "plaintext" | "ref"; // pragma: allowlist secret export type OnboardOptions = { mode?: OnboardMode; diff --git a/src/commands/onboard.test.ts b/src/commands/onboard.test.ts index 4fa6b04cc12..1233222bf54 100644 --- a/src/commands/onboard.test.ts +++ b/src/commands/onboard.test.ts @@ -47,7 +47,7 @@ describe("onboardCommand", () => { await onboardCommand( { - secretInputMode: "invalid" as never, + secretInputMode: "invalid" as never, // pragma: allowlist secret }, runtime, ); diff --git a/src/commands/onboard.ts b/src/commands/onboard.ts index 1901d70e08f..9c55bddf1d6 100644 --- a/src/commands/onboard.ts +++ b/src/commands/onboard.ts @@ -39,8 +39,8 @@ export async function onboardCommand(opts: OnboardOptions, runtime: RuntimeEnv = : { ...opts, authChoice: normalizedAuthChoice, flow }; if ( normalizedOpts.secretInputMode && - normalizedOpts.secretInputMode !== "plaintext" && - normalizedOpts.secretInputMode !== "ref" + normalizedOpts.secretInputMode !== "plaintext" && // pragma: allowlist secret + normalizedOpts.secretInputMode !== "ref" // pragma: allowlist secret ) { runtime.error('Invalid --secret-input-mode. Use "plaintext" or "ref".'); runtime.exit(1); diff --git a/src/commands/status-all/channels.mattermost-token-summary.test.ts b/src/commands/status-all/channels.mattermost-token-summary.test.ts index 3d0a84d3ee6..3c028ba44d1 100644 --- a/src/commands/status-all/channels.mattermost-token-summary.test.ts +++ b/src/commands/status-all/channels.mattermost-token-summary.test.ts @@ -236,9 +236,9 @@ function makeHttpSlackUnavailablePlugin(): ChannelPlugin { botToken: "xoxb-http", signingSecret: "", botTokenSource: "config", - signingSecretSource: "config", + signingSecretSource: "config", // pragma: allowlist secret botTokenStatus: "available", - signingSecretStatus: "configured_unavailable", + signingSecretStatus: "configured_unavailable", // pragma: allowlist secret }), resolveAccount: () => ({ name: "Primary", @@ -248,9 +248,9 @@ function makeHttpSlackUnavailablePlugin(): ChannelPlugin { botToken: "xoxb-http", signingSecret: "", botTokenSource: "config", - signingSecretSource: "config", + signingSecretSource: "config", // pragma: allowlist secret botTokenStatus: "available", - signingSecretStatus: "configured_unavailable", + signingSecretStatus: "configured_unavailable", // pragma: allowlist secret }), isConfigured: () => true, isEnabled: () => true, diff --git a/src/commands/status-all/channels.ts b/src/commands/status-all/channels.ts index bfa4fa03112..cf3a67a99b5 100644 --- a/src/commands/status-all/channels.ts +++ b/src/commands/status-all/channels.ts @@ -177,7 +177,10 @@ const buildAccountNotes = (params: { if (snapshot.appTokenSource && snapshot.appTokenSource !== "none") { notes.push(`app:${snapshot.appTokenSource}`); } - if (snapshot.signingSecretSource && snapshot.signingSecretSource !== "none") { + if ( + snapshot.signingSecretSource && + snapshot.signingSecretSource !== "none" /* pragma: allowlist secret */ + ) { notes.push(`signing:${snapshot.signingSecretSource}`); } if (hasConfiguredUnavailableCredentialStatus(entry.account)) { diff --git a/src/config/schema.help.ts b/src/config/schema.help.ts index ee760f2d23f..dbf416865b7 100644 --- a/src/config/schema.help.ts +++ b/src/config/schema.help.ts @@ -150,7 +150,7 @@ export const FIELD_HELP: Record = { "talk.providers.*.voiceAliases": "Optional provider voice alias map for Talk directives.", "talk.providers.*.modelId": "Provider default model ID for Talk mode.", "talk.providers.*.outputFormat": "Provider default output format for Talk mode.", - "talk.providers.*.apiKey": "Provider API key for Talk mode.", + "talk.providers.*.apiKey": "Provider API key for Talk mode.", // pragma: allowlist secret "talk.voiceId": "Legacy ElevenLabs default voice ID for Talk mode. Prefer talk.providers.elevenlabs.voiceId.", "talk.voiceAliases": @@ -651,7 +651,7 @@ export const FIELD_HELP: Record = { "tools.web.search.gemini.apiKey": "Gemini API key for Google Search grounding (fallback: GEMINI_API_KEY env var).", "tools.web.search.gemini.model": 'Gemini model override (default: "gemini-2.5-flash").', - "tools.web.search.grok.apiKey": "Grok (xAI) API key (fallback: XAI_API_KEY env var).", + "tools.web.search.grok.apiKey": "Grok (xAI) API key (fallback: XAI_API_KEY env var).", // pragma: allowlist secret "tools.web.search.grok.model": 'Grok model override (default: "grok-4-1-fast").', "tools.web.search.kimi.apiKey": "Moonshot/Kimi API key (fallback: KIMI_API_KEY or MOONSHOT_API_KEY env var).", diff --git a/src/config/schema.labels.ts b/src/config/schema.labels.ts index a5fec8dadcf..bf7b401f943 100644 --- a/src/config/schema.labels.ts +++ b/src/config/schema.labels.ts @@ -217,14 +217,14 @@ export const FIELD_LABELS: Record = { "tools.web.search.maxResults": "Web Search Max Results", "tools.web.search.timeoutSeconds": "Web Search Timeout (sec)", "tools.web.search.cacheTtlMinutes": "Web Search Cache TTL (min)", - "tools.web.search.perplexity.apiKey": "Perplexity API Key", + "tools.web.search.perplexity.apiKey": "Perplexity API Key", // pragma: allowlist secret "tools.web.search.perplexity.baseUrl": "Perplexity Base URL", "tools.web.search.perplexity.model": "Perplexity Model", - "tools.web.search.gemini.apiKey": "Gemini Search API Key", + "tools.web.search.gemini.apiKey": "Gemini Search API Key", // pragma: allowlist secret "tools.web.search.gemini.model": "Gemini Search Model", - "tools.web.search.grok.apiKey": "Grok Search API Key", + "tools.web.search.grok.apiKey": "Grok Search API Key", // pragma: allowlist secret "tools.web.search.grok.model": "Grok Search Model", - "tools.web.search.kimi.apiKey": "Kimi Search API Key", + "tools.web.search.kimi.apiKey": "Kimi Search API Key", // pragma: allowlist secret "tools.web.search.kimi.baseUrl": "Kimi Search Base URL", "tools.web.search.kimi.model": "Kimi Search Model", "tools.web.fetch.enabled": "Enable Web Fetch Tool", @@ -236,7 +236,7 @@ export const FIELD_LABELS: Record = { "tools.web.fetch.userAgent": "Web Fetch User-Agent", "tools.web.fetch.readability": "Web Fetch Readability Extraction", "tools.web.fetch.firecrawl.enabled": "Enable Firecrawl Fallback", - "tools.web.fetch.firecrawl.apiKey": "Firecrawl API Key", + "tools.web.fetch.firecrawl.apiKey": "Firecrawl API Key", // pragma: allowlist secret "tools.web.fetch.firecrawl.baseUrl": "Firecrawl Base URL", "tools.web.fetch.firecrawl.onlyMainContent": "Firecrawl Main Content Only", "tools.web.fetch.firecrawl.maxAgeMs": "Firecrawl Cache Max Age (ms)", @@ -411,7 +411,7 @@ export const FIELD_LABELS: Record = { "models.mode": "Model Catalog Mode", "models.providers": "Model Providers", "models.providers.*.baseUrl": "Model Provider Base URL", - "models.providers.*.apiKey": "Model Provider API Key", + "models.providers.*.apiKey": "Model Provider API Key", // pragma: allowlist secret "models.providers.*.auth": "Model Provider Auth Mode", "models.providers.*.api": "Model Provider API Adapter", "models.providers.*.injectNumCtxForOpenAICompat": "Model Provider Inject num_ctx (OpenAI Compat)", @@ -484,7 +484,7 @@ export const FIELD_LABELS: Record = { "commands.useAccessGroups": "Use Access Groups", "commands.ownerAllowFrom": "Command Owners", "commands.ownerDisplay": "Owner ID Display", - "commands.ownerDisplaySecret": "Owner ID Hash Secret", + "commands.ownerDisplaySecret": "Owner ID Hash Secret", // pragma: allowlist secret "commands.allowFrom": "Command Elevated Access Rules", ui: "UI", "ui.seamColor": "Accent Color", @@ -679,8 +679,8 @@ export const FIELD_LABELS: Record = { "talk.providers.*.voiceAliases": "Talk Provider Voice Aliases", "talk.providers.*.modelId": "Talk Provider Model ID", "talk.providers.*.outputFormat": "Talk Provider Output Format", - "talk.providers.*.apiKey": "Talk Provider API Key", - "talk.apiKey": "Talk API Key", + "talk.providers.*.apiKey": "Talk Provider API Key", // pragma: allowlist secret + "talk.apiKey": "Talk API Key", // pragma: allowlist secret channels: "Channels", "channels.defaults": "Channel Defaults", "channels.defaults.groupPolicy": "Default Group Policy", @@ -823,7 +823,7 @@ export const FIELD_LABELS: Record = { "plugins.entries.*.enabled": "Plugin Enabled", "plugins.entries.*.hooks": "Plugin Hook Policy", "plugins.entries.*.hooks.allowPromptInjection": "Allow Prompt Injection Hooks", - "plugins.entries.*.apiKey": "Plugin API Key", + "plugins.entries.*.apiKey": "Plugin API Key", // pragma: allowlist secret "plugins.entries.*.env": "Plugin Environment Variables", "plugins.entries.*.config": "Plugin Config", "plugins.installs": "Plugin Install Records", diff --git a/src/config/types.secrets.ts b/src/config/types.secrets.ts index 40a6963f2d8..687f00a212a 100644 --- a/src/config/types.secrets.ts +++ b/src/config/types.secrets.ts @@ -1,4 +1,4 @@ -export type SecretRefSource = "env" | "file" | "exec"; +export type SecretRefSource = "env" | "file" | "exec"; // pragma: allowlist secret /** * Stable identifier for a secret in a configured source. @@ -14,7 +14,7 @@ export type SecretRef = { }; export type SecretInput = string | SecretRef; -export const DEFAULT_SECRET_PROVIDER_ALIAS = "default"; +export const DEFAULT_SECRET_PROVIDER_ALIAS = "default"; // pragma: allowlist secret export const ENV_SECRET_REF_ID_RE = /^[A-Z][A-Z0-9_]{0,127}$/; const ENV_SECRET_TEMPLATE_RE = /^\$\{([A-Z][A-Z0-9_]{0,127})\}$/; type SecretDefaults = { @@ -179,7 +179,7 @@ export type EnvSecretProviderConfig = { allowlist?: string[]; }; -export type FileSecretProviderMode = "singleValue" | "json"; +export type FileSecretProviderMode = "singleValue" | "json"; // pragma: allowlist secret export type FileSecretProviderConfig = { source: "file"; diff --git a/src/gateway/auth-mode-policy.test.ts b/src/gateway/auth-mode-policy.test.ts index 50b62f6bcfb..81907f7e3a2 100644 --- a/src/gateway/auth-mode-policy.test.ts +++ b/src/gateway/auth-mode-policy.test.ts @@ -13,7 +13,7 @@ describe("gateway auth mode policy", () => { auth: { mode: "token", token: "token-value", - password: "password-value", + password: "password-value", // pragma: allowlist secret }, }, }; @@ -36,7 +36,7 @@ describe("gateway auth mode policy", () => { gateway: { auth: { token: "token-value", - password: "password-value", + password: "password-value", // pragma: allowlist secret }, }, }; @@ -65,7 +65,7 @@ describe("gateway auth mode policy", () => { gateway: { auth: { token: "token-value", - password: "password-value", + password: "password-value", // pragma: allowlist secret }, }, }; diff --git a/src/gateway/auth.ts b/src/gateway/auth.ts index b55482b304d..467d14d4337 100644 --- a/src/gateway/auth.ts +++ b/src/gateway/auth.ts @@ -252,7 +252,7 @@ export function resolveGatewayAuth(params: { env, includeLegacyEnv: false, tokenPrecedence: "config-first", - passwordPrecedence: "config-first", + passwordPrecedence: "config-first", // pragma: allowlist secret }); const token = resolvedCredentials.token; const password = resolvedCredentials.password; diff --git a/src/gateway/call.ts b/src/gateway/call.ts index ba1e079e455..fa140608216 100644 --- a/src/gateway/call.ts +++ b/src/gateway/call.ts @@ -358,7 +358,7 @@ async function resolveGatewayCredentialsWithEnv( explicitAuth: context.explicitAuth, urlOverride: context.urlOverride, urlOverrideSource: context.urlOverrideSource, - remotePasswordPrecedence: "env-first", + remotePasswordPrecedence: "env-first", // pragma: allowlist secret }); } @@ -487,7 +487,7 @@ async function resolveGatewayCredentialsWithEnv( explicitAuth: context.explicitAuth, urlOverride: context.urlOverride, urlOverrideSource: context.urlOverrideSource, - remotePasswordPrecedence: "env-first", + remotePasswordPrecedence: "env-first", // pragma: allowlist secret }); } diff --git a/src/gateway/credentials.ts b/src/gateway/credentials.ts index c1172a09029..88c8a86088b 100644 --- a/src/gateway/credentials.ts +++ b/src/gateway/credentials.ts @@ -16,7 +16,7 @@ export type GatewayCredentialPrecedence = "env-first" | "config-first"; export type GatewayRemoteCredentialPrecedence = "remote-first" | "env-first"; export type GatewayRemoteCredentialFallback = "remote-env-local" | "remote-only"; -const GATEWAY_SECRET_REF_UNAVAILABLE_ERROR_CODE = "GATEWAY_SECRET_REF_UNAVAILABLE"; +const GATEWAY_SECRET_REF_UNAVAILABLE_ERROR_CODE = "GATEWAY_SECRET_REF_UNAVAILABLE"; // pragma: allowlist secret export class GatewaySecretRefUnavailableError extends Error { readonly code = GATEWAY_SECRET_REF_UNAVAILABLE_ERROR_CODE; @@ -119,7 +119,7 @@ export function resolveGatewayCredentialsFromValues(params: { ? firstDefined([configToken, envToken]) : firstDefined([envToken, configToken]); const password = - passwordPrecedence === "config-first" + passwordPrecedence === "config-first" // pragma: allowlist secret ? firstDefined([configPassword, envPassword]) : firstDefined([envPassword, configPassword]); @@ -158,7 +158,7 @@ export function resolveGatewayCredentialsFromConfig(params: { env, includeLegacyEnv, tokenPrecedence: "env-first", - passwordPrecedence: "env-first", + passwordPrecedence: "env-first", // pragma: allowlist secret }); } @@ -243,9 +243,9 @@ export function resolveGatewayCredentialsFromConfig(params: { ? firstDefined([envToken, remoteToken, localToken]) : firstDefined([remoteToken, envToken, localToken]); const password = - remotePasswordFallback === "remote-only" + remotePasswordFallback === "remote-only" // pragma: allowlist secret ? remotePassword - : remotePasswordPrecedence === "env-first" + : remotePasswordPrecedence === "env-first" // pragma: allowlist secret ? firstDefined([envPassword, remotePassword, localPassword]) : firstDefined([remotePassword, envPassword, localPassword]); @@ -255,7 +255,7 @@ export function resolveGatewayCredentialsFromConfig(params: { const localTokenFallbackEnabled = remoteTokenFallback !== "remote-only"; const localTokenFallback = remoteTokenFallback === "remote-only" ? undefined : localToken; const localPasswordFallback = - remotePasswordFallback === "remote-only" ? undefined : localPassword; + remotePasswordFallback === "remote-only" ? undefined : localPassword; // pragma: allowlist secret if (remoteTokenRef && !token && !envToken && !localTokenFallback && !password) { throwUnresolvedGatewaySecretInput("gateway.remote.token"); } diff --git a/src/gateway/protocol/connect-error-details.ts b/src/gateway/protocol/connect-error-details.ts index 62286092671..442e8f2c54d 100644 --- a/src/gateway/protocol/connect-error-details.ts +++ b/src/gateway/protocol/connect-error-details.ts @@ -4,9 +4,9 @@ export const ConnectErrorDetailCodes = { AUTH_TOKEN_MISSING: "AUTH_TOKEN_MISSING", AUTH_TOKEN_MISMATCH: "AUTH_TOKEN_MISMATCH", AUTH_TOKEN_NOT_CONFIGURED: "AUTH_TOKEN_NOT_CONFIGURED", - AUTH_PASSWORD_MISSING: "AUTH_PASSWORD_MISSING", - AUTH_PASSWORD_MISMATCH: "AUTH_PASSWORD_MISMATCH", - AUTH_PASSWORD_NOT_CONFIGURED: "AUTH_PASSWORD_NOT_CONFIGURED", + AUTH_PASSWORD_MISSING: "AUTH_PASSWORD_MISSING", // pragma: allowlist secret + AUTH_PASSWORD_MISMATCH: "AUTH_PASSWORD_MISMATCH", // pragma: allowlist secret + AUTH_PASSWORD_NOT_CONFIGURED: "AUTH_PASSWORD_NOT_CONFIGURED", // pragma: allowlist secret AUTH_DEVICE_TOKEN_MISMATCH: "AUTH_DEVICE_TOKEN_MISMATCH", AUTH_RATE_LIMITED: "AUTH_RATE_LIMITED", AUTH_TAILSCALE_IDENTITY_MISSING: "AUTH_TAILSCALE_IDENTITY_MISSING", diff --git a/src/gateway/resolve-configured-secret-input-string.ts b/src/gateway/resolve-configured-secret-input-string.ts index c83354aa9dd..e698b09910c 100644 --- a/src/gateway/resolve-configured-secret-input-string.ts +++ b/src/gateway/resolve-configured-secret-input-string.ts @@ -3,7 +3,7 @@ import { resolveSecretInputRef } from "../config/types.secrets.js"; import { secretRefKey } from "../secrets/ref-contract.js"; import { resolveSecretRefValues } from "../secrets/resolve.js"; -export type SecretInputUnresolvedReasonStyle = "generic" | "detailed"; +export type SecretInputUnresolvedReasonStyle = "generic" | "detailed"; // pragma: allowlist secret function trimToUndefined(value: unknown): string | undefined { if (typeof value !== "string") { diff --git a/src/gateway/server-methods/nodes.invoke-wake.test.ts b/src/gateway/server-methods/nodes.invoke-wake.test.ts index 39392db70b5..6e3ced97d6f 100644 --- a/src/gateway/server-methods/nodes.invoke-wake.test.ts +++ b/src/gateway/server-methods/nodes.invoke-wake.test.ts @@ -115,7 +115,7 @@ function mockSuccessfulWakeConfig(nodeId: string) { value: { teamId: "TEAM123", keyId: "KEY123", - privateKey: "-----BEGIN PRIVATE KEY-----\nabc\n-----END PRIVATE KEY-----", + privateKey: "-----BEGIN PRIVATE KEY-----\nabc\n-----END PRIVATE KEY-----", // pragma: allowlist secret }, }); mocks.sendApnsBackgroundWake.mockResolvedValue({ diff --git a/src/gateway/server.auth.modes.suite.ts b/src/gateway/server.auth.modes.suite.ts index efe9ad7b111..77c23a0d0b2 100644 --- a/src/gateway/server.auth.modes.suite.ts +++ b/src/gateway/server.auth.modes.suite.ts @@ -20,7 +20,7 @@ export function registerAuthModesSuite(): void { let port: number; beforeAll(async () => { - testState.gatewayAuth = { mode: "password", password: "secret" }; + testState.gatewayAuth = { mode: "password", password: "secret" }; // pragma: allowlist secret port = await getFreePort(); server = await startGatewayServer(port); }); @@ -31,14 +31,14 @@ export function registerAuthModesSuite(): void { test("accepts password auth when configured", async () => { const ws = await openWs(port); - const res = await connectReq(ws, { password: "secret" }); + const res = await connectReq(ws, { password: "secret" }); // pragma: allowlist secret expect(res.ok).toBe(true); ws.close(); }); test("rejects invalid password", async () => { const ws = await openWs(port); - const res = await connectReq(ws, { password: "wrong" }); + const res = await connectReq(ws, { password: "wrong" }); // pragma: allowlist secret expect(res.ok).toBe(false); expect(res.error?.message ?? "").toContain("unauthorized"); ws.close(); diff --git a/src/gateway/server.reload.test.ts b/src/gateway/server.reload.test.ts index a6fa5327628..e691256d70f 100644 --- a/src/gateway/server.reload.test.ts +++ b/src/gateway/server.reload.test.ts @@ -465,7 +465,7 @@ describe("gateway hot reload", () => { serverOptions: { auth: { mode: "password", - password: "override-password", + password: "override-password", // pragma: allowlist secret }, }, }), @@ -486,7 +486,7 @@ describe("gateway hot reload", () => { it("emits one-shot degraded and recovered system events during secret reload transitions", async () => { await writeEnvRefConfig(); - process.env.OPENAI_API_KEY = "sk-startup"; + process.env.OPENAI_API_KEY = "sk-startup"; // pragma: allowlist secret await withGatewayServer(async () => { const onHotReload = hoisted.getOnHotReload(); @@ -531,7 +531,7 @@ describe("gateway hot reload", () => { ); expect(drainSystemEvents(sessionKey)).toEqual([]); - process.env.OPENAI_API_KEY = "sk-recovered"; + process.env.OPENAI_API_KEY = "sk-recovered"; // pragma: allowlist secret await expect(onHotReload?.(plan, nextConfig)).resolves.toBeUndefined(); const recoveredEvents = drainSystemEvents(sessionKey); expect(recoveredEvents.some((event) => event.includes("[SECRETS_RELOADER_RECOVERED]"))).toBe( @@ -542,7 +542,7 @@ describe("gateway hot reload", () => { it("serves secrets.reload immediately after startup without race failures", async () => { await writeEnvRefConfig(); - process.env.OPENAI_API_KEY = "sk-startup"; + process.env.OPENAI_API_KEY = "sk-startup"; // pragma: allowlist secret const { server, ws } = await startServerWithClient(); try { await connectOk(ws); diff --git a/src/infra/channel-summary.test.ts b/src/infra/channel-summary.test.ts index d56bdd7ac1e..1a16bdc53b6 100644 --- a/src/infra/channel-summary.test.ts +++ b/src/infra/channel-summary.test.ts @@ -33,9 +33,9 @@ function makeSlackHttpSummaryPlugin(): ChannelPlugin { botToken: "xoxb-http", signingSecret: "", botTokenSource: "config", - signingSecretSource: "config", + signingSecretSource: "config", // pragma: allowlist secret botTokenStatus: "available", - signingSecretStatus: "configured_unavailable", + signingSecretStatus: "configured_unavailable", // pragma: allowlist secret } : { accountId: "primary", diff --git a/src/infra/channel-summary.ts b/src/infra/channel-summary.ts index f412d687fd1..08fd35d9327 100644 --- a/src/infra/channel-summary.ts +++ b/src/infra/channel-summary.ts @@ -69,7 +69,10 @@ const buildAccountDetails = (params: { if (snapshot.appTokenSource && snapshot.appTokenSource !== "none") { details.push(`app:${snapshot.appTokenSource}`); } - if (snapshot.signingSecretSource && snapshot.signingSecretSource !== "none") { + if ( + snapshot.signingSecretSource && + snapshot.signingSecretSource !== "none" /* pragma: allowlist secret */ + ) { details.push(`signing:${snapshot.signingSecretSource}`); } if (hasConfiguredUnavailableCredentialStatus(params.entry.account)) { diff --git a/src/infra/provider-usage.auth.normalizes-keys.test.ts b/src/infra/provider-usage.auth.normalizes-keys.test.ts index 3dccd2bf1be..bae5ae5a7d9 100644 --- a/src/infra/provider-usage.auth.normalizes-keys.test.ts +++ b/src/infra/provider-usage.auth.normalizes-keys.test.ts @@ -248,17 +248,17 @@ describe("resolveProviderAuths key normalization", () => { zai: { baseUrl: "https://api.z.ai", models: [modelDef], - apiKey: "cfg-zai-key", + apiKey: "cfg-zai-key", // pragma: allowlist secret }, minimax: { baseUrl: "https://api.minimaxi.com", models: [modelDef], - apiKey: "cfg-minimax-key", + apiKey: "cfg-minimax-key", // pragma: allowlist secret }, xiaomi: { baseUrl: "https://api.xiaomi.example", models: [modelDef], - apiKey: "cfg-xiaomi-key", + apiKey: "cfg-xiaomi-key", // pragma: allowlist secret }, }, }, diff --git a/src/media-understanding/providers/moonshot/video.test.ts b/src/media-understanding/providers/moonshot/video.test.ts index eba98042884..f6ffb1ca957 100644 --- a/src/media-understanding/providers/moonshot/video.test.ts +++ b/src/media-understanding/providers/moonshot/video.test.ts @@ -16,7 +16,7 @@ describe("describeMoonshotVideo", () => { const result = await describeMoonshotVideo({ buffer: Buffer.from("video-bytes"), fileName: "clip.mp4", - apiKey: "moonshot-test", + apiKey: "moonshot-test", // pragma: allowlist secret timeoutMs: 1500, baseUrl: "https://api.moonshot.ai/v1/", model: "kimi-k2.5", @@ -61,7 +61,7 @@ describe("describeMoonshotVideo", () => { const result = await describeMoonshotVideo({ buffer: Buffer.from("video"), fileName: "clip.mp4", - apiKey: "moonshot-test", + apiKey: "moonshot-test", // pragma: allowlist secret timeoutMs: 1000, fetchFn, }); diff --git a/src/media-understanding/runner.auto-audio.test.ts b/src/media-understanding/runner.auto-audio.test.ts index 975f1438b46..b2e282f3666 100644 --- a/src/media-understanding/runner.auto-audio.test.ts +++ b/src/media-understanding/runner.auto-audio.test.ts @@ -120,7 +120,7 @@ describe("runCapability auto audio entries", () => { delete process.env.GROQ_API_KEY; delete process.env.DEEPGRAM_API_KEY; delete process.env.GEMINI_API_KEY; - process.env.MISTRAL_API_KEY = "mistral-test-key"; + process.env.MISTRAL_API_KEY = "mistral-test-key"; // pragma: allowlist secret let runResult: Awaited> | undefined; try { await withAudioFixture("openclaw-auto-audio-mistral", async ({ ctx, media, cache }) => { @@ -140,7 +140,7 @@ describe("runCapability auto audio entries", () => { models: { providers: { mistral: { - apiKey: "mistral-test-key", + apiKey: "mistral-test-key", // pragma: allowlist secret models: [], }, }, diff --git a/src/media-understanding/runner.proxy.test.ts b/src/media-understanding/runner.proxy.test.ts index b96f099d3cc..f05ff4a87a1 100644 --- a/src/media-understanding/runner.proxy.test.ts +++ b/src/media-understanding/runner.proxy.test.ts @@ -25,7 +25,7 @@ async function runAudioCapabilityWithFetchCapture(params: { models: { providers: { openai: { - apiKey: "test-key", + apiKey: "test-key", // pragma: allowlist secret models: [], }, }, @@ -80,7 +80,7 @@ describe("runCapability proxy fetch passthrough", () => { models: { providers: { moonshot: { - apiKey: "test-key", + apiKey: "test-key", // pragma: allowlist secret models: [], }, }, diff --git a/src/media-understanding/runner.skip-tiny-audio.test.ts b/src/media-understanding/runner.skip-tiny-audio.test.ts index 6447e2b1dbf..a4021fb52a8 100644 --- a/src/media-understanding/runner.skip-tiny-audio.test.ts +++ b/src/media-understanding/runner.skip-tiny-audio.test.ts @@ -52,7 +52,7 @@ const AUDIO_CAPABILITY_CFG = { models: { providers: { openai: { - apiKey: "test-key", + apiKey: "test-key", // pragma: allowlist secret models: [], }, }, diff --git a/src/memory/embeddings.test.ts b/src/memory/embeddings.test.ts index c8cca71029e..027673c7099 100644 --- a/src/memory/embeddings.test.ts +++ b/src/memory/embeddings.test.ts @@ -233,7 +233,7 @@ describe("embedding provider remote overrides", () => { config: {} as never, provider: "gemini", remote: { - apiKey: "GEMINI_API_KEY", + apiKey: "GEMINI_API_KEY", // pragma: allowlist secret }, model: "text-embedding-004", fallback: "openai", @@ -266,7 +266,7 @@ describe("embedding provider remote overrides", () => { config: cfg as never, provider: "mistral", remote: { - apiKey: "mistral-key", + apiKey: "mistral-key", // pragma: allowlist secret }, model: "mistral/mistral-embed", fallback: "none", @@ -356,7 +356,7 @@ describe("embedding provider auto selection", () => { vi.stubGlobal("fetch", fetchMock); vi.mocked(authModule.resolveApiKeyForProvider).mockImplementation(async ({ provider }) => { if (provider === "mistral") { - return { apiKey: "mistral-key", source: "env: MISTRAL_API_KEY", mode: "api-key" }; + return { apiKey: "mistral-key", source: "env: MISTRAL_API_KEY", mode: "api-key" }; // pragma: allowlist secret } throw new Error(`No API key found for provider "${provider}".`); }); diff --git a/src/secrets/apply.ts b/src/secrets/apply.ts index 1286071cf91..85408954239 100644 --- a/src/secrets/apply.ts +++ b/src/secrets/apply.ts @@ -298,7 +298,8 @@ function applyConfigTargetMutations(params: { } const targetPathSegments = resolved.pathSegments; - if (resolved.entry.secretShape === "sibling_ref") { + const usesSiblingRef = resolved.entry.secretShape === "sibling_ref"; // pragma: allowlist secret + if (usesSiblingRef) { const previous = getPath(params.nextConfig, targetPathSegments); if (isNonEmptyString(previous)) { scrubbedValues.add(previous.trim()); @@ -530,7 +531,8 @@ function applyAuthProfileTargetMutation(params: { store, }); const targetPathSegments = params.resolved.pathSegments; - if (params.resolved.entry.secretShape === "sibling_ref") { + const usesSiblingRef = params.resolved.entry.secretShape === "sibling_ref"; // pragma: allowlist secret + if (usesSiblingRef) { const previous = getPath(store, targetPathSegments); if (isNonEmptyString(previous)) { params.scrubbedValues.add(previous.trim()); diff --git a/src/secrets/audit.test.ts b/src/secrets/audit.test.ts index 21f59d51cac..cd85d84d3d8 100644 --- a/src/secrets/audit.test.ts +++ b/src/secrets/audit.test.ts @@ -53,7 +53,7 @@ async function createAuditFixture(): Promise { env: { OPENCLAW_STATE_DIR: stateDir, OPENCLAW_CONFIG_PATH: configPath, - OPENAI_API_KEY: "env-openai-key", + OPENAI_API_KEY: "env-openai-key", // pragma: allowlist secret PATH: resolveRuntimePathEnv(), }, }; @@ -146,7 +146,7 @@ describe("secrets audit", () => { "#!/bin/sh", `printf 'x\\n' >> ${JSON.stringify(execLogPath)}`, "cat >/dev/null", - 'printf \'{"protocolVersion":1,"values":{"providers/openai/apiKey":"value:providers/openai/apiKey","providers/moonshot/apiKey":"value:providers/moonshot/apiKey"}}\'', + 'printf \'{"protocolVersion":1,"values":{"providers/openai/apiKey":"value:providers/openai/apiKey","providers/moonshot/apiKey":"value:providers/moonshot/apiKey"}}\'', // pragma: allowlist secret ].join("\n"), { encoding: "utf8", mode: 0o700 }, ); diff --git a/src/secrets/audit.ts b/src/secrets/audit.ts index 277983d1deb..132ea4ac431 100644 --- a/src/secrets/audit.ts +++ b/src/secrets/audit.ts @@ -36,7 +36,7 @@ export type SecretsAuditCode = | "REF_SHADOWED" | "LEGACY_RESIDUE"; -export type SecretsAuditSeverity = "info" | "warn" | "error"; +export type SecretsAuditSeverity = "info" | "warn" | "error"; // pragma: allowlist secret export type SecretsAuditFinding = { code: SecretsAuditCode; @@ -48,7 +48,7 @@ export type SecretsAuditFinding = { profileId?: string; }; -export type SecretsAuditStatus = "clean" | "findings" | "unresolved"; +export type SecretsAuditStatus = "clean" | "findings" | "unresolved"; // pragma: allowlist secret export type SecretsAuditReport = { version: 1; diff --git a/src/secrets/command-config.ts b/src/secrets/command-config.ts index dc542eba00b..0d264aad9e7 100644 --- a/src/secrets/command-config.ts +++ b/src/secrets/command-config.ts @@ -79,7 +79,9 @@ export function analyzeCommandSecretAssignmentsFromSnapshot(params: { value: resolved, }); - if (target.entry.secretShape === "sibling_ref" && explicitRef && inlineCandidateRef) { + const hasCompetingSiblingRef = + target.entry.secretShape === "sibling_ref" && explicitRef && inlineCandidateRef; // pragma: allowlist secret + if (hasCompetingSiblingRef) { diagnostics.push( `${target.path}: both inline and sibling ref were present; sibling ref took precedence.`, ); diff --git a/src/secrets/credential-matrix.ts b/src/secrets/credential-matrix.ts index a3c44e34fdb..05fa45f749e 100644 --- a/src/secrets/credential-matrix.ts +++ b/src/secrets/credential-matrix.ts @@ -6,7 +6,7 @@ type CredentialMatrixEntry = { path: string; refPath?: string; when?: { type: "api_key" | "token" }; - secretShape: "secret_input" | "sibling_ref"; + secretShape: "secret_input" | "sibling_ref"; // pragma: allowlist secret optIn: true; notes?: string; }; diff --git a/src/secrets/resolve.test.ts b/src/secrets/resolve.test.ts index 716ab5af7fa..376f591b73e 100644 --- a/src/secrets/resolve.test.ts +++ b/src/secrets/resolve.test.ts @@ -153,7 +153,7 @@ describe("secret ref resolver", () => { { source: "env", provider: "default", id: "OPENAI_API_KEY" }, { config, - env: { OPENAI_API_KEY: "sk-env-value" }, + env: { OPENAI_API_KEY: "sk-env-value" }, // pragma: allowlist secret }, ); expect(value).toBe("sk-env-value"); @@ -167,7 +167,7 @@ describe("secret ref resolver", () => { JSON.stringify({ providers: { openai: { - apiKey: "sk-file-value", + apiKey: "sk-file-value", // pragma: allowlist secret }, }, }), @@ -375,7 +375,7 @@ describe("secret ref resolver", () => { JSON.stringify({ providers: { openai: { - apiKey: "sk-file-value", + apiKey: "sk-file-value", // pragma: allowlist secret }, }, }), diff --git a/src/secrets/runtime.test.ts b/src/secrets/runtime.test.ts index 40e766179e2..e1ca5774a75 100644 --- a/src/secrets/runtime.test.ts +++ b/src/secrets/runtime.test.ts @@ -122,21 +122,21 @@ describe("secrets runtime snapshot", () => { const snapshot = await prepareSecretsRuntimeSnapshot({ config, env: { - OPENAI_API_KEY: "sk-env-openai", - GITHUB_TOKEN: "ghp-env-token", - REVIEW_SKILL_API_KEY: "sk-skill-ref", - MEMORY_REMOTE_API_KEY: "mem-ref-key", - TALK_API_KEY: "talk-ref-key", - TALK_PROVIDER_API_KEY: "talk-provider-ref-key", + OPENAI_API_KEY: "sk-env-openai", // pragma: allowlist secret + GITHUB_TOKEN: "ghp-env-token", // pragma: allowlist secret + REVIEW_SKILL_API_KEY: "sk-skill-ref", // pragma: allowlist secret + MEMORY_REMOTE_API_KEY: "mem-ref-key", // pragma: allowlist secret + TALK_API_KEY: "talk-ref-key", // pragma: allowlist secret + TALK_PROVIDER_API_KEY: "talk-provider-ref-key", // pragma: allowlist secret REMOTE_GATEWAY_TOKEN: "remote-token-ref", - REMOTE_GATEWAY_PASSWORD: "remote-password-ref", + REMOTE_GATEWAY_PASSWORD: "remote-password-ref", // pragma: allowlist secret TELEGRAM_BOT_TOKEN_REF: "telegram-bot-ref", - TELEGRAM_WEBHOOK_SECRET_REF: "telegram-webhook-ref", + TELEGRAM_WEBHOOK_SECRET_REF: "telegram-webhook-ref", // pragma: allowlist secret TELEGRAM_WORK_BOT_TOKEN_REF: "telegram-work-ref", - SLACK_SIGNING_SECRET_REF: "slack-signing-ref", + SLACK_SIGNING_SECRET_REF: "slack-signing-ref", // pragma: allowlist secret SLACK_WORK_BOT_TOKEN_REF: "slack-work-bot-ref", SLACK_WORK_APP_TOKEN_REF: "slack-work-app-ref", - WEB_SEARCH_API_KEY: "web-search-ref", + WEB_SEARCH_API_KEY: "web-search-ref", // pragma: allowlist secret }, agentDirs: ["/tmp/openclaw-agent-main"], loadAuthStore: () => @@ -305,7 +305,7 @@ describe("secrets runtime snapshot", () => { }, }), env: { - WEB_SEARCH_API_KEY: "web-search-ref", + WEB_SEARCH_API_KEY: "web-search-ref", // pragma: allowlist secret }, agentDirs: ["/tmp/openclaw-agent-main"], loadAuthStore: () => ({ version: 1, profiles: {} }), @@ -343,8 +343,8 @@ describe("secrets runtime snapshot", () => { }, }), env: { - WEB_SEARCH_API_KEY: "web-search-ref", - WEB_SEARCH_GEMINI_API_KEY: "web-search-gemini-ref", + WEB_SEARCH_API_KEY: "web-search-ref", // pragma: allowlist secret + WEB_SEARCH_GEMINI_API_KEY: "web-search-gemini-ref", // pragma: allowlist secret }, agentDirs: ["/tmp/openclaw-agent-main"], loadAuthStore: () => ({ version: 1, profiles: {} }), @@ -374,7 +374,7 @@ describe("secrets runtime snapshot", () => { }, }), env: { - WEB_SEARCH_GEMINI_API_KEY: "web-search-gemini-ref", + WEB_SEARCH_GEMINI_API_KEY: "web-search-gemini-ref", // pragma: allowlist secret }, agentDirs: ["/tmp/openclaw-agent-main"], loadAuthStore: () => ({ version: 1, profiles: {} }), @@ -399,7 +399,7 @@ describe("secrets runtime snapshot", () => { { providers: { openai: { - apiKey: "sk-from-file-provider", + apiKey: "sk-from-file-provider", // pragma: allowlist secret }, }, }, @@ -494,7 +494,7 @@ describe("secrets runtime snapshot", () => { }, }, }), - env: { OPENAI_API_KEY: "sk-runtime" }, + env: { OPENAI_API_KEY: "sk-runtime" }, // pragma: allowlist secret agentDirs: ["/tmp/openclaw-agent-main"], loadAuthStore: () => loadAuthStoreWithProfiles({ @@ -603,7 +603,7 @@ describe("secrets runtime snapshot", () => { auth: { mode: "password", token: "local-token", - password: "local-password", + password: "local-password", // pragma: allowlist secret }, remote: { enabled: true, @@ -642,7 +642,7 @@ describe("secrets runtime snapshot", () => { }, }), env: { - GATEWAY_PASSWORD_REF: "resolved-gateway-password", + GATEWAY_PASSWORD_REF: "resolved-gateway-password", // pragma: allowlist secret }, agentDirs: ["/tmp/openclaw-agent-main"], loadAuthStore: () => ({ version: 1, profiles: {} }), @@ -680,7 +680,7 @@ describe("secrets runtime snapshot", () => { auth: { mode: "password", token: { source: "env", provider: "default", id: "GATEWAY_TOKEN_REF" }, - password: "password-123", + password: "password-123", // pragma: allowlist secret }, }, }), @@ -728,7 +728,7 @@ describe("secrets runtime snapshot", () => { }, }), env: { - GATEWAY_PASSWORD_REF: "resolved-gateway-password", + GATEWAY_PASSWORD_REF: "resolved-gateway-password", // pragma: allowlist secret }, agentDirs: ["/tmp/openclaw-agent-main"], loadAuthStore: () => ({ version: 1, profiles: {} }), @@ -822,7 +822,7 @@ describe("secrets runtime snapshot", () => { }), env: { REMOTE_TOKEN: "resolved-remote-token", - REMOTE_PASSWORD: "resolved-remote-password", + REMOTE_PASSWORD: "resolved-remote-password", // pragma: allowlist secret }, agentDirs: ["/tmp/openclaw-agent-main"], loadAuthStore: () => ({ version: 1, profiles: {} }), @@ -846,7 +846,7 @@ describe("secrets runtime snapshot", () => { }, }), env: { - REMOTE_PASSWORD: "resolved-remote-password", + REMOTE_PASSWORD: "resolved-remote-password", // pragma: allowlist secret }, agentDirs: ["/tmp/openclaw-agent-main"], loadAuthStore: () => ({ version: 1, profiles: {} }), @@ -980,8 +980,8 @@ describe("secrets runtime snapshot", () => { }, }), env: { - NEXTCLOUD_BOT_SECRET: "resolved-nextcloud-bot-secret", - NEXTCLOUD_API_PASSWORD: "resolved-nextcloud-api-password", + NEXTCLOUD_BOT_SECRET: "resolved-nextcloud-bot-secret", // pragma: allowlist secret + NEXTCLOUD_API_PASSWORD: "resolved-nextcloud-api-password", // pragma: allowlist secret }, agentDirs: ["/tmp/openclaw-agent-main"], loadAuthStore: () => ({ version: 1, profiles: {} }), @@ -1022,8 +1022,8 @@ describe("secrets runtime snapshot", () => { }, }), env: { - NEXTCLOUD_WORK_BOT_SECRET: "resolved-nextcloud-work-bot-secret", - NEXTCLOUD_WORK_API_PASSWORD: "resolved-nextcloud-work-api-password", + NEXTCLOUD_WORK_BOT_SECRET: "resolved-nextcloud-work-bot-secret", // pragma: allowlist secret + NEXTCLOUD_WORK_API_PASSWORD: "resolved-nextcloud-work-api-password", // pragma: allowlist secret }, agentDirs: ["/tmp/openclaw-agent-main"], loadAuthStore: () => ({ version: 1, profiles: {} }), @@ -1058,7 +1058,7 @@ describe("secrets runtime snapshot", () => { }), env: { REMOTE_GATEWAY_TOKEN: "tailscale-remote-token", - REMOTE_GATEWAY_PASSWORD: "tailscale-remote-password", + REMOTE_GATEWAY_PASSWORD: "tailscale-remote-password", // pragma: allowlist secret }, agentDirs: ["/tmp/openclaw-agent-main"], loadAuthStore: () => ({ version: 1, profiles: {} }), @@ -1931,7 +1931,7 @@ describe("secrets runtime snapshot", () => { list: [{ id: "worker" }], }, }, - env: { OPENAI_API_KEY: "sk-runtime-worker" }, + env: { OPENAI_API_KEY: "sk-runtime-worker" }, // pragma: allowlist secret }); await expect(fs.access(workerStorePath)).rejects.toMatchObject({ code: "ENOENT" }); diff --git a/src/secrets/secret-value.ts b/src/secrets/secret-value.ts index 9713451e892..9a192fede16 100644 --- a/src/secrets/secret-value.ts +++ b/src/secrets/secret-value.ts @@ -1,6 +1,6 @@ import { isNonEmptyString, isRecord } from "./shared.js"; -export type SecretExpectedResolvedValue = "string" | "string-or-object"; +export type SecretExpectedResolvedValue = "string" | "string-or-object"; // pragma: allowlist secret export function isExpectedResolvedSecretValue( value: unknown, diff --git a/src/secrets/target-registry-data.ts b/src/secrets/target-registry-data.ts index 53eb4307751..61ccb1f9b66 100644 --- a/src/secrets/target-registry-data.ts +++ b/src/secrets/target-registry-data.ts @@ -1,5 +1,8 @@ import type { SecretTargetRegistryEntry } from "./target-registry-types.js"; +const SECRET_INPUT_SHAPE = "secret_input"; // pragma: allowlist secret +const SIBLING_REF_SHAPE = "sibling_ref"; // pragma: allowlist secret + const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ { id: "auth-profiles.api_key.key", @@ -7,7 +10,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ configFile: "auth-profiles.json", pathPattern: "profiles.*.key", refPathPattern: "profiles.*.keyRef", - secretShape: "sibling_ref", + secretShape: SIBLING_REF_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -20,7 +23,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ configFile: "auth-profiles.json", pathPattern: "profiles.*.token", refPathPattern: "profiles.*.tokenRef", - secretShape: "sibling_ref", + secretShape: SIBLING_REF_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -32,7 +35,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "agents.defaults.memorySearch.remote.apiKey", configFile: "openclaw.json", pathPattern: "agents.defaults.memorySearch.remote.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -43,7 +46,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "agents.list[].memorySearch.remote.apiKey", configFile: "openclaw.json", pathPattern: "agents.list[].memorySearch.remote.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -54,7 +57,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.bluebubbles.accounts.*.password", configFile: "openclaw.json", pathPattern: "channels.bluebubbles.accounts.*.password", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -65,7 +68,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.bluebubbles.password", configFile: "openclaw.json", pathPattern: "channels.bluebubbles.password", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -76,7 +79,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.discord.accounts.*.pluralkit.token", configFile: "openclaw.json", pathPattern: "channels.discord.accounts.*.pluralkit.token", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -87,7 +90,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.discord.accounts.*.token", configFile: "openclaw.json", pathPattern: "channels.discord.accounts.*.token", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -98,7 +101,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.discord.accounts.*.voice.tts.elevenlabs.apiKey", configFile: "openclaw.json", pathPattern: "channels.discord.accounts.*.voice.tts.elevenlabs.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -109,7 +112,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.discord.accounts.*.voice.tts.openai.apiKey", configFile: "openclaw.json", pathPattern: "channels.discord.accounts.*.voice.tts.openai.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -120,7 +123,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.discord.pluralkit.token", configFile: "openclaw.json", pathPattern: "channels.discord.pluralkit.token", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -131,7 +134,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.discord.token", configFile: "openclaw.json", pathPattern: "channels.discord.token", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -142,7 +145,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.discord.voice.tts.elevenlabs.apiKey", configFile: "openclaw.json", pathPattern: "channels.discord.voice.tts.elevenlabs.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -153,7 +156,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.discord.voice.tts.openai.apiKey", configFile: "openclaw.json", pathPattern: "channels.discord.voice.tts.openai.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -164,7 +167,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.feishu.accounts.*.appSecret", configFile: "openclaw.json", pathPattern: "channels.feishu.accounts.*.appSecret", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -175,7 +178,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.feishu.accounts.*.verificationToken", configFile: "openclaw.json", pathPattern: "channels.feishu.accounts.*.verificationToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -186,7 +189,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.feishu.appSecret", configFile: "openclaw.json", pathPattern: "channels.feishu.appSecret", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -197,7 +200,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.feishu.verificationToken", configFile: "openclaw.json", pathPattern: "channels.feishu.verificationToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -210,7 +213,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ configFile: "openclaw.json", pathPattern: "channels.googlechat.accounts.*.serviceAccount", refPathPattern: "channels.googlechat.accounts.*.serviceAccountRef", - secretShape: "sibling_ref", + secretShape: SIBLING_REF_SHAPE, expectedResolvedValue: "string-or-object", includeInPlan: true, includeInConfigure: true, @@ -223,7 +226,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ configFile: "openclaw.json", pathPattern: "channels.googlechat.serviceAccount", refPathPattern: "channels.googlechat.serviceAccountRef", - secretShape: "sibling_ref", + secretShape: SIBLING_REF_SHAPE, expectedResolvedValue: "string-or-object", includeInPlan: true, includeInConfigure: true, @@ -234,7 +237,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.irc.accounts.*.nickserv.password", configFile: "openclaw.json", pathPattern: "channels.irc.accounts.*.nickserv.password", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -245,7 +248,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.irc.accounts.*.password", configFile: "openclaw.json", pathPattern: "channels.irc.accounts.*.password", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -256,7 +259,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.irc.nickserv.password", configFile: "openclaw.json", pathPattern: "channels.irc.nickserv.password", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -267,7 +270,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.irc.password", configFile: "openclaw.json", pathPattern: "channels.irc.password", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -278,7 +281,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.mattermost.accounts.*.botToken", configFile: "openclaw.json", pathPattern: "channels.mattermost.accounts.*.botToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -289,7 +292,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.mattermost.botToken", configFile: "openclaw.json", pathPattern: "channels.mattermost.botToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -300,7 +303,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.matrix.accounts.*.password", configFile: "openclaw.json", pathPattern: "channels.matrix.accounts.*.password", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -311,7 +314,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.matrix.password", configFile: "openclaw.json", pathPattern: "channels.matrix.password", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -322,7 +325,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.msteams.appPassword", configFile: "openclaw.json", pathPattern: "channels.msteams.appPassword", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -333,7 +336,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.nextcloud-talk.accounts.*.apiPassword", configFile: "openclaw.json", pathPattern: "channels.nextcloud-talk.accounts.*.apiPassword", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -344,7 +347,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.nextcloud-talk.accounts.*.botSecret", configFile: "openclaw.json", pathPattern: "channels.nextcloud-talk.accounts.*.botSecret", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -355,7 +358,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.nextcloud-talk.apiPassword", configFile: "openclaw.json", pathPattern: "channels.nextcloud-talk.apiPassword", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -366,7 +369,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.nextcloud-talk.botSecret", configFile: "openclaw.json", pathPattern: "channels.nextcloud-talk.botSecret", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -377,7 +380,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.slack.accounts.*.appToken", configFile: "openclaw.json", pathPattern: "channels.slack.accounts.*.appToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -388,7 +391,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.slack.accounts.*.botToken", configFile: "openclaw.json", pathPattern: "channels.slack.accounts.*.botToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -399,7 +402,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.slack.accounts.*.signingSecret", configFile: "openclaw.json", pathPattern: "channels.slack.accounts.*.signingSecret", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -410,7 +413,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.slack.accounts.*.userToken", configFile: "openclaw.json", pathPattern: "channels.slack.accounts.*.userToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -421,7 +424,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.slack.appToken", configFile: "openclaw.json", pathPattern: "channels.slack.appToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -432,7 +435,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.slack.botToken", configFile: "openclaw.json", pathPattern: "channels.slack.botToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -443,7 +446,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.slack.signingSecret", configFile: "openclaw.json", pathPattern: "channels.slack.signingSecret", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -454,7 +457,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.slack.userToken", configFile: "openclaw.json", pathPattern: "channels.slack.userToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -465,7 +468,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.telegram.accounts.*.botToken", configFile: "openclaw.json", pathPattern: "channels.telegram.accounts.*.botToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -476,7 +479,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.telegram.accounts.*.webhookSecret", configFile: "openclaw.json", pathPattern: "channels.telegram.accounts.*.webhookSecret", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -487,7 +490,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.telegram.botToken", configFile: "openclaw.json", pathPattern: "channels.telegram.botToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -498,7 +501,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.telegram.webhookSecret", configFile: "openclaw.json", pathPattern: "channels.telegram.webhookSecret", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -509,7 +512,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.zalo.accounts.*.botToken", configFile: "openclaw.json", pathPattern: "channels.zalo.accounts.*.botToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -520,7 +523,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.zalo.accounts.*.webhookSecret", configFile: "openclaw.json", pathPattern: "channels.zalo.accounts.*.webhookSecret", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -531,7 +534,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.zalo.botToken", configFile: "openclaw.json", pathPattern: "channels.zalo.botToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -542,7 +545,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "channels.zalo.webhookSecret", configFile: "openclaw.json", pathPattern: "channels.zalo.webhookSecret", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -553,7 +556,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "cron.webhookToken", configFile: "openclaw.json", pathPattern: "cron.webhookToken", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -564,7 +567,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "gateway.auth.token", configFile: "openclaw.json", pathPattern: "gateway.auth.token", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -575,7 +578,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "gateway.auth.password", configFile: "openclaw.json", pathPattern: "gateway.auth.password", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -586,7 +589,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "gateway.remote.password", configFile: "openclaw.json", pathPattern: "gateway.remote.password", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -597,7 +600,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "gateway.remote.token", configFile: "openclaw.json", pathPattern: "gateway.remote.token", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -608,7 +611,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "messages.tts.elevenlabs.apiKey", configFile: "openclaw.json", pathPattern: "messages.tts.elevenlabs.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -619,7 +622,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "messages.tts.openai.apiKey", configFile: "openclaw.json", pathPattern: "messages.tts.openai.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -631,7 +634,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetTypeAliases: ["models.providers.*.apiKey"], configFile: "openclaw.json", pathPattern: "models.providers.*.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -645,7 +648,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetTypeAliases: ["skills.entries.*.apiKey"], configFile: "openclaw.json", pathPattern: "skills.entries.*.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -656,7 +659,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "talk.apiKey", configFile: "openclaw.json", pathPattern: "talk.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -667,7 +670,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "talk.providers.*.apiKey", configFile: "openclaw.json", pathPattern: "talk.providers.*.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -678,7 +681,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "tools.web.search.apiKey", configFile: "openclaw.json", pathPattern: "tools.web.search.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -689,7 +692,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "tools.web.search.gemini.apiKey", configFile: "openclaw.json", pathPattern: "tools.web.search.gemini.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -700,7 +703,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "tools.web.search.grok.apiKey", configFile: "openclaw.json", pathPattern: "tools.web.search.grok.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -711,7 +714,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "tools.web.search.kimi.apiKey", configFile: "openclaw.json", pathPattern: "tools.web.search.kimi.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, @@ -722,7 +725,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [ targetType: "tools.web.search.perplexity.apiKey", configFile: "openclaw.json", pathPattern: "tools.web.search.perplexity.apiKey", - secretShape: "secret_input", + secretShape: SECRET_INPUT_SHAPE, expectedResolvedValue: "string", includeInPlan: true, includeInConfigure: true, diff --git a/src/secrets/target-registry-pattern.test.ts b/src/secrets/target-registry-pattern.test.ts index fe8668c4d1d..4739ca5776d 100644 --- a/src/secrets/target-registry-pattern.test.ts +++ b/src/secrets/target-registry-pattern.test.ts @@ -49,8 +49,8 @@ describe("target registry pattern helpers", () => { }, talk: { providers: { - openai: { apiKey: "oa" }, - anthropic: { apiKey: "an" }, + openai: { apiKey: "oa" }, // pragma: allowlist secret + anthropic: { apiKey: "an" }, // pragma: allowlist secret }, }, }; diff --git a/src/secrets/target-registry-pattern.ts b/src/secrets/target-registry-pattern.ts index d6c0970efaf..0504c3023e0 100644 --- a/src/secrets/target-registry-pattern.ts +++ b/src/secrets/target-registry-pattern.ts @@ -47,7 +47,8 @@ export function compileTargetRegistryEntry( const pathDynamicTokenCount = countDynamicPatternTokens(pathTokens); const refPathTokens = entry.refPathPattern ? parsePathPattern(entry.refPathPattern) : undefined; const refPathDynamicTokenCount = refPathTokens ? countDynamicPatternTokens(refPathTokens) : 0; - if (entry.secretShape === "sibling_ref" && !refPathTokens) { + const requiresSiblingRefPath = entry.secretShape === "sibling_ref"; // pragma: allowlist secret + if (requiresSiblingRefPath && !refPathTokens) { throw new Error(`Missing refPathPattern for sibling_ref target: ${entry.id}`); } if (refPathTokens && refPathDynamicTokenCount !== pathDynamicTokenCount) { diff --git a/src/secrets/target-registry-types.ts b/src/secrets/target-registry-types.ts index 0990f72a30d..e8c31d1c251 100644 --- a/src/secrets/target-registry-types.ts +++ b/src/secrets/target-registry-types.ts @@ -1,6 +1,6 @@ -export type SecretTargetConfigFile = "openclaw.json" | "auth-profiles.json"; -export type SecretTargetShape = "secret_input" | "sibling_ref"; -export type SecretTargetExpected = "string" | "string-or-object"; +export type SecretTargetConfigFile = "openclaw.json" | "auth-profiles.json"; // pragma: allowlist secret +export type SecretTargetShape = "secret_input" | "sibling_ref"; // pragma: allowlist secret +export type SecretTargetExpected = "string" | "string-or-object"; // pragma: allowlist secret export type AuthProfileType = "api_key" | "token"; export type SecretTargetRegistryEntry = { diff --git a/src/tui/gateway-chat.test.ts b/src/tui/gateway-chat.test.ts index 58d5433f07f..2113abc7eb5 100644 --- a/src/tui/gateway-chat.test.ts +++ b/src/tui/gateway-chat.test.ts @@ -118,7 +118,7 @@ describe("resolveGatewayConnection", () => { gateway: { mode: "local", auth: { - password: "config-password", + password: "config-password", // pragma: allowlist secret }, }, }); @@ -134,7 +134,7 @@ describe("resolveGatewayConnection", () => { mode: "local", auth: { token: "config-token", - password: "config-password", + password: "config-password", // pragma: allowlist secret }, }, }); @@ -180,13 +180,15 @@ describe("resolveGatewayConnection", () => { loadConfig.mockReturnValue({ gateway: { mode: "remote", - remote: { url: "wss://remote.example/ws", token: "remote-token", password: "remote-pass" }, + remote: { url: "wss://remote.example/ws", token: "remote-token", password: "remote-pass" }, // pragma: allowlist secret }, }); - await withEnvAsync({ OPENCLAW_GATEWAY_PASSWORD: "env-pass" }, async () => { + const gatewayPasswordEnv = "OPENCLAW_GATEWAY_PASSWORD"; // pragma: allowlist secret + const gatewayPassword = "env-pass"; // pragma: allowlist secret + await withEnvAsync({ [gatewayPasswordEnv]: gatewayPassword }, async () => { const result = await resolveGatewayConnection({}); - expect(result.password).toBe("env-pass"); + expect(result.password).toBe(gatewayPassword); }); }); @@ -263,12 +265,12 @@ describe("resolveGatewayConnection", () => { const tokenExecProgram = [ "const fs=require('node:fs');", `fs.writeFileSync(${JSON.stringify(tokenMarker)},'1');`, - "process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { TOKEN_SECRET: 'token-from-exec' } }));", + "process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { TOKEN_SECRET: 'token-from-exec' } }));", // pragma: allowlist secret ].join(""); const passwordExecProgram = [ "const fs=require('node:fs');", `fs.writeFileSync(${JSON.stringify(passwordMarker)},'1');`, - "process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { PASSWORD_SECRET: 'password-from-exec' } }));", + "process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { PASSWORD_SECRET: 'password-from-exec' } }));", // pragma: allowlist secret ].join(""); loadConfig.mockReturnValue({ @@ -316,12 +318,12 @@ describe("resolveGatewayConnection", () => { const tokenExecProgram = [ "const fs=require('node:fs');", `fs.writeFileSync(${JSON.stringify(tokenMarker)},'1');`, - "process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { TOKEN_SECRET: 'token-from-exec' } }));", + "process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { TOKEN_SECRET: 'token-from-exec' } }));", // pragma: allowlist secret ].join(""); const passwordExecProgram = [ "const fs=require('node:fs');", `fs.writeFileSync(${JSON.stringify(passwordMarker)},'1');`, - "process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { PASSWORD_SECRET: 'password-from-exec' } }));", + "process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { PASSWORD_SECRET: 'password-from-exec' } }));", // pragma: allowlist secret ].join(""); loadConfig.mockReturnValue({ diff --git a/src/tui/tui-formatters.test.ts b/src/tui/tui-formatters.test.ts index c4dfa26bb14..3ceb0c56570 100644 --- a/src/tui/tui-formatters.test.ts +++ b/src/tui/tui-formatters.test.ts @@ -250,14 +250,14 @@ describe("sanitizeRenderableText", () => { }); it("preserves long credential-like mixed alnum tokens for copy safety", () => { - const input = "e3b19c3b87bcf364b23eebb2c276e96ec478956ba1d84c93"; + const input = "e3b19c3b87bcf364b23eebb2c276e96ec478956ba1d84c93"; // pragma: allowlist secret const sanitized = sanitizeRenderableText(input); expect(sanitized).toBe(input); }); it("preserves quoted credential-like mixed alnum tokens for copy safety", () => { - const input = "'e3b19c3b87bcf364b23eebb2c276e96ec478956ba1d84c93'"; + const input = "'e3b19c3b87bcf364b23eebb2c276e96ec478956ba1d84c93'"; // pragma: allowlist secret const sanitized = sanitizeRenderableText(input); expect(sanitized).toBe(input); diff --git a/src/utils/mask-api-key.test.ts b/src/utils/mask-api-key.test.ts index 3620dc01b34..023576a4eeb 100644 --- a/src/utils/mask-api-key.test.ts +++ b/src/utils/mask-api-key.test.ts @@ -15,6 +15,6 @@ describe("maskApiKey", () => { }); it("masks long values with first and last 8 chars", () => { - expect(maskApiKey("1234567890abcdefghijklmnop")).toBe("12345678...ijklmnop"); + expect(maskApiKey("1234567890abcdefghijklmnop")).toBe("12345678...ijklmnop"); // pragma: allowlist secret }); }); diff --git a/src/wizard/onboarding.finalize.test.ts b/src/wizard/onboarding.finalize.test.ts index ea7f6ce23bd..8d720c2f594 100644 --- a/src/wizard/onboarding.finalize.test.ts +++ b/src/wizard/onboarding.finalize.test.ts @@ -113,7 +113,7 @@ describe("finalizeOnboardingWizard", () => { it("resolves gateway password SecretRef for probe and TUI", async () => { const previous = process.env.OPENCLAW_GATEWAY_PASSWORD; - process.env.OPENCLAW_GATEWAY_PASSWORD = "resolved-gateway-password"; + process.env.OPENCLAW_GATEWAY_PASSWORD = "resolved-gateway-password"; // pragma: allowlist secret const select = vi.fn(async (params: { message: string }) => { if (params.message === "How do you want to hatch your bot?") { return "tui"; @@ -179,13 +179,13 @@ describe("finalizeOnboardingWizard", () => { expect(probeGatewayReachable).toHaveBeenCalledWith( expect.objectContaining({ url: "ws://127.0.0.1:18789", - password: "resolved-gateway-password", + password: "resolved-gateway-password", // pragma: allowlist secret }), ); expect(runTui).toHaveBeenCalledWith( expect.objectContaining({ url: "ws://127.0.0.1:18789", - password: "resolved-gateway-password", + password: "resolved-gateway-password", // pragma: allowlist secret }), ); }); diff --git a/src/wizard/onboarding.gateway-config.ts b/src/wizard/onboarding.gateway-config.ts index a1f5dfee624..c6d9111c3e4 100644 --- a/src/wizard/onboarding.gateway-config.ts +++ b/src/wizard/onboarding.gateway-config.ts @@ -165,7 +165,7 @@ export async function configureGatewayForOnboarding( defaults: nextConfig.secrets?.defaults, }).ref; const tokenMode = - flow === "quickstart" && opts.secretInputMode !== "ref" + flow === "quickstart" && opts.secretInputMode !== "ref" // pragma: allowlist secret ? quickstartTokenRef ? "ref" : "plaintext" diff --git a/ui/src/i18n/locales/de.ts b/ui/src/i18n/locales/de.ts index 633bdeb12d8..f45ffc3f4c0 100644 --- a/ui/src/i18n/locales/de.ts +++ b/ui/src/i18n/locales/de.ts @@ -58,7 +58,7 @@ export const de: TranslationMap = { subtitle: "Wo sich das Dashboard verbindet und wie es sich authentifiziert.", wsUrl: "WebSocket-URL", token: "Gateway-Token", - password: "Passwort (nicht gespeichert)", + password: "Passwort (nicht gespeichert)", // pragma: allowlist secret sessionKey: "Standard-Sitzungsschlüssel", language: "Sprache", connectHint: "Klicken Sie auf Verbinden, um Verbindungsänderungen anzuwenden.", diff --git a/ui/src/i18n/locales/es.ts b/ui/src/i18n/locales/es.ts index 0a77e447a0f..a96ee7ad2d7 100644 --- a/ui/src/i18n/locales/es.ts +++ b/ui/src/i18n/locales/es.ts @@ -58,7 +58,7 @@ export const es: TranslationMap = { subtitle: "Dónde se conecta el panel y cómo se autentica.", wsUrl: "URL de WebSocket", token: "Token de la puerta de enlace", - password: "Contraseña (no se guarda)", + password: "Contraseña (no se guarda)", // pragma: allowlist secret sessionKey: "Clave de sesión predeterminada", language: "Idioma", connectHint: "Haz clic en Conectar para aplicar los cambios de conexión.",