Secrets: add inline allowlist review set (#38314)

* Secrets: add inline allowlist review set

* Secrets: narrow detect-secrets file exclusions

* Secrets: exclude Docker fingerprint false positive

* Secrets: allowlist test and docs false positives

* Secrets: refresh baseline after allowlist updates

* Secrets: fix gateway chat fixture pragma

* Secrets: format pre-commit config

* Android: keep talk mode fixture JSON valid

* Feishu: rely on client timeout injection

* Secrets: allowlist provider auth test fixtures

* Secrets: allowlist onboard search fixtures

* Secrets: allowlist onboard mode fixture

* Secrets: allowlist gateway auth mode fixture

* Secrets: allowlist APNS wake test key

* Secrets: allowlist gateway reload fixtures

* Secrets: allowlist moonshot video fixture

* Secrets: allowlist auto audio fixture

* Secrets: allowlist tiny audio fixture

* Secrets: allowlist embeddings fixtures

* Secrets: allowlist resolve fixtures

* Secrets: allowlist target registry pattern fixtures

* Secrets: allowlist gateway chat env fixture

* Secrets: refresh baseline after fixture allowlists

* Secrets: reapply gateway chat env allowlist

* Secrets: reapply gateway chat env allowlist

* Secrets: stabilize gateway chat env allowlist

* Secrets: allowlist runtime snapshot save fixture

* Secrets: allowlist oauth profile fixtures

* Secrets: allowlist compaction identifier fixture

* Secrets: allowlist model auth fixture

* Secrets: allowlist model status fixtures

* Secrets: allowlist custom onboarding fixture

* Secrets: allowlist mattermost token summary fixtures

* Secrets: allowlist gateway auth suite fixtures

* Secrets: allowlist channel summary fixture

* Secrets: allowlist provider usage auth fixtures

* Secrets: allowlist media proxy fixture

* Secrets: allowlist secrets audit fixtures

* Secrets: refresh baseline after final fixture allowlists

* Feishu: prefer explicit client timeout

* Feishu: test direct timeout precedence
This commit is contained in:
Vincent Koc
2026-03-06 19:35:26 -05:00
committed by GitHub
parent 3070fafec1
commit 42e3d8d693
80 changed files with 363 additions and 317 deletions

View File

@@ -7,10 +7,6 @@
[exclude-files] [exclude-files]
# pnpm lockfiles contain lots of high-entropy package integrity blobs. # pnpm lockfiles contain lots of high-entropy package integrity blobs.
pattern = (^|/)pnpm-lock\.yaml$ pattern = (^|/)pnpm-lock\.yaml$
# Generated output and vendored assets.
pattern = (^|/)(dist|vendor)/
# Local config file with allowlist patterns.
pattern = (^|/)\.detect-secrets\.cfg$
[exclude-lines] [exclude-lines]
# Fastlane checks for private key marker; not a real key. # Fastlane checks for private key marker; not a real key.
@@ -28,3 +24,5 @@ pattern = "talk\.apiKey"
pattern = === "string" pattern = === "string"
# specific optional-chaining password check that didn't match the line above. # specific optional-chaining password check that didn't match the line above.
pattern = typeof remote\?\.password === "string" pattern = typeof remote\?\.password === "string"
# Docker apt signing key fingerprint constant; not a secret.
pattern = OPENCLAW_DOCKER_GPG_FINGERPRINT=

View File

@@ -30,7 +30,7 @@ repos:
- --baseline - --baseline
- .secrets.baseline - .secrets.baseline
- --exclude-files - --exclude-files
- '(^|/)(dist/|vendor/|pnpm-lock\.yaml$|\.detect-secrets\.cfg$)' - '(^|/)pnpm-lock\.yaml$'
- --exclude-lines - --exclude-lines
- 'key_content\.include\?\("BEGIN PRIVATE KEY"\)' - 'key_content\.include\?\("BEGIN PRIVATE KEY"\)'
- --exclude-lines - --exclude-lines
@@ -47,6 +47,8 @@ repos:
- '=== "string"' - '=== "string"'
- --exclude-lines - --exclude-lines
- 'typeof remote\?\.password === "string"' - 'typeof remote\?\.password === "string"'
- --exclude-lines
- "OPENCLAW_DOCKER_GPG_FINGERPRINT="
# Shell script linting # Shell script linting
- repo: https://github.com/koalaman/shellcheck-precommit - repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.11.0 rev: v0.11.0

View File

@@ -141,7 +141,8 @@
"\"gateway\\.auth\\.password\"", "\"gateway\\.auth\\.password\"",
"\"talk\\.apiKey\"", "\"talk\\.apiKey\"",
"=== \"string\"", "=== \"string\"",
"typeof remote\\?\\.password === \"string\"" "typeof remote\\?\\.password === \"string\"",
"OPENCLAW_DOCKER_GPG_FINGERPRINT="
] ]
} }
], ],
@@ -152,14 +153,14 @@
"filename": ".detect-secrets.cfg", "filename": ".detect-secrets.cfg",
"hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9", "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9",
"is_verified": false, "is_verified": false,
"line_number": 17 "line_number": 13
}, },
{ {
"type": "Secret Keyword", "type": "Secret Keyword",
"filename": ".detect-secrets.cfg", "filename": ".detect-secrets.cfg",
"hashed_secret": "fe88fceb47e040ba1bfafa4ac639366188df2f6d", "hashed_secret": "fe88fceb47e040ba1bfafa4ac639366188df2f6d",
"is_verified": false, "is_verified": false,
"line_number": 19 "line_number": 15
} }
], ],
"appcast.xml": [ "appcast.xml": [
@@ -12387,21 +12388,14 @@
"filename": "src/config/schema.help.ts", "filename": "src/config/schema.help.ts",
"hashed_secret": "9f4cda226d3868676ac7f86f59e4190eb94bd208", "hashed_secret": "9f4cda226d3868676ac7f86f59e4190eb94bd208",
"is_verified": false, "is_verified": false,
"line_number": 109 "line_number": 647
}, },
{ {
"type": "Secret Keyword", "type": "Secret Keyword",
"filename": "src/config/schema.help.ts", "filename": "src/config/schema.help.ts",
"hashed_secret": "01822c8bbf6a8b136944b14182cb885100ec2eae", "hashed_secret": "01822c8bbf6a8b136944b14182cb885100ec2eae",
"is_verified": false, "is_verified": false,
"line_number": 130 "line_number": 678
},
{
"type": "Secret Keyword",
"filename": "src/config/schema.help.ts",
"hashed_secret": "bb7dfd9746e660e4a4374951ec5938ef0e343255",
"is_verified": false,
"line_number": 187
} }
], ],
"src/config/schema.irc.ts": [ "src/config/schema.irc.ts": [
@@ -12720,21 +12714,21 @@
"filename": "src/infra/provider-usage.auth.normalizes-keys.test.ts", "filename": "src/infra/provider-usage.auth.normalizes-keys.test.ts",
"hashed_secret": "45c7365e3b542cdb4fae6ec10c2ff149224d7656", "hashed_secret": "45c7365e3b542cdb4fae6ec10c2ff149224d7656",
"is_verified": false, "is_verified": false,
"line_number": 80 "line_number": 123
}, },
{ {
"type": "Secret Keyword", "type": "Secret Keyword",
"filename": "src/infra/provider-usage.auth.normalizes-keys.test.ts", "filename": "src/infra/provider-usage.auth.normalizes-keys.test.ts",
"hashed_secret": "b67074884ab7ef7c7a8cd6a3da9565d96c792248", "hashed_secret": "b67074884ab7ef7c7a8cd6a3da9565d96c792248",
"is_verified": false, "is_verified": false,
"line_number": 81 "line_number": 124
}, },
{ {
"type": "Secret Keyword", "type": "Secret Keyword",
"filename": "src/infra/provider-usage.auth.normalizes-keys.test.ts", "filename": "src/infra/provider-usage.auth.normalizes-keys.test.ts",
"hashed_secret": "d4d8027e64f9cf4180d3aecfe31ea409368022ee", "hashed_secret": "d4d8027e64f9cf4180d3aecfe31ea409368022ee",
"is_verified": false, "is_verified": false,
"line_number": 82 "line_number": 125
} }
], ],
"src/infra/shell-env.test.ts": [ "src/infra/shell-env.test.ts": [
@@ -12900,7 +12894,7 @@
"filename": "src/media-understanding/runner.auto-audio.test.ts", "filename": "src/media-understanding/runner.auto-audio.test.ts",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f", "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false, "is_verified": false,
"line_number": 40 "line_number": 23
} }
], ],
"src/media-understanding/runner.deepgram.test.ts": [ "src/media-understanding/runner.deepgram.test.ts": [
@@ -12934,21 +12928,21 @@
"filename": "src/memory/embeddings.test.ts", "filename": "src/memory/embeddings.test.ts",
"hashed_secret": "a47110e348a3063541fb1f1f640d635d457181a0", "hashed_secret": "a47110e348a3063541fb1f1f640d635d457181a0",
"is_verified": false, "is_verified": false,
"line_number": 45 "line_number": 47
}, },
{ {
"type": "Secret Keyword", "type": "Secret Keyword",
"filename": "src/memory/embeddings.test.ts", "filename": "src/memory/embeddings.test.ts",
"hashed_secret": "c734e47630dda71619c696d88381f06f7511bd78", "hashed_secret": "c734e47630dda71619c696d88381f06f7511bd78",
"is_verified": false, "is_verified": false,
"line_number": 160 "line_number": 195
}, },
{ {
"type": "Secret Keyword", "type": "Secret Keyword",
"filename": "src/memory/embeddings.test.ts", "filename": "src/memory/embeddings.test.ts",
"hashed_secret": "56e1d57b8db262b08bc73c60ed08d8c92e59503f", "hashed_secret": "56e1d57b8db262b08bc73c60ed08d8c92e59503f",
"is_verified": false, "is_verified": false,
"line_number": 189 "line_number": 291
} }
], ],
"src/pairing/pairing-store.ts": [ "src/pairing/pairing-store.ts": [
@@ -13060,7 +13054,7 @@
"filename": "src/tui/gateway-chat.test.ts", "filename": "src/tui/gateway-chat.test.ts",
"hashed_secret": "6255675480f681df08c1704b7b3cd2c49917f0e2", "hashed_secret": "6255675480f681df08c1704b7b3cd2c49917f0e2",
"is_verified": false, "is_verified": false,
"line_number": 85 "line_number": 60
} }
], ],
"src/web/login.test.ts": [ "src/web/login.test.ts": [
@@ -13100,5 +13094,5 @@
} }
] ]
}, },
"generated_at": "2026-02-17T13:34:38Z" "generated_at": "2026-03-07T00:11:03Z"
} }

View File

@@ -219,7 +219,7 @@
</ul> </ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p> <p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description> ]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.3.2/OpenClaw-2026.3.2.zip" length="23181513" type="application/octet-stream" sparkle:edSignature="THMgkcoMgz2vv5zse3Po3K7l3Or2RhBKurXZIi8iYVXN76yJy1YXAY6kXi6ovD+dbYn68JKYDIKA1Ya78bO7BQ=="/> <enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.3.2/OpenClaw-2026.3.2.zip" length="23181513" type="application/octet-stream" sparkle:edSignature="THMgkcoMgz2vv5zse3Po3K7l3Or2RhBKurXZIi8iYVXN76yJy1YXAY6kXi6ovD+dbYn68JKYDIKA1Ya78bO7BQ=="/> <!-- pragma: allowlist secret -->
</item> </item>
<item> <item>
<title>2026.3.1</title> <title>2026.3.1</title>
@@ -357,7 +357,7 @@
</ul> </ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p> <p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description> ]]></description>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.3.1/OpenClaw-2026.3.1.zip" length="12804155" type="application/octet-stream" sparkle:edSignature="TF1otD4Vk3pG0iViX7mvix5DQEgAsk4JkSFvH7opjf9aawV16f29SUa2wRmiCFU6HEgyNgnGI/078O+A27eXCA=="/> <enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.3.1/OpenClaw-2026.3.1.zip" length="12804155" type="application/octet-stream" sparkle:edSignature="TF1otD4Vk3pG0iViX7mvix5DQEgAsk4JkSFvH7opjf9aawV16f29SUa2wRmiCFU6HEgyNgnGI/078O+A27eXCA=="/> <!-- pragma: allowlist secret -->
</item> </item>
</channel> </channel>
</rss> </rss>

View File

@@ -1,8 +1,10 @@
package ai.openclaw.android.voice package ai.openclaw.android.voice
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.put
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
@@ -38,16 +40,12 @@ class TalkModeConfigParsingTest {
@Test @Test
fun fallsBackToLegacyTalkFieldsWhenNormalizedPayloadMissing() { fun fallsBackToLegacyTalkFieldsWhenNormalizedPayloadMissing() {
val legacyApiKey = "legacy-key" // pragma: allowlist secret
val talk = val talk =
json.parseToJsonElement( buildJsonObject {
""" put("voiceId", "voice-legacy")
{ put("apiKey", legacyApiKey) // pragma: allowlist secret
"voiceId": "voice-legacy", }
"apiKey": "legacy-key"
}
""".trimIndent(),
)
.jsonObject
val selection = TalkModeManager.selectTalkProviderConfig(talk) val selection = TalkModeManager.selectTalkProviderConfig(talk)
assertNotNull(selection) assertNotNull(selection)

View File

@@ -26,7 +26,7 @@ enum GatewaySettingsStore {
private static let preferredGatewayStableIDAccount = "preferredStableID" private static let preferredGatewayStableIDAccount = "preferredStableID"
private static let lastDiscoveredGatewayStableIDAccount = "lastDiscoveredStableID" private static let lastDiscoveredGatewayStableIDAccount = "lastDiscoveredStableID"
private static let lastGatewayConnectionAccount = "lastConnection" private static let lastGatewayConnectionAccount = "lastConnection"
private static let talkProviderApiKeyAccountPrefix = "provider.apiKey." private static let talkProviderApiKeyAccountPrefix = "provider.apiKey." // pragma: allowlist secret
static func bootstrapPersistence() { static func bootstrapPersistence() {
self.ensureStableInstanceID() self.ensureStableInstanceID()

View File

@@ -23,7 +23,7 @@ import Testing
@Test func ignoresLegacyTalkFieldsWhenNormalizedPayloadMissing() { @Test func ignoresLegacyTalkFieldsWhenNormalizedPayloadMissing() {
let talk: [String: Any] = [ let talk: [String: Any] = [
"voiceId": "voice-legacy", "voiceId": "voice-legacy",
"apiKey": "legacy-key", "apiKey": "legacy-key", // pragma: allowlist secret
] ]
let selection = TalkModeManager.selectTalkProviderConfig(talk) let selection = TalkModeManager.selectTalkProviderConfig(talk)

View File

@@ -804,7 +804,7 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \
```yaml ```yaml
channels: channels:
telegram: telegram:
proxy: socks5://user:pass@proxy-host:1080 proxy: socks5://<user>:<password>@proxy-host:1080
``` ```
- Node 22+ defaults to `autoSelectFamily=true` (except WSL2) and `dnsResultOrder=ipv4first`. - Node 22+ defaults to `autoSelectFamily=true` (except WSL2) and `dnsResultOrder=ipv4first`.

View File

@@ -179,8 +179,8 @@ Request payload (stdin):
Response payload (stdout): Response payload (stdout):
```json ```jsonc
{ "protocolVersion": 1, "values": { "providers/openai/apiKey": "sk-..." } } { "protocolVersion": 1, "values": { "providers/openai/apiKey": "<openai-api-key>" } } // pragma: allowlist secret
``` ```
Optional per-id errors: Optional per-id errors:

View File

@@ -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: On lower-power Pi hosts, enable Node's module compile cache so repeated CLI runs are faster:
```bash ```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 export NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache
mkdir -p /var/tmp/openclaw-compile-cache mkdir -p /var/tmp/openclaw-compile-cache
export OPENCLAW_NO_RESPAWN=1 export OPENCLAW_NO_RESPAWN=1

View File

@@ -25,14 +25,14 @@ openclaw onboard --kilocode-api-key <key>
Or set the environment variable: Or set the environment variable:
```bash ```bash
export KILOCODE_API_KEY="your-api-key" export KILOCODE_API_KEY="<your-kilocode-api-key>" # pragma: allowlist secret
``` ```
## Config snippet ## Config snippet
```json5 ```json5
{ {
env: { KILOCODE_API_KEY: "sk-..." }, env: { KILOCODE_API_KEY: "<your-kilocode-api-key>" }, // pragma: allowlist secret
agents: { agents: {
defaults: { defaults: {
model: { primary: "kilocode/anthropic/claude-opus-4.6" }, model: { primary: "kilocode/anthropic/claude-opus-4.6" },

View File

@@ -5,7 +5,7 @@ describe("BlueBubblesConfigSchema", () => {
it("accepts account config when serverUrl and password are both set", () => { it("accepts account config when serverUrl and password are both set", () => {
const parsed = BlueBubblesConfigSchema.safeParse({ const parsed = BlueBubblesConfigSchema.safeParse({
serverUrl: "http://localhost:1234", serverUrl: "http://localhost:1234",
password: "secret", password: "secret", // pragma: allowlist secret
}); });
expect(parsed.success).toBe(true); expect(parsed.success).toBe(true);
}); });

View File

@@ -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"; process.env[FEISHU_HTTP_TIMEOUT_ENV_VAR] = "60000";
createFeishuClient({ 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<typeof vi.fn>).mock.calls;
const lastCall = calls[calls.length - 1][0] as {
httpInstance: { get: (...args: unknown[]) => Promise<unknown> };
};
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 () => { it("clamps env timeout override to max bound", async () => {
process.env[FEISHU_HTTP_TIMEOUT_ENV_VAR] = String(FEISHU_HTTP_TIMEOUT_MAX_MS + 123_456); process.env[FEISHU_HTTP_TIMEOUT_ENV_VAR] = String(FEISHU_HTTP_TIMEOUT_MAX_MS + 123_456);

View File

@@ -79,6 +79,15 @@ function resolveConfiguredHttpTimeoutMs(creds: FeishuClientCredentials): number
return Math.min(Math.max(rounded, 1), FEISHU_HTTP_TIMEOUT_MAX_MS); 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]; const envRaw = process.env[FEISHU_HTTP_TIMEOUT_ENV_VAR];
if (envRaw) { if (envRaw) {
const envValue = Number(envRaw); const envValue = Number(envRaw);
@@ -88,8 +97,7 @@ function resolveConfiguredHttpTimeoutMs(creds: FeishuClientCredentials): number
} }
const fromConfig = creds.config?.httpTimeoutMs; const fromConfig = creds.config?.httpTimeoutMs;
const fromDirectField = creds.httpTimeoutMs; const timeout = fromConfig;
const timeout = fromDirectField ?? fromConfig;
if (typeof timeout !== "number" || !Number.isFinite(timeout) || timeout <= 0) { if (typeof timeout !== "number" || !Number.isFinite(timeout) || timeout <= 0) {
return FEISHU_HTTP_TIMEOUT_MS; return FEISHU_HTTP_TIMEOUT_MS;
} }

View File

@@ -77,7 +77,7 @@ function buildDebounceAccount(): ResolvedFeishuAccount {
enabled: true, enabled: true,
configured: true, configured: true,
appId: "cli_test", appId: "cli_test",
appSecret: "secret_test", appSecret: "secret_test", // pragma: allowlist secret
domain: "feishu", domain: "feishu",
config: { config: {
enabled: true, enabled: true,

View File

@@ -41,7 +41,7 @@ function buildMultiAccountWebsocketConfig(accountIds: string[]): ClawdbotConfig
{ {
enabled: true, enabled: true,
appId: `cli_${accountId}`, appId: `cli_${accountId}`,
appSecret: `secret_${accountId}`, appSecret: `secret_${accountId}`, // pragma: allowlist secret
connectionMode: "websocket", connectionMode: "websocket",
}, },
]), ]),

View File

@@ -45,7 +45,7 @@ describe("nextcloud-talk inbound authz", () => {
enabled: true, enabled: true,
baseUrl: "", baseUrl: "",
secret: "", secret: "",
secretSource: "none", secretSource: "none", // pragma: allowlist secret
config: { config: {
dmPolicy: "pairing", dmPolicy: "pairing",
allowFrom: [], allowFrom: [],

View File

@@ -16,7 +16,7 @@ export function createSignedCreateMessageRequest(params?: { backend?: string })
const body = JSON.stringify(payload); const body = JSON.stringify(payload);
const { random, signature } = generateNextcloudTalkSignature({ const { random, signature } = generateNextcloudTalkSignature({
body, body,
secret: "nextcloud-secret", secret: "nextcloud-secret", // pragma: allowlist secret
}); });
return { return {
body, body,

View File

@@ -130,7 +130,7 @@ describe("ensureAuthProfileStore", () => {
profile: { profile: {
provider: "anthropic", provider: "anthropic",
mode: "api_key", mode: "api_key",
apiKey: "sk-ant-alias", apiKey: "sk-ant-alias", // pragma: allowlist secret
}, },
expected: { expected: {
type: "api_key", type: "api_key",
@@ -156,7 +156,7 @@ describe("ensureAuthProfileStore", () => {
provider: "anthropic", provider: "anthropic",
type: "api_key", type: "api_key",
key: "sk-ant-canonical", key: "sk-ant-canonical",
apiKey: "sk-ant-alias", apiKey: "sk-ant-alias", // pragma: allowlist secret
}, },
expected: { expected: {
type: "api_key", type: "api_key",
@@ -210,7 +210,7 @@ describe("ensureAuthProfileStore", () => {
anthropic: { anthropic: {
provider: "anthropic", provider: "anthropic",
mode: "api_key", mode: "api_key",
apiKey: "sk-ant-legacy", apiKey: "sk-ant-legacy", // pragma: allowlist secret
}, },
}, },
null, null,

View File

@@ -37,7 +37,7 @@ describe("auth profile runtime snapshot persistence", () => {
const snapshot = await prepareSecretsRuntimeSnapshot({ const snapshot = await prepareSecretsRuntimeSnapshot({
config: {}, config: {},
env: { OPENAI_API_KEY: "sk-runtime-openai" }, env: { OPENAI_API_KEY: "sk-runtime-openai" }, // pragma: allowlist secret
agentDirs: [agentDir], agentDirs: [agentDir],
}); });
activateSecretsRuntimeSnapshot(snapshot); activateSecretsRuntimeSnapshot(snapshot);

View File

@@ -65,7 +65,7 @@ describe("resolveApiKeyForProfile config compatibility", () => {
profileId, profileId,
}); });
expect(result).toEqual({ expect(result).toEqual({
apiKey: "tok-123", apiKey: "tok-123", // pragma: allowlist secret
provider: "anthropic", provider: "anthropic",
email: undefined, email: undefined,
}); });
@@ -124,7 +124,7 @@ describe("resolveApiKeyForProfile config compatibility", () => {
}); });
// token ↔ oauth are bidirectionally compatible bearer-token auth paths. // token ↔ oauth are bidirectionally compatible bearer-token auth paths.
expect(result).toEqual({ expect(result).toEqual({
apiKey: "access-123", apiKey: "access-123", // pragma: allowlist secret
provider: "anthropic", provider: "anthropic",
email: undefined, email: undefined,
}); });
@@ -145,7 +145,7 @@ describe("resolveApiKeyForProfile token expiry handling", () => {
}), }),
}); });
expect(result).toEqual({ expect(result).toEqual({
apiKey: "tok-123", apiKey: "tok-123", // pragma: allowlist secret
provider: "anthropic", provider: "anthropic",
email: undefined, email: undefined,
}); });
@@ -165,7 +165,7 @@ describe("resolveApiKeyForProfile token expiry handling", () => {
}), }),
}); });
expect(result).toEqual({ expect(result).toEqual({
apiKey: "tok-123", apiKey: "tok-123", // pragma: allowlist secret
provider: "anthropic", provider: "anthropic",
email: undefined, email: undefined,
}); });
@@ -231,7 +231,7 @@ describe("resolveApiKeyForProfile secret refs", () => {
it("resolves api_key keyRef from env", async () => { it("resolves api_key keyRef from env", async () => {
const profileId = "openai:default"; const profileId = "openai:default";
const previous = process.env.OPENAI_API_KEY; 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 { try {
const result = await resolveApiKeyForProfile({ const result = await resolveApiKeyForProfile({
cfg: cfgFor(profileId, "openai", "api_key"), cfg: cfgFor(profileId, "openai", "api_key"),
@@ -248,7 +248,7 @@ describe("resolveApiKeyForProfile secret refs", () => {
profileId, profileId,
}); });
expect(result).toEqual({ expect(result).toEqual({
apiKey: "sk-openai-ref", apiKey: "sk-openai-ref", // pragma: allowlist secret
provider: "openai", provider: "openai",
email: undefined, email: undefined,
}); });
@@ -282,7 +282,7 @@ describe("resolveApiKeyForProfile secret refs", () => {
profileId, profileId,
}); });
expect(result).toEqual({ expect(result).toEqual({
apiKey: "gh-ref-token", apiKey: "gh-ref-token", // pragma: allowlist secret
provider: "github-copilot", provider: "github-copilot",
email: undefined, email: undefined,
}); });
@@ -315,7 +315,7 @@ describe("resolveApiKeyForProfile secret refs", () => {
profileId, profileId,
}); });
expect(result).toEqual({ expect(result).toEqual({
apiKey: "gh-ref-token", apiKey: "gh-ref-token", // pragma: allowlist secret
provider: "github-copilot", provider: "github-copilot",
email: undefined, email: undefined,
}); });
@@ -331,7 +331,7 @@ describe("resolveApiKeyForProfile secret refs", () => {
it("resolves inline ${ENV} api_key values", async () => { it("resolves inline ${ENV} api_key values", async () => {
const profileId = "openai:inline-env"; const profileId = "openai:inline-env";
const previous = process.env.OPENAI_API_KEY; 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 { try {
const result = await resolveApiKeyForProfile({ const result = await resolveApiKeyForProfile({
cfg: cfgFor(profileId, "openai", "api_key"), cfg: cfgFor(profileId, "openai", "api_key"),
@@ -348,7 +348,7 @@ describe("resolveApiKeyForProfile secret refs", () => {
profileId, profileId,
}); });
expect(result).toEqual({ expect(result).toEqual({
apiKey: "sk-openai-inline", apiKey: "sk-openai-inline", // pragma: allowlist secret
provider: "openai", provider: "openai",
email: undefined, email: undefined,
}); });
@@ -381,7 +381,7 @@ describe("resolveApiKeyForProfile secret refs", () => {
profileId, profileId,
}); });
expect(result).toEqual({ expect(result).toEqual({
apiKey: "gh-inline-token", apiKey: "gh-inline-token", // pragma: allowlist secret
provider: "github-copilot", provider: "github-copilot",
email: undefined, email: undefined,
}); });

View File

@@ -31,7 +31,7 @@ describe("compaction identifier-preservation instructions", () => {
} as unknown as NonNullable<ExtensionContext["model"]>; } as unknown as NonNullable<ExtensionContext["model"]>;
const summarizeBase: Omit<SummarizeInStagesInput, "messages"> = { const summarizeBase: Omit<SummarizeInStagesInput, "messages"> = {
model: testModel, model: testModel,
apiKey: "test-key", apiKey: "test-key", // pragma: allowlist secret
reserveTokens: 4000, reserveTokens: 4000,
maxChunkTokens: 8000, maxChunkTokens: 8000,
contextWindow: 200_000, contextWindow: 200_000,

View File

@@ -7,7 +7,7 @@ describe("resolveAwsSdkEnvVarName", () => {
const env = { const env = {
AWS_BEARER_TOKEN_BEDROCK: "bearer", AWS_BEARER_TOKEN_BEDROCK: "bearer",
AWS_ACCESS_KEY_ID: "access", AWS_ACCESS_KEY_ID: "access",
AWS_SECRET_ACCESS_KEY: "secret", AWS_SECRET_ACCESS_KEY: "secret", // pragma: allowlist secret
AWS_PROFILE: "default", AWS_PROFILE: "default",
} as NodeJS.ProcessEnv; } as NodeJS.ProcessEnv;
@@ -17,7 +17,7 @@ describe("resolveAwsSdkEnvVarName", () => {
it("uses access keys when bearer token is missing", () => { it("uses access keys when bearer token is missing", () => {
const env = { const env = {
AWS_ACCESS_KEY_ID: "access", AWS_ACCESS_KEY_ID: "access",
AWS_SECRET_ACCESS_KEY: "secret", AWS_SECRET_ACCESS_KEY: "secret", // pragma: allowlist secret
AWS_PROFILE: "default", AWS_PROFILE: "default",
} as NodeJS.ProcessEnv; } as NodeJS.ProcessEnv;

View File

@@ -90,7 +90,7 @@ function resolveSyntheticLocalProviderAuth(params: {
} }
return { return {
apiKey: "ollama-local", apiKey: "ollama-local", // pragma: allowlist secret
source: "models.providers.ollama (synthetic local key)", source: "models.providers.ollama (synthetic local key)",
mode: "api-key", mode: "api-key",
}; };

View File

@@ -119,9 +119,10 @@ function createAuthStorage(AuthStorageLike: unknown, path: string, creds: PiCred
? withFactory.create(path) ? withFactory.create(path)
: new (AuthStorageLike as { new (path: string): unknown })(path) : new (AuthStorageLike as { new (path: string): unknown })(path)
) as PiAuthStorage & { ) 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)) { for (const [provider, credential] of Object.entries(creds)) {
if (credential.type === "api_key") { if (credential.type === "api_key") {
withRuntimeOverride.setRuntimeApiKey(provider, credential.key); withRuntimeOverride.setRuntimeApiKey(provider, credential.key);

View File

@@ -1,6 +1,6 @@
import crypto from "node:crypto"; 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_TOKEN_TTL_MS = 60 * 1000;
const NOVNC_PASSWORD_LENGTH = 8; const NOVNC_PASSWORD_LENGTH = 8;
const NOVNC_PASSWORD_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; const NOVNC_PASSWORD_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

View File

@@ -25,7 +25,7 @@ type ResolveCommandSecretsResult = {
hadUnresolvedTargets: boolean; hadUnresolvedTargets: boolean;
}; };
export type CommandSecretResolutionMode = "strict" | "summary" | "operational_readonly"; export type CommandSecretResolutionMode = "strict" | "summary" | "operational_readonly"; // pragma: allowlist secret
export type CommandSecretTargetState = export type CommandSecretTargetState =
| "resolved_gateway" | "resolved_gateway"

View File

@@ -20,7 +20,7 @@ import type { SecretInputMode } from "./onboard-types.js";
const ENV_SOURCE_LABEL_RE = /(?:^|:\s)([A-Z][A-Z0-9_]*)$/; 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 = { export type SecretInputModePromptCopy = {
modeMessage?: string; modeMessage?: string;
@@ -101,7 +101,7 @@ export async function promptSecretRefForOnboarding(params: {
const defaultEnvVar = const defaultEnvVar =
params.preferredEnvVar ?? resolveDefaultProviderEnvVar(params.provider) ?? ""; params.preferredEnvVar ?? resolveDefaultProviderEnvVar(params.provider) ?? "";
const defaultFilePointer = resolveDefaultFilePointerId(params.provider); const defaultFilePointer = resolveDefaultFilePointerId(params.provider);
let sourceChoice: SecretRefChoice = "env"; let sourceChoice: SecretRefChoice = "env"; // pragma: allowlist secret
while (true) { while (true) {
const sourceRaw: SecretRefChoice = await params.prompter.select<SecretRefChoice>({ const sourceRaw: SecretRefChoice = await params.prompter.select<SecretRefChoice>({

View File

@@ -9,14 +9,14 @@ const mocks = vi.hoisted(() => {
type: "oauth", type: "oauth",
provider: "anthropic", provider: "anthropic",
access: "sk-ant-oat01-ACCESS-TOKEN-1234567890", 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, expires: Date.now() + 60_000,
email: "peter@example.com", email: "peter@example.com",
}, },
"anthropic:work": { "anthropic:work": {
type: "api_key", type: "api_key",
provider: "anthropic", provider: "anthropic",
key: "sk-ant-api-0123456789abcdefghijklmnopqrstuvwxyz", key: "sk-ant-api-0123456789abcdefghijklmnopqrstuvwxyz", // pragma: allowlist secret
}, },
"openai-codex:default": { "openai-codex:default": {
type: "oauth", type: "oauth",
@@ -49,13 +49,13 @@ const mocks = vi.hoisted(() => {
resolveEnvApiKey: vi.fn((provider: string) => { resolveEnvApiKey: vi.fn((provider: string) => {
if (provider === "openai") { if (provider === "openai") {
return { return {
apiKey: "sk-openai-0123456789abcdefghijklmnopqrstuvwxyz", apiKey: "sk-openai-0123456789abcdefghijklmnopqrstuvwxyz", // pragma: allowlist secret
source: "shell env: OPENAI_API_KEY", source: "shell env: OPENAI_API_KEY",
}; };
} }
if (provider === "anthropic") { if (provider === "anthropic") {
return { return {
apiKey: "sk-ant-oat01-ACCESS-TOKEN-1234567890", apiKey: "sk-ant-oat01-ACCESS-TOKEN-1234567890", // pragma: allowlist secret
source: "env: ANTHROPIC_OAUTH_TOKEN", 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 () => { it("does not emit raw short api-key values in JSON labels", async () => {
const localRuntime = createRuntime(); const localRuntime = createRuntime();
const shortSecret = "abc123"; const shortSecret = "abc123"; // pragma: allowlist secret
const originalProfiles = { ...mocks.store.profiles }; const originalProfiles = { ...mocks.store.profiles };
mocks.store.profiles = { mocks.store.profiles = {
...mocks.store.profiles, ...mocks.store.profiles,

View File

@@ -63,7 +63,8 @@ function resolveApiKeySecretInput(
if (inlineEnvRef) { if (inlineEnvRef) {
return inlineEnvRef; return inlineEnvRef;
} }
if (options?.secretInputMode === "ref") { const useSecretRefMode = options?.secretInputMode === "ref"; // pragma: allowlist secret
if (useSecretRefMode) {
return resolveProviderDefaultEnvSecretRef(provider); return resolveProviderDefaultEnvSecretRef(provider);
} }
return normalized; return normalized;

View File

@@ -429,7 +429,7 @@ describe("parseNonInteractiveCustomApiFlags", () => {
baseUrl: "https://llm.example.com/v1", baseUrl: "https://llm.example.com/v1",
modelId: "foo-large", modelId: "foo-large",
compatibility: "openai", compatibility: "openai",
apiKey: "custom-test-key", apiKey: "custom-test-key", // pragma: allowlist secret
providerId: "my-custom", providerId: "my-custom",
}); });
}); });

View File

@@ -184,7 +184,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv("openclaw-onboard-minimax-", async (env) => { await withOnboardEnv("openclaw-onboard-minimax-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, { const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "minimax-api", authChoice: "minimax-api",
minimaxApiKey: "sk-minimax-test", minimaxApiKey: "sk-minimax-test", // pragma: allowlist secret
}); });
expect(cfg.auth?.profiles?.["minimax:default"]?.provider).toBe("minimax"); 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) => { await withOnboardEnv("openclaw-onboard-minimax-cn-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, { const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "minimax-api-key-cn", 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"); 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) => { await withOnboardEnv("openclaw-onboard-zai-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, { const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "zai-api-key", authChoice: "zai-api-key",
zaiApiKey: "zai-test-key", zaiApiKey: "zai-test-key", // pragma: allowlist secret
}); });
expect(cfg.auth?.profiles?.["zai:default"]?.provider).toBe("zai"); 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) => { await withOnboardEnv("openclaw-onboard-zai-cn-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, { const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "zai-coding-cn", authChoice: "zai-coding-cn",
zaiApiKey: "zai-test-key", zaiApiKey: "zai-test-key", // pragma: allowlist secret
}); });
expect(cfg.models?.providers?.zai?.baseUrl).toBe( 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 () => { it("infers Mistral auth choice from --mistral-api-key and sets default model", async () => {
await withOnboardEnv("openclaw-onboard-mistral-infer-", async (env) => { await withOnboardEnv("openclaw-onboard-mistral-infer-", async (env) => {
const cfg = await runOnboardingAndReadConfig(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"); 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) => { await withOnboardEnv("openclaw-onboard-volcengine-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, { const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "volcengine-api-key", 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"); 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 () => { it("infers BytePlus auth choice from --byteplus-api-key and sets default model", async () => {
await withOnboardEnv("openclaw-onboard-byteplus-infer-", async (env) => { await withOnboardEnv("openclaw-onboard-byteplus-infer-", async (env) => {
const cfg = await runOnboardingAndReadConfig(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"); 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) => { await withOnboardEnv("openclaw-onboard-ai-gateway-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, { const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "ai-gateway-api-key", 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"); 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) => { await withOnboardEnv("openclaw-onboard-openai-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, { const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "openai-api-key", 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); 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", "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 }) => { async ({ prefix, authChoice, optionKey, flagName, envVar }) => {
await withOnboardEnv(prefix, async ({ runtime }) => { 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<string, unknown> = { const options: Record<string, unknown> = {
authChoice, authChoice,
secretInputMode: "ref", secretInputMode: "ref", // pragma: allowlist secret
[optionKey]: providedSecret, [optionKey]: providedSecret,
skipSkills: true, skipSkills: true,
}; };
@@ -447,12 +447,12 @@ describe("onboard (non-interactive): provider auth", () => {
await withEnvAsync( await withEnvAsync(
{ {
OPENCODE_API_KEY: undefined, OPENCODE_API_KEY: undefined,
OPENCODE_ZEN_API_KEY: "opencode-zen-env-key", OPENCODE_ZEN_API_KEY: "opencode-zen-env-key", // pragma: allowlist secret
}, },
async () => { async () => {
await runNonInteractiveOnboardingWithDefaults(runtime, { await runNonInteractiveOnboardingWithDefaults(runtime, {
authChoice: "opencode-zen", authChoice: "opencode-zen",
secretInputMode: "ref", secretInputMode: "ref", // pragma: allowlist secret
skipSkills: true, skipSkills: true,
}); });
@@ -487,7 +487,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv("openclaw-onboard-litellm-", async (env) => { await withOnboardEnv("openclaw-onboard-litellm-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, { const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "litellm-api-key", authChoice: "litellm-api-key",
litellmApiKey: "litellm-test-key", litellmApiKey: "litellm-test-key", // pragma: allowlist secret
}); });
expect(cfg.auth?.profiles?.["litellm:default"]?.provider).toBe("litellm"); expect(cfg.auth?.profiles?.["litellm:default"]?.provider).toBe("litellm");
@@ -519,7 +519,7 @@ describe("onboard (non-interactive): provider auth", () => {
await runNonInteractiveOnboardingWithDefaults(runtime, { await runNonInteractiveOnboardingWithDefaults(runtime, {
cloudflareAiGatewayAccountId: "cf-account-id", cloudflareAiGatewayAccountId: "cf-account-id",
cloudflareAiGatewayGatewayId: "cf-gateway-id", cloudflareAiGatewayGatewayId: "cf-gateway-id",
cloudflareAiGatewayApiKey: "cf-gateway-test-key", cloudflareAiGatewayApiKey: "cf-gateway-test-key", // pragma: allowlist secret
skipSkills: true, skipSkills: true,
...options, ...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 () => { it("infers Together auth choice from --together-api-key and sets default model", async () => {
await withOnboardEnv("openclaw-onboard-together-infer-", async (env) => { await withOnboardEnv("openclaw-onboard-together-infer-", async (env) => {
const cfg = await runOnboardingAndReadConfig(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"); 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 () => { it("infers QIANFAN auth choice from --qianfan-api-key and sets default model", async () => {
await withOnboardEnv("openclaw-onboard-qianfan-infer-", async (env) => { await withOnboardEnv("openclaw-onboard-qianfan-infer-", async (env) => {
const cfg = await runOnboardingAndReadConfig(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"); expect(cfg.auth?.profiles?.["qianfan:default"]?.provider).toBe("qianfan");
@@ -579,7 +579,7 @@ describe("onboard (non-interactive): provider auth", () => {
await runNonInteractiveOnboardingWithDefaults(runtime, { await runNonInteractiveOnboardingWithDefaults(runtime, {
authChoice: "custom-api-key", authChoice: "custom-api-key",
customBaseUrl: "https://llm.example.com/v1", customBaseUrl: "https://llm.example.com/v1",
customApiKey: "custom-test-key", customApiKey: "custom-test-key", // pragma: allowlist secret
customModelId: "foo-large", customModelId: "foo-large",
customCompatibility: "anthropic", customCompatibility: "anthropic",
skipSkills: true, skipSkills: true,
@@ -603,7 +603,7 @@ describe("onboard (non-interactive): provider auth", () => {
await runNonInteractiveOnboardingWithDefaults(runtime, { await runNonInteractiveOnboardingWithDefaults(runtime, {
customBaseUrl: "https://models.custom.local/v1", customBaseUrl: "https://models.custom.local/v1",
customModelId: "local-large", customModelId: "local-large",
customApiKey: "custom-test-key", customApiKey: "custom-test-key", // pragma: allowlist secret
skipSkills: true, skipSkills: true,
}); });
@@ -624,7 +624,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv( await withOnboardEnv(
"openclaw-onboard-custom-provider-env-fallback-", "openclaw-onboard-custom-provider-env-fallback-",
async ({ configPath, runtime }) => { 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); await runCustomLocalNonInteractive(runtime);
expect(await readCustomLocalProviderApiKey(configPath)).toBe("custom-env-key"); expect(await readCustomLocalProviderApiKey(configPath)).toBe("custom-env-key");
}, },
@@ -635,9 +635,9 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv( await withOnboardEnv(
"openclaw-onboard-custom-provider-env-ref-", "openclaw-onboard-custom-provider-env-ref-",
async ({ configPath, runtime }) => { 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, { await runCustomLocalNonInteractive(runtime, {
secretInputMode: "ref", secretInputMode: "ref", // pragma: allowlist secret
}); });
expect(await readCustomLocalProviderApiKeyInput(configPath)).toEqual({ expect(await readCustomLocalProviderApiKeyInput(configPath)).toEqual({
source: "env", 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 () => { 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 }) => { 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 () => { await withEnvAsync({ CUSTOM_API_KEY: undefined }, async () => {
let thrown: Error | undefined; let thrown: Error | undefined;
try { try {
await runCustomLocalNonInteractive(runtime, { await runCustomLocalNonInteractive(runtime, {
secretInputMode: "ref", secretInputMode: "ref", // pragma: allowlist secret
customApiKey: providedSecret, customApiKey: providedSecret,
}); });
} catch (error) { } catch (error) {
@@ -731,7 +731,7 @@ describe("onboard (non-interactive): provider auth", () => {
async ({ runtime }) => { async ({ runtime }) => {
await expect( await expect(
runNonInteractiveOnboardingWithDefaults(runtime, { runNonInteractiveOnboardingWithDefaults(runtime, {
customApiKey: "custom-test-key", customApiKey: "custom-test-key", // pragma: allowlist secret
skipSkills: true, skipSkills: true,
}), }),
).rejects.toThrow('Auth choice "custom-api-key" requires a base URL and model ID.'); ).rejects.toThrow('Auth choice "custom-api-key" requires a base URL and model ID.');

View File

@@ -70,7 +70,8 @@ export async function resolveNonInteractiveApiKey(params: {
const resolvedEnvKey = envResolved?.apiKey ?? explicitEnvKey; const resolvedEnvKey = envResolved?.apiKey ?? explicitEnvKey;
const resolvedEnvVarName = parseEnvVarNameFromSourceLabel(envResolved?.source) ?? explicitEnvVar; const resolvedEnvVarName = parseEnvVarNameFromSourceLabel(envResolved?.source) ?? explicitEnvVar;
if (params.secretInputMode === "ref") { const useSecretRefMode = params.secretInputMode === "ref"; // pragma: allowlist secret
if (useSecretRefMode) {
if (!resolvedEnvKey && flagKey) { if (!resolvedEnvKey && flagKey) {
params.runtime.error( params.runtime.error(
[ [

View File

@@ -91,7 +91,8 @@ export async function applyNonInteractiveAuthChoice(params: {
? { secretInputMode: requestedSecretInputMode } ? { secretInputMode: requestedSecretInputMode }
: undefined; : undefined;
const toStoredSecretInput = (resolved: ResolvedNonInteractiveApiKey): SecretInput | null => { const toStoredSecretInput = (resolved: ResolvedNonInteractiveApiKey): SecretInput | null => {
if (requestedSecretInputMode !== "ref") { const storePlaintextSecret = requestedSecretInputMode !== "ref"; // pragma: allowlist secret
if (storePlaintextSecret) {
return resolved.key; return resolved.key;
} }
if (resolved.source !== "env") { if (resolved.source !== "env") {
@@ -948,7 +949,8 @@ export async function applyNonInteractiveAuthChoice(params: {
}); });
let customApiKeyInput: SecretInput | undefined; let customApiKeyInput: SecretInput | undefined;
if (resolvedCustomApiKey) { if (resolvedCustomApiKey) {
if (requestedSecretInputMode === "ref") { const storeCustomApiKeyAsRef = requestedSecretInputMode === "ref"; // pragma: allowlist secret
if (storeCustomApiKeyAsRef) {
const stored = toStoredSecretInput(resolvedCustomApiKey); const stored = toStoredSecretInput(resolvedCustomApiKey);
if (!stored) { if (!stored) {
return null; return null;

View File

@@ -121,7 +121,7 @@ describe("setupSearch", () => {
web: { web: {
search: { search: {
provider: "perplexity", provider: "perplexity",
perplexity: { apiKey: "existing-key" }, perplexity: { apiKey: "existing-key" }, // pragma: allowlist secret
}, },
}, },
}, },
@@ -142,7 +142,7 @@ describe("setupSearch", () => {
search: { search: {
provider: "perplexity", provider: "perplexity",
enabled: false, enabled: false,
perplexity: { apiKey: "existing-key" }, perplexity: { apiKey: "existing-key" }, // pragma: allowlist secret
}, },
}, },
}, },
@@ -162,7 +162,7 @@ describe("setupSearch", () => {
web: { web: {
search: { search: {
provider: "perplexity", provider: "perplexity",
perplexity: { apiKey: "stored-pplx-key" }, perplexity: { apiKey: "stored-pplx-key" }, // pragma: allowlist secret
}, },
}, },
}, },
@@ -184,7 +184,7 @@ describe("setupSearch", () => {
search: { search: {
provider: "perplexity", provider: "perplexity",
enabled: false, 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 () => { it("quickstart skips key prompt when env var is available", async () => {
const orig = process.env.BRAVE_API_KEY; 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 { try {
const cfg: OpenClawConfig = {}; const cfg: OpenClawConfig = {};
const { prompter } = createPrompter({ selectValue: "brave" }); const { prompter } = createPrompter({ selectValue: "brave" });
@@ -235,13 +235,13 @@ describe("setupSearch", () => {
const cfg: OpenClawConfig = {}; const cfg: OpenClawConfig = {};
const { prompter } = createPrompter({ selectValue: "perplexity" }); const { prompter } = createPrompter({ selectValue: "perplexity" });
const result = await setupSearch(cfg, runtime, prompter, { 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?.provider).toBe("perplexity");
expect(result.tools?.web?.search?.perplexity?.apiKey).toEqual({ expect(result.tools?.web?.search?.perplexity?.apiKey).toEqual({
source: "env", source: "env",
provider: "default", provider: "default",
id: "PERPLEXITY_API_KEY", id: "PERPLEXITY_API_KEY", // pragma: allowlist secret
}); });
expect(prompter.text).not.toHaveBeenCalled(); expect(prompter.text).not.toHaveBeenCalled();
}); });
@@ -250,7 +250,7 @@ describe("setupSearch", () => {
const cfg: OpenClawConfig = {}; const cfg: OpenClawConfig = {};
const { prompter } = createPrompter({ selectValue: "brave" }); const { prompter } = createPrompter({ selectValue: "brave" });
const result = await setupSearch(cfg, runtime, prompter, { 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?.provider).toBe("brave");
expect(result.tools?.web?.search?.apiKey).toEqual({ expect(result.tools?.web?.search?.apiKey).toEqual({

View File

@@ -115,7 +115,8 @@ function resolveSearchSecretInput(
key: string, key: string,
secretInputMode?: SecretInputMode, secretInputMode?: SecretInputMode,
): SecretInput { ): SecretInput {
if (secretInputMode === "ref") { const useSecretRefMode = secretInputMode === "ref"; // pragma: allowlist secret
if (useSecretRefMode) {
return buildSearchEnvRef(provider); return buildSearchEnvRef(provider);
} }
return key; return key;
@@ -254,7 +255,8 @@ export async function setupSearch(
return preserveDisabledState(config, result); return preserveDisabledState(config, result);
} }
if (opts?.secretInputMode === "ref") { const useSecretRefMode = opts?.secretInputMode === "ref"; // pragma: allowlist secret
if (useSecretRefMode) {
if (keyConfigured) { if (keyConfigured) {
return preserveDisabledState(config, applyProviderOnly(config, choice)); return preserveDisabledState(config, applyProviderOnly(config, choice));
} }

View File

@@ -87,7 +87,7 @@ export type NodeManagerChoice = "npm" | "pnpm" | "bun";
export type ChannelChoice = ChannelId; export type ChannelChoice = ChannelId;
// Legacy alias (pre-rename). // Legacy alias (pre-rename).
export type ProviderChoice = ChannelChoice; export type ProviderChoice = ChannelChoice;
export type SecretInputMode = "plaintext" | "ref"; export type SecretInputMode = "plaintext" | "ref"; // pragma: allowlist secret
export type OnboardOptions = { export type OnboardOptions = {
mode?: OnboardMode; mode?: OnboardMode;

View File

@@ -47,7 +47,7 @@ describe("onboardCommand", () => {
await onboardCommand( await onboardCommand(
{ {
secretInputMode: "invalid" as never, secretInputMode: "invalid" as never, // pragma: allowlist secret
}, },
runtime, runtime,
); );

View File

@@ -39,8 +39,8 @@ export async function onboardCommand(opts: OnboardOptions, runtime: RuntimeEnv =
: { ...opts, authChoice: normalizedAuthChoice, flow }; : { ...opts, authChoice: normalizedAuthChoice, flow };
if ( if (
normalizedOpts.secretInputMode && normalizedOpts.secretInputMode &&
normalizedOpts.secretInputMode !== "plaintext" && normalizedOpts.secretInputMode !== "plaintext" && // pragma: allowlist secret
normalizedOpts.secretInputMode !== "ref" normalizedOpts.secretInputMode !== "ref" // pragma: allowlist secret
) { ) {
runtime.error('Invalid --secret-input-mode. Use "plaintext" or "ref".'); runtime.error('Invalid --secret-input-mode. Use "plaintext" or "ref".');
runtime.exit(1); runtime.exit(1);

View File

@@ -236,9 +236,9 @@ function makeHttpSlackUnavailablePlugin(): ChannelPlugin {
botToken: "xoxb-http", botToken: "xoxb-http",
signingSecret: "", signingSecret: "",
botTokenSource: "config", botTokenSource: "config",
signingSecretSource: "config", signingSecretSource: "config", // pragma: allowlist secret
botTokenStatus: "available", botTokenStatus: "available",
signingSecretStatus: "configured_unavailable", signingSecretStatus: "configured_unavailable", // pragma: allowlist secret
}), }),
resolveAccount: () => ({ resolveAccount: () => ({
name: "Primary", name: "Primary",
@@ -248,9 +248,9 @@ function makeHttpSlackUnavailablePlugin(): ChannelPlugin {
botToken: "xoxb-http", botToken: "xoxb-http",
signingSecret: "", signingSecret: "",
botTokenSource: "config", botTokenSource: "config",
signingSecretSource: "config", signingSecretSource: "config", // pragma: allowlist secret
botTokenStatus: "available", botTokenStatus: "available",
signingSecretStatus: "configured_unavailable", signingSecretStatus: "configured_unavailable", // pragma: allowlist secret
}), }),
isConfigured: () => true, isConfigured: () => true,
isEnabled: () => true, isEnabled: () => true,

View File

@@ -177,7 +177,10 @@ const buildAccountNotes = (params: {
if (snapshot.appTokenSource && snapshot.appTokenSource !== "none") { if (snapshot.appTokenSource && snapshot.appTokenSource !== "none") {
notes.push(`app:${snapshot.appTokenSource}`); 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}`); notes.push(`signing:${snapshot.signingSecretSource}`);
} }
if (hasConfiguredUnavailableCredentialStatus(entry.account)) { if (hasConfiguredUnavailableCredentialStatus(entry.account)) {

View File

@@ -150,7 +150,7 @@ export const FIELD_HELP: Record<string, string> = {
"talk.providers.*.voiceAliases": "Optional provider voice alias map for Talk directives.", "talk.providers.*.voiceAliases": "Optional provider voice alias map for Talk directives.",
"talk.providers.*.modelId": "Provider default model ID for Talk mode.", "talk.providers.*.modelId": "Provider default model ID for Talk mode.",
"talk.providers.*.outputFormat": "Provider default output format 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": "talk.voiceId":
"Legacy ElevenLabs default voice ID for Talk mode. Prefer talk.providers.elevenlabs.voiceId.", "Legacy ElevenLabs default voice ID for Talk mode. Prefer talk.providers.elevenlabs.voiceId.",
"talk.voiceAliases": "talk.voiceAliases":
@@ -651,7 +651,7 @@ export const FIELD_HELP: Record<string, string> = {
"tools.web.search.gemini.apiKey": "tools.web.search.gemini.apiKey":
"Gemini API key for Google Search grounding (fallback: GEMINI_API_KEY env var).", "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.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.grok.model": 'Grok model override (default: "grok-4-1-fast").',
"tools.web.search.kimi.apiKey": "tools.web.search.kimi.apiKey":
"Moonshot/Kimi API key (fallback: KIMI_API_KEY or MOONSHOT_API_KEY env var).", "Moonshot/Kimi API key (fallback: KIMI_API_KEY or MOONSHOT_API_KEY env var).",

View File

@@ -217,14 +217,14 @@ export const FIELD_LABELS: Record<string, string> = {
"tools.web.search.maxResults": "Web Search Max Results", "tools.web.search.maxResults": "Web Search Max Results",
"tools.web.search.timeoutSeconds": "Web Search Timeout (sec)", "tools.web.search.timeoutSeconds": "Web Search Timeout (sec)",
"tools.web.search.cacheTtlMinutes": "Web Search Cache TTL (min)", "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.baseUrl": "Perplexity Base URL",
"tools.web.search.perplexity.model": "Perplexity Model", "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.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.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.baseUrl": "Kimi Search Base URL",
"tools.web.search.kimi.model": "Kimi Search Model", "tools.web.search.kimi.model": "Kimi Search Model",
"tools.web.fetch.enabled": "Enable Web Fetch Tool", "tools.web.fetch.enabled": "Enable Web Fetch Tool",
@@ -236,7 +236,7 @@ export const FIELD_LABELS: Record<string, string> = {
"tools.web.fetch.userAgent": "Web Fetch User-Agent", "tools.web.fetch.userAgent": "Web Fetch User-Agent",
"tools.web.fetch.readability": "Web Fetch Readability Extraction", "tools.web.fetch.readability": "Web Fetch Readability Extraction",
"tools.web.fetch.firecrawl.enabled": "Enable Firecrawl Fallback", "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.baseUrl": "Firecrawl Base URL",
"tools.web.fetch.firecrawl.onlyMainContent": "Firecrawl Main Content Only", "tools.web.fetch.firecrawl.onlyMainContent": "Firecrawl Main Content Only",
"tools.web.fetch.firecrawl.maxAgeMs": "Firecrawl Cache Max Age (ms)", "tools.web.fetch.firecrawl.maxAgeMs": "Firecrawl Cache Max Age (ms)",
@@ -411,7 +411,7 @@ export const FIELD_LABELS: Record<string, string> = {
"models.mode": "Model Catalog Mode", "models.mode": "Model Catalog Mode",
"models.providers": "Model Providers", "models.providers": "Model Providers",
"models.providers.*.baseUrl": "Model Provider Base URL", "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.*.auth": "Model Provider Auth Mode",
"models.providers.*.api": "Model Provider API Adapter", "models.providers.*.api": "Model Provider API Adapter",
"models.providers.*.injectNumCtxForOpenAICompat": "Model Provider Inject num_ctx (OpenAI Compat)", "models.providers.*.injectNumCtxForOpenAICompat": "Model Provider Inject num_ctx (OpenAI Compat)",
@@ -484,7 +484,7 @@ export const FIELD_LABELS: Record<string, string> = {
"commands.useAccessGroups": "Use Access Groups", "commands.useAccessGroups": "Use Access Groups",
"commands.ownerAllowFrom": "Command Owners", "commands.ownerAllowFrom": "Command Owners",
"commands.ownerDisplay": "Owner ID Display", "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", "commands.allowFrom": "Command Elevated Access Rules",
ui: "UI", ui: "UI",
"ui.seamColor": "Accent Color", "ui.seamColor": "Accent Color",
@@ -679,8 +679,8 @@ export const FIELD_LABELS: Record<string, string> = {
"talk.providers.*.voiceAliases": "Talk Provider Voice Aliases", "talk.providers.*.voiceAliases": "Talk Provider Voice Aliases",
"talk.providers.*.modelId": "Talk Provider Model ID", "talk.providers.*.modelId": "Talk Provider Model ID",
"talk.providers.*.outputFormat": "Talk Provider Output Format", "talk.providers.*.outputFormat": "Talk Provider Output Format",
"talk.providers.*.apiKey": "Talk Provider API Key", "talk.providers.*.apiKey": "Talk Provider API Key", // pragma: allowlist secret
"talk.apiKey": "Talk API Key", "talk.apiKey": "Talk API Key", // pragma: allowlist secret
channels: "Channels", channels: "Channels",
"channels.defaults": "Channel Defaults", "channels.defaults": "Channel Defaults",
"channels.defaults.groupPolicy": "Default Group Policy", "channels.defaults.groupPolicy": "Default Group Policy",
@@ -823,7 +823,7 @@ export const FIELD_LABELS: Record<string, string> = {
"plugins.entries.*.enabled": "Plugin Enabled", "plugins.entries.*.enabled": "Plugin Enabled",
"plugins.entries.*.hooks": "Plugin Hook Policy", "plugins.entries.*.hooks": "Plugin Hook Policy",
"plugins.entries.*.hooks.allowPromptInjection": "Allow Prompt Injection Hooks", "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.*.env": "Plugin Environment Variables",
"plugins.entries.*.config": "Plugin Config", "plugins.entries.*.config": "Plugin Config",
"plugins.installs": "Plugin Install Records", "plugins.installs": "Plugin Install Records",

View File

@@ -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. * Stable identifier for a secret in a configured source.
@@ -14,7 +14,7 @@ export type SecretRef = {
}; };
export type SecretInput = string | 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}$/; 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})\}$/; const ENV_SECRET_TEMPLATE_RE = /^\$\{([A-Z][A-Z0-9_]{0,127})\}$/;
type SecretDefaults = { type SecretDefaults = {
@@ -179,7 +179,7 @@ export type EnvSecretProviderConfig = {
allowlist?: string[]; allowlist?: string[];
}; };
export type FileSecretProviderMode = "singleValue" | "json"; export type FileSecretProviderMode = "singleValue" | "json"; // pragma: allowlist secret
export type FileSecretProviderConfig = { export type FileSecretProviderConfig = {
source: "file"; source: "file";

View File

@@ -13,7 +13,7 @@ describe("gateway auth mode policy", () => {
auth: { auth: {
mode: "token", mode: "token",
token: "token-value", token: "token-value",
password: "password-value", password: "password-value", // pragma: allowlist secret
}, },
}, },
}; };
@@ -36,7 +36,7 @@ describe("gateway auth mode policy", () => {
gateway: { gateway: {
auth: { auth: {
token: "token-value", token: "token-value",
password: "password-value", password: "password-value", // pragma: allowlist secret
}, },
}, },
}; };
@@ -65,7 +65,7 @@ describe("gateway auth mode policy", () => {
gateway: { gateway: {
auth: { auth: {
token: "token-value", token: "token-value",
password: "password-value", password: "password-value", // pragma: allowlist secret
}, },
}, },
}; };

View File

@@ -252,7 +252,7 @@ export function resolveGatewayAuth(params: {
env, env,
includeLegacyEnv: false, includeLegacyEnv: false,
tokenPrecedence: "config-first", tokenPrecedence: "config-first",
passwordPrecedence: "config-first", passwordPrecedence: "config-first", // pragma: allowlist secret
}); });
const token = resolvedCredentials.token; const token = resolvedCredentials.token;
const password = resolvedCredentials.password; const password = resolvedCredentials.password;

View File

@@ -358,7 +358,7 @@ async function resolveGatewayCredentialsWithEnv(
explicitAuth: context.explicitAuth, explicitAuth: context.explicitAuth,
urlOverride: context.urlOverride, urlOverride: context.urlOverride,
urlOverrideSource: context.urlOverrideSource, urlOverrideSource: context.urlOverrideSource,
remotePasswordPrecedence: "env-first", remotePasswordPrecedence: "env-first", // pragma: allowlist secret
}); });
} }
@@ -487,7 +487,7 @@ async function resolveGatewayCredentialsWithEnv(
explicitAuth: context.explicitAuth, explicitAuth: context.explicitAuth,
urlOverride: context.urlOverride, urlOverride: context.urlOverride,
urlOverrideSource: context.urlOverrideSource, urlOverrideSource: context.urlOverrideSource,
remotePasswordPrecedence: "env-first", remotePasswordPrecedence: "env-first", // pragma: allowlist secret
}); });
} }

View File

@@ -16,7 +16,7 @@ export type GatewayCredentialPrecedence = "env-first" | "config-first";
export type GatewayRemoteCredentialPrecedence = "remote-first" | "env-first"; export type GatewayRemoteCredentialPrecedence = "remote-first" | "env-first";
export type GatewayRemoteCredentialFallback = "remote-env-local" | "remote-only"; 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 { export class GatewaySecretRefUnavailableError extends Error {
readonly code = GATEWAY_SECRET_REF_UNAVAILABLE_ERROR_CODE; readonly code = GATEWAY_SECRET_REF_UNAVAILABLE_ERROR_CODE;
@@ -119,7 +119,7 @@ export function resolveGatewayCredentialsFromValues(params: {
? firstDefined([configToken, envToken]) ? firstDefined([configToken, envToken])
: firstDefined([envToken, configToken]); : firstDefined([envToken, configToken]);
const password = const password =
passwordPrecedence === "config-first" passwordPrecedence === "config-first" // pragma: allowlist secret
? firstDefined([configPassword, envPassword]) ? firstDefined([configPassword, envPassword])
: firstDefined([envPassword, configPassword]); : firstDefined([envPassword, configPassword]);
@@ -158,7 +158,7 @@ export function resolveGatewayCredentialsFromConfig(params: {
env, env,
includeLegacyEnv, includeLegacyEnv,
tokenPrecedence: "env-first", 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([envToken, remoteToken, localToken])
: firstDefined([remoteToken, envToken, localToken]); : firstDefined([remoteToken, envToken, localToken]);
const password = const password =
remotePasswordFallback === "remote-only" remotePasswordFallback === "remote-only" // pragma: allowlist secret
? remotePassword ? remotePassword
: remotePasswordPrecedence === "env-first" : remotePasswordPrecedence === "env-first" // pragma: allowlist secret
? firstDefined([envPassword, remotePassword, localPassword]) ? firstDefined([envPassword, remotePassword, localPassword])
: firstDefined([remotePassword, envPassword, localPassword]); : firstDefined([remotePassword, envPassword, localPassword]);
@@ -255,7 +255,7 @@ export function resolveGatewayCredentialsFromConfig(params: {
const localTokenFallbackEnabled = remoteTokenFallback !== "remote-only"; const localTokenFallbackEnabled = remoteTokenFallback !== "remote-only";
const localTokenFallback = remoteTokenFallback === "remote-only" ? undefined : localToken; const localTokenFallback = remoteTokenFallback === "remote-only" ? undefined : localToken;
const localPasswordFallback = const localPasswordFallback =
remotePasswordFallback === "remote-only" ? undefined : localPassword; remotePasswordFallback === "remote-only" ? undefined : localPassword; // pragma: allowlist secret
if (remoteTokenRef && !token && !envToken && !localTokenFallback && !password) { if (remoteTokenRef && !token && !envToken && !localTokenFallback && !password) {
throwUnresolvedGatewaySecretInput("gateway.remote.token"); throwUnresolvedGatewaySecretInput("gateway.remote.token");
} }

View File

@@ -4,9 +4,9 @@ export const ConnectErrorDetailCodes = {
AUTH_TOKEN_MISSING: "AUTH_TOKEN_MISSING", AUTH_TOKEN_MISSING: "AUTH_TOKEN_MISSING",
AUTH_TOKEN_MISMATCH: "AUTH_TOKEN_MISMATCH", AUTH_TOKEN_MISMATCH: "AUTH_TOKEN_MISMATCH",
AUTH_TOKEN_NOT_CONFIGURED: "AUTH_TOKEN_NOT_CONFIGURED", AUTH_TOKEN_NOT_CONFIGURED: "AUTH_TOKEN_NOT_CONFIGURED",
AUTH_PASSWORD_MISSING: "AUTH_PASSWORD_MISSING", AUTH_PASSWORD_MISSING: "AUTH_PASSWORD_MISSING", // pragma: allowlist secret
AUTH_PASSWORD_MISMATCH: "AUTH_PASSWORD_MISMATCH", AUTH_PASSWORD_MISMATCH: "AUTH_PASSWORD_MISMATCH", // pragma: allowlist secret
AUTH_PASSWORD_NOT_CONFIGURED: "AUTH_PASSWORD_NOT_CONFIGURED", AUTH_PASSWORD_NOT_CONFIGURED: "AUTH_PASSWORD_NOT_CONFIGURED", // pragma: allowlist secret
AUTH_DEVICE_TOKEN_MISMATCH: "AUTH_DEVICE_TOKEN_MISMATCH", AUTH_DEVICE_TOKEN_MISMATCH: "AUTH_DEVICE_TOKEN_MISMATCH",
AUTH_RATE_LIMITED: "AUTH_RATE_LIMITED", AUTH_RATE_LIMITED: "AUTH_RATE_LIMITED",
AUTH_TAILSCALE_IDENTITY_MISSING: "AUTH_TAILSCALE_IDENTITY_MISSING", AUTH_TAILSCALE_IDENTITY_MISSING: "AUTH_TAILSCALE_IDENTITY_MISSING",

View File

@@ -3,7 +3,7 @@ import { resolveSecretInputRef } from "../config/types.secrets.js";
import { secretRefKey } from "../secrets/ref-contract.js"; import { secretRefKey } from "../secrets/ref-contract.js";
import { resolveSecretRefValues } from "../secrets/resolve.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 { function trimToUndefined(value: unknown): string | undefined {
if (typeof value !== "string") { if (typeof value !== "string") {

View File

@@ -115,7 +115,7 @@ function mockSuccessfulWakeConfig(nodeId: string) {
value: { value: {
teamId: "TEAM123", teamId: "TEAM123",
keyId: "KEY123", 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({ mocks.sendApnsBackgroundWake.mockResolvedValue({

View File

@@ -20,7 +20,7 @@ export function registerAuthModesSuite(): void {
let port: number; let port: number;
beforeAll(async () => { beforeAll(async () => {
testState.gatewayAuth = { mode: "password", password: "secret" }; testState.gatewayAuth = { mode: "password", password: "secret" }; // pragma: allowlist secret
port = await getFreePort(); port = await getFreePort();
server = await startGatewayServer(port); server = await startGatewayServer(port);
}); });
@@ -31,14 +31,14 @@ export function registerAuthModesSuite(): void {
test("accepts password auth when configured", async () => { test("accepts password auth when configured", async () => {
const ws = await openWs(port); 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); expect(res.ok).toBe(true);
ws.close(); ws.close();
}); });
test("rejects invalid password", async () => { test("rejects invalid password", async () => {
const ws = await openWs(port); 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.ok).toBe(false);
expect(res.error?.message ?? "").toContain("unauthorized"); expect(res.error?.message ?? "").toContain("unauthorized");
ws.close(); ws.close();

View File

@@ -465,7 +465,7 @@ describe("gateway hot reload", () => {
serverOptions: { serverOptions: {
auth: { auth: {
mode: "password", 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 () => { it("emits one-shot degraded and recovered system events during secret reload transitions", async () => {
await writeEnvRefConfig(); await writeEnvRefConfig();
process.env.OPENAI_API_KEY = "sk-startup"; process.env.OPENAI_API_KEY = "sk-startup"; // pragma: allowlist secret
await withGatewayServer(async () => { await withGatewayServer(async () => {
const onHotReload = hoisted.getOnHotReload(); const onHotReload = hoisted.getOnHotReload();
@@ -531,7 +531,7 @@ describe("gateway hot reload", () => {
); );
expect(drainSystemEvents(sessionKey)).toEqual([]); 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(); await expect(onHotReload?.(plan, nextConfig)).resolves.toBeUndefined();
const recoveredEvents = drainSystemEvents(sessionKey); const recoveredEvents = drainSystemEvents(sessionKey);
expect(recoveredEvents.some((event) => event.includes("[SECRETS_RELOADER_RECOVERED]"))).toBe( 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 () => { it("serves secrets.reload immediately after startup without race failures", async () => {
await writeEnvRefConfig(); await writeEnvRefConfig();
process.env.OPENAI_API_KEY = "sk-startup"; process.env.OPENAI_API_KEY = "sk-startup"; // pragma: allowlist secret
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
try { try {
await connectOk(ws); await connectOk(ws);

View File

@@ -33,9 +33,9 @@ function makeSlackHttpSummaryPlugin(): ChannelPlugin {
botToken: "xoxb-http", botToken: "xoxb-http",
signingSecret: "", signingSecret: "",
botTokenSource: "config", botTokenSource: "config",
signingSecretSource: "config", signingSecretSource: "config", // pragma: allowlist secret
botTokenStatus: "available", botTokenStatus: "available",
signingSecretStatus: "configured_unavailable", signingSecretStatus: "configured_unavailable", // pragma: allowlist secret
} }
: { : {
accountId: "primary", accountId: "primary",

View File

@@ -69,7 +69,10 @@ const buildAccountDetails = (params: {
if (snapshot.appTokenSource && snapshot.appTokenSource !== "none") { if (snapshot.appTokenSource && snapshot.appTokenSource !== "none") {
details.push(`app:${snapshot.appTokenSource}`); 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}`); details.push(`signing:${snapshot.signingSecretSource}`);
} }
if (hasConfiguredUnavailableCredentialStatus(params.entry.account)) { if (hasConfiguredUnavailableCredentialStatus(params.entry.account)) {

View File

@@ -248,17 +248,17 @@ describe("resolveProviderAuths key normalization", () => {
zai: { zai: {
baseUrl: "https://api.z.ai", baseUrl: "https://api.z.ai",
models: [modelDef], models: [modelDef],
apiKey: "cfg-zai-key", apiKey: "cfg-zai-key", // pragma: allowlist secret
}, },
minimax: { minimax: {
baseUrl: "https://api.minimaxi.com", baseUrl: "https://api.minimaxi.com",
models: [modelDef], models: [modelDef],
apiKey: "cfg-minimax-key", apiKey: "cfg-minimax-key", // pragma: allowlist secret
}, },
xiaomi: { xiaomi: {
baseUrl: "https://api.xiaomi.example", baseUrl: "https://api.xiaomi.example",
models: [modelDef], models: [modelDef],
apiKey: "cfg-xiaomi-key", apiKey: "cfg-xiaomi-key", // pragma: allowlist secret
}, },
}, },
}, },

View File

@@ -16,7 +16,7 @@ describe("describeMoonshotVideo", () => {
const result = await describeMoonshotVideo({ const result = await describeMoonshotVideo({
buffer: Buffer.from("video-bytes"), buffer: Buffer.from("video-bytes"),
fileName: "clip.mp4", fileName: "clip.mp4",
apiKey: "moonshot-test", apiKey: "moonshot-test", // pragma: allowlist secret
timeoutMs: 1500, timeoutMs: 1500,
baseUrl: "https://api.moonshot.ai/v1/", baseUrl: "https://api.moonshot.ai/v1/",
model: "kimi-k2.5", model: "kimi-k2.5",
@@ -61,7 +61,7 @@ describe("describeMoonshotVideo", () => {
const result = await describeMoonshotVideo({ const result = await describeMoonshotVideo({
buffer: Buffer.from("video"), buffer: Buffer.from("video"),
fileName: "clip.mp4", fileName: "clip.mp4",
apiKey: "moonshot-test", apiKey: "moonshot-test", // pragma: allowlist secret
timeoutMs: 1000, timeoutMs: 1000,
fetchFn, fetchFn,
}); });

View File

@@ -120,7 +120,7 @@ describe("runCapability auto audio entries", () => {
delete process.env.GROQ_API_KEY; delete process.env.GROQ_API_KEY;
delete process.env.DEEPGRAM_API_KEY; delete process.env.DEEPGRAM_API_KEY;
delete process.env.GEMINI_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<ReturnType<typeof runCapability>> | undefined; let runResult: Awaited<ReturnType<typeof runCapability>> | undefined;
try { try {
await withAudioFixture("openclaw-auto-audio-mistral", async ({ ctx, media, cache }) => { await withAudioFixture("openclaw-auto-audio-mistral", async ({ ctx, media, cache }) => {
@@ -140,7 +140,7 @@ describe("runCapability auto audio entries", () => {
models: { models: {
providers: { providers: {
mistral: { mistral: {
apiKey: "mistral-test-key", apiKey: "mistral-test-key", // pragma: allowlist secret
models: [], models: [],
}, },
}, },

View File

@@ -25,7 +25,7 @@ async function runAudioCapabilityWithFetchCapture(params: {
models: { models: {
providers: { providers: {
openai: { openai: {
apiKey: "test-key", apiKey: "test-key", // pragma: allowlist secret
models: [], models: [],
}, },
}, },
@@ -80,7 +80,7 @@ describe("runCapability proxy fetch passthrough", () => {
models: { models: {
providers: { providers: {
moonshot: { moonshot: {
apiKey: "test-key", apiKey: "test-key", // pragma: allowlist secret
models: [], models: [],
}, },
}, },

View File

@@ -52,7 +52,7 @@ const AUDIO_CAPABILITY_CFG = {
models: { models: {
providers: { providers: {
openai: { openai: {
apiKey: "test-key", apiKey: "test-key", // pragma: allowlist secret
models: [], models: [],
}, },
}, },

View File

@@ -233,7 +233,7 @@ describe("embedding provider remote overrides", () => {
config: {} as never, config: {} as never,
provider: "gemini", provider: "gemini",
remote: { remote: {
apiKey: "GEMINI_API_KEY", apiKey: "GEMINI_API_KEY", // pragma: allowlist secret
}, },
model: "text-embedding-004", model: "text-embedding-004",
fallback: "openai", fallback: "openai",
@@ -266,7 +266,7 @@ describe("embedding provider remote overrides", () => {
config: cfg as never, config: cfg as never,
provider: "mistral", provider: "mistral",
remote: { remote: {
apiKey: "mistral-key", apiKey: "mistral-key", // pragma: allowlist secret
}, },
model: "mistral/mistral-embed", model: "mistral/mistral-embed",
fallback: "none", fallback: "none",
@@ -356,7 +356,7 @@ describe("embedding provider auto selection", () => {
vi.stubGlobal("fetch", fetchMock); vi.stubGlobal("fetch", fetchMock);
vi.mocked(authModule.resolveApiKeyForProvider).mockImplementation(async ({ provider }) => { vi.mocked(authModule.resolveApiKeyForProvider).mockImplementation(async ({ provider }) => {
if (provider === "mistral") { 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}".`); throw new Error(`No API key found for provider "${provider}".`);
}); });

View File

@@ -298,7 +298,8 @@ function applyConfigTargetMutations(params: {
} }
const targetPathSegments = resolved.pathSegments; 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); const previous = getPath(params.nextConfig, targetPathSegments);
if (isNonEmptyString(previous)) { if (isNonEmptyString(previous)) {
scrubbedValues.add(previous.trim()); scrubbedValues.add(previous.trim());
@@ -530,7 +531,8 @@ function applyAuthProfileTargetMutation(params: {
store, store,
}); });
const targetPathSegments = params.resolved.pathSegments; 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); const previous = getPath(store, targetPathSegments);
if (isNonEmptyString(previous)) { if (isNonEmptyString(previous)) {
params.scrubbedValues.add(previous.trim()); params.scrubbedValues.add(previous.trim());

View File

@@ -53,7 +53,7 @@ async function createAuditFixture(): Promise<AuditFixture> {
env: { env: {
OPENCLAW_STATE_DIR: stateDir, OPENCLAW_STATE_DIR: stateDir,
OPENCLAW_CONFIG_PATH: configPath, OPENCLAW_CONFIG_PATH: configPath,
OPENAI_API_KEY: "env-openai-key", OPENAI_API_KEY: "env-openai-key", // pragma: allowlist secret
PATH: resolveRuntimePathEnv(), PATH: resolveRuntimePathEnv(),
}, },
}; };
@@ -146,7 +146,7 @@ describe("secrets audit", () => {
"#!/bin/sh", "#!/bin/sh",
`printf 'x\\n' >> ${JSON.stringify(execLogPath)}`, `printf 'x\\n' >> ${JSON.stringify(execLogPath)}`,
"cat >/dev/null", "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"), ].join("\n"),
{ encoding: "utf8", mode: 0o700 }, { encoding: "utf8", mode: 0o700 },
); );

View File

@@ -36,7 +36,7 @@ export type SecretsAuditCode =
| "REF_SHADOWED" | "REF_SHADOWED"
| "LEGACY_RESIDUE"; | "LEGACY_RESIDUE";
export type SecretsAuditSeverity = "info" | "warn" | "error"; export type SecretsAuditSeverity = "info" | "warn" | "error"; // pragma: allowlist secret
export type SecretsAuditFinding = { export type SecretsAuditFinding = {
code: SecretsAuditCode; code: SecretsAuditCode;
@@ -48,7 +48,7 @@ export type SecretsAuditFinding = {
profileId?: string; profileId?: string;
}; };
export type SecretsAuditStatus = "clean" | "findings" | "unresolved"; export type SecretsAuditStatus = "clean" | "findings" | "unresolved"; // pragma: allowlist secret
export type SecretsAuditReport = { export type SecretsAuditReport = {
version: 1; version: 1;

View File

@@ -79,7 +79,9 @@ export function analyzeCommandSecretAssignmentsFromSnapshot(params: {
value: resolved, 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( diagnostics.push(
`${target.path}: both inline and sibling ref were present; sibling ref took precedence.`, `${target.path}: both inline and sibling ref were present; sibling ref took precedence.`,
); );

View File

@@ -6,7 +6,7 @@ type CredentialMatrixEntry = {
path: string; path: string;
refPath?: string; refPath?: string;
when?: { type: "api_key" | "token" }; when?: { type: "api_key" | "token" };
secretShape: "secret_input" | "sibling_ref"; secretShape: "secret_input" | "sibling_ref"; // pragma: allowlist secret
optIn: true; optIn: true;
notes?: string; notes?: string;
}; };

View File

@@ -153,7 +153,7 @@ describe("secret ref resolver", () => {
{ source: "env", provider: "default", id: "OPENAI_API_KEY" }, { source: "env", provider: "default", id: "OPENAI_API_KEY" },
{ {
config, config,
env: { OPENAI_API_KEY: "sk-env-value" }, env: { OPENAI_API_KEY: "sk-env-value" }, // pragma: allowlist secret
}, },
); );
expect(value).toBe("sk-env-value"); expect(value).toBe("sk-env-value");
@@ -167,7 +167,7 @@ describe("secret ref resolver", () => {
JSON.stringify({ JSON.stringify({
providers: { providers: {
openai: { openai: {
apiKey: "sk-file-value", apiKey: "sk-file-value", // pragma: allowlist secret
}, },
}, },
}), }),
@@ -375,7 +375,7 @@ describe("secret ref resolver", () => {
JSON.stringify({ JSON.stringify({
providers: { providers: {
openai: { openai: {
apiKey: "sk-file-value", apiKey: "sk-file-value", // pragma: allowlist secret
}, },
}, },
}), }),

View File

@@ -122,21 +122,21 @@ describe("secrets runtime snapshot", () => {
const snapshot = await prepareSecretsRuntimeSnapshot({ const snapshot = await prepareSecretsRuntimeSnapshot({
config, config,
env: { env: {
OPENAI_API_KEY: "sk-env-openai", OPENAI_API_KEY: "sk-env-openai", // pragma: allowlist secret
GITHUB_TOKEN: "ghp-env-token", GITHUB_TOKEN: "ghp-env-token", // pragma: allowlist secret
REVIEW_SKILL_API_KEY: "sk-skill-ref", REVIEW_SKILL_API_KEY: "sk-skill-ref", // pragma: allowlist secret
MEMORY_REMOTE_API_KEY: "mem-ref-key", MEMORY_REMOTE_API_KEY: "mem-ref-key", // pragma: allowlist secret
TALK_API_KEY: "talk-ref-key", TALK_API_KEY: "talk-ref-key", // pragma: allowlist secret
TALK_PROVIDER_API_KEY: "talk-provider-ref-key", TALK_PROVIDER_API_KEY: "talk-provider-ref-key", // pragma: allowlist secret
REMOTE_GATEWAY_TOKEN: "remote-token-ref", 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_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", 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_BOT_TOKEN_REF: "slack-work-bot-ref",
SLACK_WORK_APP_TOKEN_REF: "slack-work-app-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"], agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => loadAuthStore: () =>
@@ -305,7 +305,7 @@ describe("secrets runtime snapshot", () => {
}, },
}), }),
env: { env: {
WEB_SEARCH_API_KEY: "web-search-ref", WEB_SEARCH_API_KEY: "web-search-ref", // pragma: allowlist secret
}, },
agentDirs: ["/tmp/openclaw-agent-main"], agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }), loadAuthStore: () => ({ version: 1, profiles: {} }),
@@ -343,8 +343,8 @@ describe("secrets runtime snapshot", () => {
}, },
}), }),
env: { env: {
WEB_SEARCH_API_KEY: "web-search-ref", WEB_SEARCH_API_KEY: "web-search-ref", // pragma: allowlist secret
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"], agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }), loadAuthStore: () => ({ version: 1, profiles: {} }),
@@ -374,7 +374,7 @@ describe("secrets runtime snapshot", () => {
}, },
}), }),
env: { 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"], agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }), loadAuthStore: () => ({ version: 1, profiles: {} }),
@@ -399,7 +399,7 @@ describe("secrets runtime snapshot", () => {
{ {
providers: { providers: {
openai: { 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"], agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => loadAuthStore: () =>
loadAuthStoreWithProfiles({ loadAuthStoreWithProfiles({
@@ -603,7 +603,7 @@ describe("secrets runtime snapshot", () => {
auth: { auth: {
mode: "password", mode: "password",
token: "local-token", token: "local-token",
password: "local-password", password: "local-password", // pragma: allowlist secret
}, },
remote: { remote: {
enabled: true, enabled: true,
@@ -642,7 +642,7 @@ describe("secrets runtime snapshot", () => {
}, },
}), }),
env: { env: {
GATEWAY_PASSWORD_REF: "resolved-gateway-password", GATEWAY_PASSWORD_REF: "resolved-gateway-password", // pragma: allowlist secret
}, },
agentDirs: ["/tmp/openclaw-agent-main"], agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }), loadAuthStore: () => ({ version: 1, profiles: {} }),
@@ -680,7 +680,7 @@ describe("secrets runtime snapshot", () => {
auth: { auth: {
mode: "password", mode: "password",
token: { source: "env", provider: "default", id: "GATEWAY_TOKEN_REF" }, 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: { env: {
GATEWAY_PASSWORD_REF: "resolved-gateway-password", GATEWAY_PASSWORD_REF: "resolved-gateway-password", // pragma: allowlist secret
}, },
agentDirs: ["/tmp/openclaw-agent-main"], agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }), loadAuthStore: () => ({ version: 1, profiles: {} }),
@@ -822,7 +822,7 @@ describe("secrets runtime snapshot", () => {
}), }),
env: { env: {
REMOTE_TOKEN: "resolved-remote-token", REMOTE_TOKEN: "resolved-remote-token",
REMOTE_PASSWORD: "resolved-remote-password", REMOTE_PASSWORD: "resolved-remote-password", // pragma: allowlist secret
}, },
agentDirs: ["/tmp/openclaw-agent-main"], agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }), loadAuthStore: () => ({ version: 1, profiles: {} }),
@@ -846,7 +846,7 @@ describe("secrets runtime snapshot", () => {
}, },
}), }),
env: { env: {
REMOTE_PASSWORD: "resolved-remote-password", REMOTE_PASSWORD: "resolved-remote-password", // pragma: allowlist secret
}, },
agentDirs: ["/tmp/openclaw-agent-main"], agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }), loadAuthStore: () => ({ version: 1, profiles: {} }),
@@ -980,8 +980,8 @@ describe("secrets runtime snapshot", () => {
}, },
}), }),
env: { env: {
NEXTCLOUD_BOT_SECRET: "resolved-nextcloud-bot-secret", NEXTCLOUD_BOT_SECRET: "resolved-nextcloud-bot-secret", // pragma: allowlist secret
NEXTCLOUD_API_PASSWORD: "resolved-nextcloud-api-password", NEXTCLOUD_API_PASSWORD: "resolved-nextcloud-api-password", // pragma: allowlist secret
}, },
agentDirs: ["/tmp/openclaw-agent-main"], agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }), loadAuthStore: () => ({ version: 1, profiles: {} }),
@@ -1022,8 +1022,8 @@ describe("secrets runtime snapshot", () => {
}, },
}), }),
env: { env: {
NEXTCLOUD_WORK_BOT_SECRET: "resolved-nextcloud-work-bot-secret", NEXTCLOUD_WORK_BOT_SECRET: "resolved-nextcloud-work-bot-secret", // pragma: allowlist secret
NEXTCLOUD_WORK_API_PASSWORD: "resolved-nextcloud-work-api-password", NEXTCLOUD_WORK_API_PASSWORD: "resolved-nextcloud-work-api-password", // pragma: allowlist secret
}, },
agentDirs: ["/tmp/openclaw-agent-main"], agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }), loadAuthStore: () => ({ version: 1, profiles: {} }),
@@ -1058,7 +1058,7 @@ describe("secrets runtime snapshot", () => {
}), }),
env: { env: {
REMOTE_GATEWAY_TOKEN: "tailscale-remote-token", 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"], agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }), loadAuthStore: () => ({ version: 1, profiles: {} }),
@@ -1931,7 +1931,7 @@ describe("secrets runtime snapshot", () => {
list: [{ id: "worker" }], 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" }); await expect(fs.access(workerStorePath)).rejects.toMatchObject({ code: "ENOENT" });

View File

@@ -1,6 +1,6 @@
import { isNonEmptyString, isRecord } from "./shared.js"; 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( export function isExpectedResolvedSecretValue(
value: unknown, value: unknown,

View File

@@ -1,5 +1,8 @@
import type { SecretTargetRegistryEntry } from "./target-registry-types.js"; 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[] = [ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
{ {
id: "auth-profiles.api_key.key", id: "auth-profiles.api_key.key",
@@ -7,7 +10,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
configFile: "auth-profiles.json", configFile: "auth-profiles.json",
pathPattern: "profiles.*.key", pathPattern: "profiles.*.key",
refPathPattern: "profiles.*.keyRef", refPathPattern: "profiles.*.keyRef",
secretShape: "sibling_ref", secretShape: SIBLING_REF_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -20,7 +23,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
configFile: "auth-profiles.json", configFile: "auth-profiles.json",
pathPattern: "profiles.*.token", pathPattern: "profiles.*.token",
refPathPattern: "profiles.*.tokenRef", refPathPattern: "profiles.*.tokenRef",
secretShape: "sibling_ref", secretShape: SIBLING_REF_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -32,7 +35,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "agents.defaults.memorySearch.remote.apiKey", targetType: "agents.defaults.memorySearch.remote.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "agents.defaults.memorySearch.remote.apiKey", pathPattern: "agents.defaults.memorySearch.remote.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -43,7 +46,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "agents.list[].memorySearch.remote.apiKey", targetType: "agents.list[].memorySearch.remote.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "agents.list[].memorySearch.remote.apiKey", pathPattern: "agents.list[].memorySearch.remote.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -54,7 +57,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.bluebubbles.accounts.*.password", targetType: "channels.bluebubbles.accounts.*.password",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.bluebubbles.accounts.*.password", pathPattern: "channels.bluebubbles.accounts.*.password",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -65,7 +68,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.bluebubbles.password", targetType: "channels.bluebubbles.password",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.bluebubbles.password", pathPattern: "channels.bluebubbles.password",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -76,7 +79,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.discord.accounts.*.pluralkit.token", targetType: "channels.discord.accounts.*.pluralkit.token",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.discord.accounts.*.pluralkit.token", pathPattern: "channels.discord.accounts.*.pluralkit.token",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -87,7 +90,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.discord.accounts.*.token", targetType: "channels.discord.accounts.*.token",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.discord.accounts.*.token", pathPattern: "channels.discord.accounts.*.token",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -98,7 +101,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.discord.accounts.*.voice.tts.elevenlabs.apiKey", targetType: "channels.discord.accounts.*.voice.tts.elevenlabs.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.discord.accounts.*.voice.tts.elevenlabs.apiKey", pathPattern: "channels.discord.accounts.*.voice.tts.elevenlabs.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -109,7 +112,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.discord.accounts.*.voice.tts.openai.apiKey", targetType: "channels.discord.accounts.*.voice.tts.openai.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.discord.accounts.*.voice.tts.openai.apiKey", pathPattern: "channels.discord.accounts.*.voice.tts.openai.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -120,7 +123,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.discord.pluralkit.token", targetType: "channels.discord.pluralkit.token",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.discord.pluralkit.token", pathPattern: "channels.discord.pluralkit.token",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -131,7 +134,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.discord.token", targetType: "channels.discord.token",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.discord.token", pathPattern: "channels.discord.token",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -142,7 +145,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.discord.voice.tts.elevenlabs.apiKey", targetType: "channels.discord.voice.tts.elevenlabs.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.discord.voice.tts.elevenlabs.apiKey", pathPattern: "channels.discord.voice.tts.elevenlabs.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -153,7 +156,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.discord.voice.tts.openai.apiKey", targetType: "channels.discord.voice.tts.openai.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.discord.voice.tts.openai.apiKey", pathPattern: "channels.discord.voice.tts.openai.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -164,7 +167,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.feishu.accounts.*.appSecret", targetType: "channels.feishu.accounts.*.appSecret",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.feishu.accounts.*.appSecret", pathPattern: "channels.feishu.accounts.*.appSecret",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -175,7 +178,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.feishu.accounts.*.verificationToken", targetType: "channels.feishu.accounts.*.verificationToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.feishu.accounts.*.verificationToken", pathPattern: "channels.feishu.accounts.*.verificationToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -186,7 +189,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.feishu.appSecret", targetType: "channels.feishu.appSecret",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.feishu.appSecret", pathPattern: "channels.feishu.appSecret",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -197,7 +200,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.feishu.verificationToken", targetType: "channels.feishu.verificationToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.feishu.verificationToken", pathPattern: "channels.feishu.verificationToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -210,7 +213,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.googlechat.accounts.*.serviceAccount", pathPattern: "channels.googlechat.accounts.*.serviceAccount",
refPathPattern: "channels.googlechat.accounts.*.serviceAccountRef", refPathPattern: "channels.googlechat.accounts.*.serviceAccountRef",
secretShape: "sibling_ref", secretShape: SIBLING_REF_SHAPE,
expectedResolvedValue: "string-or-object", expectedResolvedValue: "string-or-object",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -223,7 +226,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.googlechat.serviceAccount", pathPattern: "channels.googlechat.serviceAccount",
refPathPattern: "channels.googlechat.serviceAccountRef", refPathPattern: "channels.googlechat.serviceAccountRef",
secretShape: "sibling_ref", secretShape: SIBLING_REF_SHAPE,
expectedResolvedValue: "string-or-object", expectedResolvedValue: "string-or-object",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -234,7 +237,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.irc.accounts.*.nickserv.password", targetType: "channels.irc.accounts.*.nickserv.password",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.irc.accounts.*.nickserv.password", pathPattern: "channels.irc.accounts.*.nickserv.password",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -245,7 +248,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.irc.accounts.*.password", targetType: "channels.irc.accounts.*.password",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.irc.accounts.*.password", pathPattern: "channels.irc.accounts.*.password",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -256,7 +259,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.irc.nickserv.password", targetType: "channels.irc.nickserv.password",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.irc.nickserv.password", pathPattern: "channels.irc.nickserv.password",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -267,7 +270,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.irc.password", targetType: "channels.irc.password",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.irc.password", pathPattern: "channels.irc.password",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -278,7 +281,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.mattermost.accounts.*.botToken", targetType: "channels.mattermost.accounts.*.botToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.mattermost.accounts.*.botToken", pathPattern: "channels.mattermost.accounts.*.botToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -289,7 +292,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.mattermost.botToken", targetType: "channels.mattermost.botToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.mattermost.botToken", pathPattern: "channels.mattermost.botToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -300,7 +303,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.matrix.accounts.*.password", targetType: "channels.matrix.accounts.*.password",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.matrix.accounts.*.password", pathPattern: "channels.matrix.accounts.*.password",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -311,7 +314,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.matrix.password", targetType: "channels.matrix.password",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.matrix.password", pathPattern: "channels.matrix.password",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -322,7 +325,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.msteams.appPassword", targetType: "channels.msteams.appPassword",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.msteams.appPassword", pathPattern: "channels.msteams.appPassword",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -333,7 +336,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.nextcloud-talk.accounts.*.apiPassword", targetType: "channels.nextcloud-talk.accounts.*.apiPassword",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.nextcloud-talk.accounts.*.apiPassword", pathPattern: "channels.nextcloud-talk.accounts.*.apiPassword",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -344,7 +347,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.nextcloud-talk.accounts.*.botSecret", targetType: "channels.nextcloud-talk.accounts.*.botSecret",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.nextcloud-talk.accounts.*.botSecret", pathPattern: "channels.nextcloud-talk.accounts.*.botSecret",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -355,7 +358,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.nextcloud-talk.apiPassword", targetType: "channels.nextcloud-talk.apiPassword",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.nextcloud-talk.apiPassword", pathPattern: "channels.nextcloud-talk.apiPassword",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -366,7 +369,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.nextcloud-talk.botSecret", targetType: "channels.nextcloud-talk.botSecret",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.nextcloud-talk.botSecret", pathPattern: "channels.nextcloud-talk.botSecret",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -377,7 +380,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.slack.accounts.*.appToken", targetType: "channels.slack.accounts.*.appToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.slack.accounts.*.appToken", pathPattern: "channels.slack.accounts.*.appToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -388,7 +391,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.slack.accounts.*.botToken", targetType: "channels.slack.accounts.*.botToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.slack.accounts.*.botToken", pathPattern: "channels.slack.accounts.*.botToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -399,7 +402,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.slack.accounts.*.signingSecret", targetType: "channels.slack.accounts.*.signingSecret",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.slack.accounts.*.signingSecret", pathPattern: "channels.slack.accounts.*.signingSecret",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -410,7 +413,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.slack.accounts.*.userToken", targetType: "channels.slack.accounts.*.userToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.slack.accounts.*.userToken", pathPattern: "channels.slack.accounts.*.userToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -421,7 +424,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.slack.appToken", targetType: "channels.slack.appToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.slack.appToken", pathPattern: "channels.slack.appToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -432,7 +435,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.slack.botToken", targetType: "channels.slack.botToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.slack.botToken", pathPattern: "channels.slack.botToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -443,7 +446,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.slack.signingSecret", targetType: "channels.slack.signingSecret",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.slack.signingSecret", pathPattern: "channels.slack.signingSecret",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -454,7 +457,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.slack.userToken", targetType: "channels.slack.userToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.slack.userToken", pathPattern: "channels.slack.userToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -465,7 +468,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.telegram.accounts.*.botToken", targetType: "channels.telegram.accounts.*.botToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.telegram.accounts.*.botToken", pathPattern: "channels.telegram.accounts.*.botToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -476,7 +479,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.telegram.accounts.*.webhookSecret", targetType: "channels.telegram.accounts.*.webhookSecret",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.telegram.accounts.*.webhookSecret", pathPattern: "channels.telegram.accounts.*.webhookSecret",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -487,7 +490,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.telegram.botToken", targetType: "channels.telegram.botToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.telegram.botToken", pathPattern: "channels.telegram.botToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -498,7 +501,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.telegram.webhookSecret", targetType: "channels.telegram.webhookSecret",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.telegram.webhookSecret", pathPattern: "channels.telegram.webhookSecret",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -509,7 +512,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.zalo.accounts.*.botToken", targetType: "channels.zalo.accounts.*.botToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.zalo.accounts.*.botToken", pathPattern: "channels.zalo.accounts.*.botToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -520,7 +523,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.zalo.accounts.*.webhookSecret", targetType: "channels.zalo.accounts.*.webhookSecret",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.zalo.accounts.*.webhookSecret", pathPattern: "channels.zalo.accounts.*.webhookSecret",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -531,7 +534,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.zalo.botToken", targetType: "channels.zalo.botToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.zalo.botToken", pathPattern: "channels.zalo.botToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -542,7 +545,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "channels.zalo.webhookSecret", targetType: "channels.zalo.webhookSecret",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "channels.zalo.webhookSecret", pathPattern: "channels.zalo.webhookSecret",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -553,7 +556,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "cron.webhookToken", targetType: "cron.webhookToken",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "cron.webhookToken", pathPattern: "cron.webhookToken",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -564,7 +567,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "gateway.auth.token", targetType: "gateway.auth.token",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "gateway.auth.token", pathPattern: "gateway.auth.token",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -575,7 +578,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "gateway.auth.password", targetType: "gateway.auth.password",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "gateway.auth.password", pathPattern: "gateway.auth.password",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -586,7 +589,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "gateway.remote.password", targetType: "gateway.remote.password",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "gateway.remote.password", pathPattern: "gateway.remote.password",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -597,7 +600,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "gateway.remote.token", targetType: "gateway.remote.token",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "gateway.remote.token", pathPattern: "gateway.remote.token",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -608,7 +611,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "messages.tts.elevenlabs.apiKey", targetType: "messages.tts.elevenlabs.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "messages.tts.elevenlabs.apiKey", pathPattern: "messages.tts.elevenlabs.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -619,7 +622,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "messages.tts.openai.apiKey", targetType: "messages.tts.openai.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "messages.tts.openai.apiKey", pathPattern: "messages.tts.openai.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -631,7 +634,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetTypeAliases: ["models.providers.*.apiKey"], targetTypeAliases: ["models.providers.*.apiKey"],
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "models.providers.*.apiKey", pathPattern: "models.providers.*.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -645,7 +648,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetTypeAliases: ["skills.entries.*.apiKey"], targetTypeAliases: ["skills.entries.*.apiKey"],
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "skills.entries.*.apiKey", pathPattern: "skills.entries.*.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -656,7 +659,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "talk.apiKey", targetType: "talk.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "talk.apiKey", pathPattern: "talk.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -667,7 +670,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "talk.providers.*.apiKey", targetType: "talk.providers.*.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "talk.providers.*.apiKey", pathPattern: "talk.providers.*.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -678,7 +681,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "tools.web.search.apiKey", targetType: "tools.web.search.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "tools.web.search.apiKey", pathPattern: "tools.web.search.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -689,7 +692,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "tools.web.search.gemini.apiKey", targetType: "tools.web.search.gemini.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "tools.web.search.gemini.apiKey", pathPattern: "tools.web.search.gemini.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -700,7 +703,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "tools.web.search.grok.apiKey", targetType: "tools.web.search.grok.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "tools.web.search.grok.apiKey", pathPattern: "tools.web.search.grok.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -711,7 +714,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "tools.web.search.kimi.apiKey", targetType: "tools.web.search.kimi.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "tools.web.search.kimi.apiKey", pathPattern: "tools.web.search.kimi.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,
@@ -722,7 +725,7 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
targetType: "tools.web.search.perplexity.apiKey", targetType: "tools.web.search.perplexity.apiKey",
configFile: "openclaw.json", configFile: "openclaw.json",
pathPattern: "tools.web.search.perplexity.apiKey", pathPattern: "tools.web.search.perplexity.apiKey",
secretShape: "secret_input", secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string", expectedResolvedValue: "string",
includeInPlan: true, includeInPlan: true,
includeInConfigure: true, includeInConfigure: true,

View File

@@ -49,8 +49,8 @@ describe("target registry pattern helpers", () => {
}, },
talk: { talk: {
providers: { providers: {
openai: { apiKey: "oa" }, openai: { apiKey: "oa" }, // pragma: allowlist secret
anthropic: { apiKey: "an" }, anthropic: { apiKey: "an" }, // pragma: allowlist secret
}, },
}, },
}; };

View File

@@ -47,7 +47,8 @@ export function compileTargetRegistryEntry(
const pathDynamicTokenCount = countDynamicPatternTokens(pathTokens); const pathDynamicTokenCount = countDynamicPatternTokens(pathTokens);
const refPathTokens = entry.refPathPattern ? parsePathPattern(entry.refPathPattern) : undefined; const refPathTokens = entry.refPathPattern ? parsePathPattern(entry.refPathPattern) : undefined;
const refPathDynamicTokenCount = refPathTokens ? countDynamicPatternTokens(refPathTokens) : 0; 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}`); throw new Error(`Missing refPathPattern for sibling_ref target: ${entry.id}`);
} }
if (refPathTokens && refPathDynamicTokenCount !== pathDynamicTokenCount) { if (refPathTokens && refPathDynamicTokenCount !== pathDynamicTokenCount) {

View File

@@ -1,6 +1,6 @@
export type SecretTargetConfigFile = "openclaw.json" | "auth-profiles.json"; export type SecretTargetConfigFile = "openclaw.json" | "auth-profiles.json"; // pragma: allowlist secret
export type SecretTargetShape = "secret_input" | "sibling_ref"; export type SecretTargetShape = "secret_input" | "sibling_ref"; // pragma: allowlist secret
export type SecretTargetExpected = "string" | "string-or-object"; export type SecretTargetExpected = "string" | "string-or-object"; // pragma: allowlist secret
export type AuthProfileType = "api_key" | "token"; export type AuthProfileType = "api_key" | "token";
export type SecretTargetRegistryEntry = { export type SecretTargetRegistryEntry = {

View File

@@ -118,7 +118,7 @@ describe("resolveGatewayConnection", () => {
gateway: { gateway: {
mode: "local", mode: "local",
auth: { auth: {
password: "config-password", password: "config-password", // pragma: allowlist secret
}, },
}, },
}); });
@@ -134,7 +134,7 @@ describe("resolveGatewayConnection", () => {
mode: "local", mode: "local",
auth: { auth: {
token: "config-token", token: "config-token",
password: "config-password", password: "config-password", // pragma: allowlist secret
}, },
}, },
}); });
@@ -180,13 +180,15 @@ describe("resolveGatewayConnection", () => {
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
gateway: { gateway: {
mode: "remote", 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({}); const result = await resolveGatewayConnection({});
expect(result.password).toBe("env-pass"); expect(result.password).toBe(gatewayPassword);
}); });
}); });
@@ -263,12 +265,12 @@ describe("resolveGatewayConnection", () => {
const tokenExecProgram = [ const tokenExecProgram = [
"const fs=require('node:fs');", "const fs=require('node:fs');",
`fs.writeFileSync(${JSON.stringify(tokenMarker)},'1');`, `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(""); ].join("");
const passwordExecProgram = [ const passwordExecProgram = [
"const fs=require('node:fs');", "const fs=require('node:fs');",
`fs.writeFileSync(${JSON.stringify(passwordMarker)},'1');`, `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(""); ].join("");
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
@@ -316,12 +318,12 @@ describe("resolveGatewayConnection", () => {
const tokenExecProgram = [ const tokenExecProgram = [
"const fs=require('node:fs');", "const fs=require('node:fs');",
`fs.writeFileSync(${JSON.stringify(tokenMarker)},'1');`, `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(""); ].join("");
const passwordExecProgram = [ const passwordExecProgram = [
"const fs=require('node:fs');", "const fs=require('node:fs');",
`fs.writeFileSync(${JSON.stringify(passwordMarker)},'1');`, `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(""); ].join("");
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({

View File

@@ -250,14 +250,14 @@ describe("sanitizeRenderableText", () => {
}); });
it("preserves long credential-like mixed alnum tokens for copy safety", () => { it("preserves long credential-like mixed alnum tokens for copy safety", () => {
const input = "e3b19c3b87bcf364b23eebb2c276e96ec478956ba1d84c93"; const input = "e3b19c3b87bcf364b23eebb2c276e96ec478956ba1d84c93"; // pragma: allowlist secret
const sanitized = sanitizeRenderableText(input); const sanitized = sanitizeRenderableText(input);
expect(sanitized).toBe(input); expect(sanitized).toBe(input);
}); });
it("preserves quoted credential-like mixed alnum tokens for copy safety", () => { it("preserves quoted credential-like mixed alnum tokens for copy safety", () => {
const input = "'e3b19c3b87bcf364b23eebb2c276e96ec478956ba1d84c93'"; const input = "'e3b19c3b87bcf364b23eebb2c276e96ec478956ba1d84c93'"; // pragma: allowlist secret
const sanitized = sanitizeRenderableText(input); const sanitized = sanitizeRenderableText(input);
expect(sanitized).toBe(input); expect(sanitized).toBe(input);

View File

@@ -15,6 +15,6 @@ describe("maskApiKey", () => {
}); });
it("masks long values with first and last 8 chars", () => { 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
}); });
}); });

View File

@@ -113,7 +113,7 @@ describe("finalizeOnboardingWizard", () => {
it("resolves gateway password SecretRef for probe and TUI", async () => { it("resolves gateway password SecretRef for probe and TUI", async () => {
const previous = process.env.OPENCLAW_GATEWAY_PASSWORD; 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 }) => { const select = vi.fn(async (params: { message: string }) => {
if (params.message === "How do you want to hatch your bot?") { if (params.message === "How do you want to hatch your bot?") {
return "tui"; return "tui";
@@ -179,13 +179,13 @@ describe("finalizeOnboardingWizard", () => {
expect(probeGatewayReachable).toHaveBeenCalledWith( expect(probeGatewayReachable).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
url: "ws://127.0.0.1:18789", url: "ws://127.0.0.1:18789",
password: "resolved-gateway-password", password: "resolved-gateway-password", // pragma: allowlist secret
}), }),
); );
expect(runTui).toHaveBeenCalledWith( expect(runTui).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
url: "ws://127.0.0.1:18789", url: "ws://127.0.0.1:18789",
password: "resolved-gateway-password", password: "resolved-gateway-password", // pragma: allowlist secret
}), }),
); );
}); });

View File

@@ -165,7 +165,7 @@ export async function configureGatewayForOnboarding(
defaults: nextConfig.secrets?.defaults, defaults: nextConfig.secrets?.defaults,
}).ref; }).ref;
const tokenMode = const tokenMode =
flow === "quickstart" && opts.secretInputMode !== "ref" flow === "quickstart" && opts.secretInputMode !== "ref" // pragma: allowlist secret
? quickstartTokenRef ? quickstartTokenRef
? "ref" ? "ref"
: "plaintext" : "plaintext"

View File

@@ -58,7 +58,7 @@ export const de: TranslationMap = {
subtitle: "Wo sich das Dashboard verbindet und wie es sich authentifiziert.", subtitle: "Wo sich das Dashboard verbindet und wie es sich authentifiziert.",
wsUrl: "WebSocket-URL", wsUrl: "WebSocket-URL",
token: "Gateway-Token", token: "Gateway-Token",
password: "Passwort (nicht gespeichert)", password: "Passwort (nicht gespeichert)", // pragma: allowlist secret
sessionKey: "Standard-Sitzungsschlüssel", sessionKey: "Standard-Sitzungsschlüssel",
language: "Sprache", language: "Sprache",
connectHint: "Klicken Sie auf Verbinden, um Verbindungsänderungen anzuwenden.", connectHint: "Klicken Sie auf Verbinden, um Verbindungsänderungen anzuwenden.",

View File

@@ -58,7 +58,7 @@ export const es: TranslationMap = {
subtitle: "Dónde se conecta el panel y cómo se autentica.", subtitle: "Dónde se conecta el panel y cómo se autentica.",
wsUrl: "URL de WebSocket", wsUrl: "URL de WebSocket",
token: "Token de la puerta de enlace", 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", sessionKey: "Clave de sesión predeterminada",
language: "Idioma", language: "Idioma",
connectHint: "Haz clic en Conectar para aplicar los cambios de conexión.", connectHint: "Haz clic en Conectar para aplicar los cambios de conexión.",