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

View File

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

View File

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

View File

@@ -219,7 +219,7 @@
</ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></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>
<title>2026.3.1</title>
@@ -357,7 +357,7 @@
</ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></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>
</channel>
</rss>
</rss>

View File

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

View File

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

View File

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

View File

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

View File

@@ -179,8 +179,8 @@ Request payload (stdin):
Response payload (stdout):
```json
{ "protocolVersion": 1, "values": { "providers/openai/apiKey": "sk-..." } }
```jsonc
{ "protocolVersion": 1, "values": { "providers/openai/apiKey": "<openai-api-key>" } } // pragma: allowlist secret
```
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:
```bash
grep -q 'NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache' ~/.bashrc || cat >> ~/.bashrc <<'EOF'
grep -q 'NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache' ~/.bashrc || cat >> ~/.bashrc <<'EOF' # pragma: allowlist secret
export NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache
mkdir -p /var/tmp/openclaw-compile-cache
export OPENCLAW_NO_RESPAWN=1

View File

@@ -25,14 +25,14 @@ openclaw onboard --kilocode-api-key <key>
Or set the environment variable:
```bash
export KILOCODE_API_KEY="your-api-key"
export KILOCODE_API_KEY="<your-kilocode-api-key>" # pragma: allowlist secret
```
## Config snippet
```json5
{
env: { KILOCODE_API_KEY: "sk-..." },
env: { KILOCODE_API_KEY: "<your-kilocode-api-key>" }, // pragma: allowlist secret
agents: {
defaults: {
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", () => {
const parsed = BlueBubblesConfigSchema.safeParse({
serverUrl: "http://localhost:1234",
password: "secret",
password: "secret", // pragma: allowlist secret
});
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";
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 () => {
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);
};
const fromDirectField = creds.httpTimeoutMs;
if (
typeof fromDirectField === "number" &&
Number.isFinite(fromDirectField) &&
fromDirectField > 0
) {
return clampTimeout(fromDirectField);
}
const envRaw = process.env[FEISHU_HTTP_TIMEOUT_ENV_VAR];
if (envRaw) {
const envValue = Number(envRaw);
@@ -88,8 +97,7 @@ function resolveConfiguredHttpTimeoutMs(creds: FeishuClientCredentials): number
}
const fromConfig = creds.config?.httpTimeoutMs;
const fromDirectField = creds.httpTimeoutMs;
const timeout = fromDirectField ?? fromConfig;
const timeout = fromConfig;
if (typeof timeout !== "number" || !Number.isFinite(timeout) || timeout <= 0) {
return FEISHU_HTTP_TIMEOUT_MS;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -119,9 +119,10 @@ function createAuthStorage(AuthStorageLike: unknown, path: string, creds: PiCred
? withFactory.create(path)
: new (AuthStorageLike as { new (path: string): unknown })(path)
) as PiAuthStorage & {
setRuntimeApiKey?: (provider: string, apiKey: string) => void;
setRuntimeApiKey?: (provider: string, apiKey: string) => void; // pragma: allowlist secret
};
if (typeof withRuntimeOverride.setRuntimeApiKey === "function") {
const hasRuntimeApiKeyOverride = typeof withRuntimeOverride.setRuntimeApiKey === "function"; // pragma: allowlist secret
if (hasRuntimeApiKeyOverride) {
for (const [provider, credential] of Object.entries(creds)) {
if (credential.type === "api_key") {
withRuntimeOverride.setRuntimeApiKey(provider, credential.key);

View File

@@ -1,6 +1,6 @@
import crypto from "node:crypto";
export const NOVNC_PASSWORD_ENV_KEY = "OPENCLAW_BROWSER_NOVNC_PASSWORD";
export const NOVNC_PASSWORD_ENV_KEY = "OPENCLAW_BROWSER_NOVNC_PASSWORD"; // pragma: allowlist secret
const NOVNC_TOKEN_TTL_MS = 60 * 1000;
const NOVNC_PASSWORD_LENGTH = 8;
const NOVNC_PASSWORD_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -184,7 +184,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv("openclaw-onboard-minimax-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "minimax-api",
minimaxApiKey: "sk-minimax-test",
minimaxApiKey: "sk-minimax-test", // pragma: allowlist secret
});
expect(cfg.auth?.profiles?.["minimax:default"]?.provider).toBe("minimax");
@@ -203,7 +203,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv("openclaw-onboard-minimax-cn-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "minimax-api-key-cn",
minimaxApiKey: "sk-minimax-test",
minimaxApiKey: "sk-minimax-test", // pragma: allowlist secret
});
expect(cfg.auth?.profiles?.["minimax-cn:default"]?.provider).toBe("minimax-cn");
@@ -222,7 +222,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv("openclaw-onboard-zai-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "zai-api-key",
zaiApiKey: "zai-test-key",
zaiApiKey: "zai-test-key", // pragma: allowlist secret
});
expect(cfg.auth?.profiles?.["zai:default"]?.provider).toBe("zai");
@@ -237,7 +237,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv("openclaw-onboard-zai-cn-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "zai-coding-cn",
zaiApiKey: "zai-test-key",
zaiApiKey: "zai-test-key", // pragma: allowlist secret
});
expect(cfg.models?.providers?.zai?.baseUrl).toBe(
@@ -264,7 +264,7 @@ describe("onboard (non-interactive): provider auth", () => {
it("infers Mistral auth choice from --mistral-api-key and sets default model", async () => {
await withOnboardEnv("openclaw-onboard-mistral-infer-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {
mistralApiKey: "mistral-test-key",
mistralApiKey: "mistral-test-key", // pragma: allowlist secret
});
expect(cfg.auth?.profiles?.["mistral:default"]?.provider).toBe("mistral");
@@ -282,7 +282,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv("openclaw-onboard-volcengine-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "volcengine-api-key",
volcengineApiKey: "volcengine-test-key",
volcengineApiKey: "volcengine-test-key", // pragma: allowlist secret
});
expect(cfg.agents?.defaults?.model?.primary).toBe("volcengine-plan/ark-code-latest");
@@ -292,7 +292,7 @@ describe("onboard (non-interactive): provider auth", () => {
it("infers BytePlus auth choice from --byteplus-api-key and sets default model", async () => {
await withOnboardEnv("openclaw-onboard-byteplus-infer-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {
byteplusApiKey: "byteplus-test-key",
byteplusApiKey: "byteplus-test-key", // pragma: allowlist secret
});
expect(cfg.agents?.defaults?.model?.primary).toBe("byteplus-plan/ark-code-latest");
@@ -303,7 +303,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv("openclaw-onboard-ai-gateway-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "ai-gateway-api-key",
aiGatewayApiKey: "gateway-test-key",
aiGatewayApiKey: "gateway-test-key", // pragma: allowlist secret
});
expect(cfg.auth?.profiles?.["vercel-ai-gateway:default"]?.provider).toBe("vercel-ai-gateway");
@@ -350,7 +350,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv("openclaw-onboard-openai-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "openai-api-key",
openaiApiKey: "sk-openai-test",
openaiApiKey: "sk-openai-test", // pragma: allowlist secret
});
expect(cfg.agents?.defaults?.model?.primary).toBe(OPENAI_DEFAULT_MODEL);
@@ -410,10 +410,10 @@ describe("onboard (non-interactive): provider auth", () => {
"fails fast for $name when --secret-input-mode ref uses explicit key without env and does not leak the key",
async ({ prefix, authChoice, optionKey, flagName, envVar }) => {
await withOnboardEnv(prefix, async ({ runtime }) => {
const providedSecret = `${envVar.toLowerCase()}-should-not-leak`;
const providedSecret = `${envVar.toLowerCase()}-should-not-leak`; // pragma: allowlist secret
const options: Record<string, unknown> = {
authChoice,
secretInputMode: "ref",
secretInputMode: "ref", // pragma: allowlist secret
[optionKey]: providedSecret,
skipSkills: true,
};
@@ -447,12 +447,12 @@ describe("onboard (non-interactive): provider auth", () => {
await withEnvAsync(
{
OPENCODE_API_KEY: undefined,
OPENCODE_ZEN_API_KEY: "opencode-zen-env-key",
OPENCODE_ZEN_API_KEY: "opencode-zen-env-key", // pragma: allowlist secret
},
async () => {
await runNonInteractiveOnboardingWithDefaults(runtime, {
authChoice: "opencode-zen",
secretInputMode: "ref",
secretInputMode: "ref", // pragma: allowlist secret
skipSkills: true,
});
@@ -487,7 +487,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv("openclaw-onboard-litellm-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {
authChoice: "litellm-api-key",
litellmApiKey: "litellm-test-key",
litellmApiKey: "litellm-test-key", // pragma: allowlist secret
});
expect(cfg.auth?.profiles?.["litellm:default"]?.provider).toBe("litellm");
@@ -519,7 +519,7 @@ describe("onboard (non-interactive): provider auth", () => {
await runNonInteractiveOnboardingWithDefaults(runtime, {
cloudflareAiGatewayAccountId: "cf-account-id",
cloudflareAiGatewayGatewayId: "cf-gateway-id",
cloudflareAiGatewayApiKey: "cf-gateway-test-key",
cloudflareAiGatewayApiKey: "cf-gateway-test-key", // pragma: allowlist secret
skipSkills: true,
...options,
});
@@ -543,7 +543,7 @@ describe("onboard (non-interactive): provider auth", () => {
it("infers Together auth choice from --together-api-key and sets default model", async () => {
await withOnboardEnv("openclaw-onboard-together-infer-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {
togetherApiKey: "together-test-key",
togetherApiKey: "together-test-key", // pragma: allowlist secret
});
expect(cfg.auth?.profiles?.["together:default"]?.provider).toBe("together");
@@ -560,7 +560,7 @@ describe("onboard (non-interactive): provider auth", () => {
it("infers QIANFAN auth choice from --qianfan-api-key and sets default model", async () => {
await withOnboardEnv("openclaw-onboard-qianfan-infer-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {
qianfanApiKey: "qianfan-test-key",
qianfanApiKey: "qianfan-test-key", // pragma: allowlist secret
});
expect(cfg.auth?.profiles?.["qianfan:default"]?.provider).toBe("qianfan");
@@ -579,7 +579,7 @@ describe("onboard (non-interactive): provider auth", () => {
await runNonInteractiveOnboardingWithDefaults(runtime, {
authChoice: "custom-api-key",
customBaseUrl: "https://llm.example.com/v1",
customApiKey: "custom-test-key",
customApiKey: "custom-test-key", // pragma: allowlist secret
customModelId: "foo-large",
customCompatibility: "anthropic",
skipSkills: true,
@@ -603,7 +603,7 @@ describe("onboard (non-interactive): provider auth", () => {
await runNonInteractiveOnboardingWithDefaults(runtime, {
customBaseUrl: "https://models.custom.local/v1",
customModelId: "local-large",
customApiKey: "custom-test-key",
customApiKey: "custom-test-key", // pragma: allowlist secret
skipSkills: true,
});
@@ -624,7 +624,7 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv(
"openclaw-onboard-custom-provider-env-fallback-",
async ({ configPath, runtime }) => {
process.env.CUSTOM_API_KEY = "custom-env-key";
process.env.CUSTOM_API_KEY = "custom-env-key"; // pragma: allowlist secret
await runCustomLocalNonInteractive(runtime);
expect(await readCustomLocalProviderApiKey(configPath)).toBe("custom-env-key");
},
@@ -635,9 +635,9 @@ describe("onboard (non-interactive): provider auth", () => {
await withOnboardEnv(
"openclaw-onboard-custom-provider-env-ref-",
async ({ configPath, runtime }) => {
process.env.CUSTOM_API_KEY = "custom-env-key";
process.env.CUSTOM_API_KEY = "custom-env-key"; // pragma: allowlist secret
await runCustomLocalNonInteractive(runtime, {
secretInputMode: "ref",
secretInputMode: "ref", // pragma: allowlist secret
});
expect(await readCustomLocalProviderApiKeyInput(configPath)).toEqual({
source: "env",
@@ -650,12 +650,12 @@ describe("onboard (non-interactive): provider auth", () => {
it("fails fast for custom provider ref mode when --custom-api-key is set but CUSTOM_API_KEY env is missing", async () => {
await withOnboardEnv("openclaw-onboard-custom-provider-ref-flag-", async ({ runtime }) => {
const providedSecret = "custom-inline-key-should-not-leak";
const providedSecret = "custom-inline-key-should-not-leak"; // pragma: allowlist secret
await withEnvAsync({ CUSTOM_API_KEY: undefined }, async () => {
let thrown: Error | undefined;
try {
await runCustomLocalNonInteractive(runtime, {
secretInputMode: "ref",
secretInputMode: "ref", // pragma: allowlist secret
customApiKey: providedSecret,
});
} catch (error) {
@@ -731,7 +731,7 @@ describe("onboard (non-interactive): provider auth", () => {
async ({ runtime }) => {
await expect(
runNonInteractiveOnboardingWithDefaults(runtime, {
customApiKey: "custom-test-key",
customApiKey: "custom-test-key", // pragma: allowlist secret
skipSkills: true,
}),
).rejects.toThrow('Auth choice "custom-api-key" requires a base URL and model ID.');

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -177,7 +177,10 @@ const buildAccountNotes = (params: {
if (snapshot.appTokenSource && snapshot.appTokenSource !== "none") {
notes.push(`app:${snapshot.appTokenSource}`);
}
if (snapshot.signingSecretSource && snapshot.signingSecretSource !== "none") {
if (
snapshot.signingSecretSource &&
snapshot.signingSecretSource !== "none" /* pragma: allowlist secret */
) {
notes.push(`signing:${snapshot.signingSecretSource}`);
}
if (hasConfiguredUnavailableCredentialStatus(entry.account)) {

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@ import { resolveSecretInputRef } from "../config/types.secrets.js";
import { secretRefKey } from "../secrets/ref-contract.js";
import { resolveSecretRefValues } from "../secrets/resolve.js";
export type SecretInputUnresolvedReasonStyle = "generic" | "detailed";
export type SecretInputUnresolvedReasonStyle = "generic" | "detailed"; // pragma: allowlist secret
function trimToUndefined(value: unknown): string | undefined {
if (typeof value !== "string") {

View File

@@ -115,7 +115,7 @@ function mockSuccessfulWakeConfig(nodeId: string) {
value: {
teamId: "TEAM123",
keyId: "KEY123",
privateKey: "-----BEGIN PRIVATE KEY-----\nabc\n-----END PRIVATE KEY-----",
privateKey: "-----BEGIN PRIVATE KEY-----\nabc\n-----END PRIVATE KEY-----", // pragma: allowlist secret
},
});
mocks.sendApnsBackgroundWake.mockResolvedValue({

View File

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

View File

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

View File

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

View File

@@ -69,7 +69,10 @@ const buildAccountDetails = (params: {
if (snapshot.appTokenSource && snapshot.appTokenSource !== "none") {
details.push(`app:${snapshot.appTokenSource}`);
}
if (snapshot.signingSecretSource && snapshot.signingSecretSource !== "none") {
if (
snapshot.signingSecretSource &&
snapshot.signingSecretSource !== "none" /* pragma: allowlist secret */
) {
details.push(`signing:${snapshot.signingSecretSource}`);
}
if (hasConfiguredUnavailableCredentialStatus(params.entry.account)) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -298,7 +298,8 @@ function applyConfigTargetMutations(params: {
}
const targetPathSegments = resolved.pathSegments;
if (resolved.entry.secretShape === "sibling_ref") {
const usesSiblingRef = resolved.entry.secretShape === "sibling_ref"; // pragma: allowlist secret
if (usesSiblingRef) {
const previous = getPath(params.nextConfig, targetPathSegments);
if (isNonEmptyString(previous)) {
scrubbedValues.add(previous.trim());
@@ -530,7 +531,8 @@ function applyAuthProfileTargetMutation(params: {
store,
});
const targetPathSegments = params.resolved.pathSegments;
if (params.resolved.entry.secretShape === "sibling_ref") {
const usesSiblingRef = params.resolved.entry.secretShape === "sibling_ref"; // pragma: allowlist secret
if (usesSiblingRef) {
const previous = getPath(store, targetPathSegments);
if (isNonEmptyString(previous)) {
params.scrubbedValues.add(previous.trim());

View File

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

View File

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

View File

@@ -79,7 +79,9 @@ export function analyzeCommandSecretAssignmentsFromSnapshot(params: {
value: resolved,
});
if (target.entry.secretShape === "sibling_ref" && explicitRef && inlineCandidateRef) {
const hasCompetingSiblingRef =
target.entry.secretShape === "sibling_ref" && explicitRef && inlineCandidateRef; // pragma: allowlist secret
if (hasCompetingSiblingRef) {
diagnostics.push(
`${target.path}: both inline and sibling ref were present; sibling ref took precedence.`,
);

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import { isNonEmptyString, isRecord } from "./shared.js";
export type SecretExpectedResolvedValue = "string" | "string-or-object";
export type SecretExpectedResolvedValue = "string" | "string-or-object"; // pragma: allowlist secret
export function isExpectedResolvedSecretValue(
value: unknown,

View File

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

View File

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

View File

@@ -47,7 +47,8 @@ export function compileTargetRegistryEntry(
const pathDynamicTokenCount = countDynamicPatternTokens(pathTokens);
const refPathTokens = entry.refPathPattern ? parsePathPattern(entry.refPathPattern) : undefined;
const refPathDynamicTokenCount = refPathTokens ? countDynamicPatternTokens(refPathTokens) : 0;
if (entry.secretShape === "sibling_ref" && !refPathTokens) {
const requiresSiblingRefPath = entry.secretShape === "sibling_ref"; // pragma: allowlist secret
if (requiresSiblingRefPath && !refPathTokens) {
throw new Error(`Missing refPathPattern for sibling_ref target: ${entry.id}`);
}
if (refPathTokens && refPathDynamicTokenCount !== pathDynamicTokenCount) {

View File

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

View File

@@ -118,7 +118,7 @@ describe("resolveGatewayConnection", () => {
gateway: {
mode: "local",
auth: {
password: "config-password",
password: "config-password", // pragma: allowlist secret
},
},
});
@@ -134,7 +134,7 @@ describe("resolveGatewayConnection", () => {
mode: "local",
auth: {
token: "config-token",
password: "config-password",
password: "config-password", // pragma: allowlist secret
},
},
});
@@ -180,13 +180,15 @@ describe("resolveGatewayConnection", () => {
loadConfig.mockReturnValue({
gateway: {
mode: "remote",
remote: { url: "wss://remote.example/ws", token: "remote-token", password: "remote-pass" },
remote: { url: "wss://remote.example/ws", token: "remote-token", password: "remote-pass" }, // pragma: allowlist secret
},
});
await withEnvAsync({ OPENCLAW_GATEWAY_PASSWORD: "env-pass" }, async () => {
const gatewayPasswordEnv = "OPENCLAW_GATEWAY_PASSWORD"; // pragma: allowlist secret
const gatewayPassword = "env-pass"; // pragma: allowlist secret
await withEnvAsync({ [gatewayPasswordEnv]: gatewayPassword }, async () => {
const result = await resolveGatewayConnection({});
expect(result.password).toBe("env-pass");
expect(result.password).toBe(gatewayPassword);
});
});
@@ -263,12 +265,12 @@ describe("resolveGatewayConnection", () => {
const tokenExecProgram = [
"const fs=require('node:fs');",
`fs.writeFileSync(${JSON.stringify(tokenMarker)},'1');`,
"process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { TOKEN_SECRET: 'token-from-exec' } }));",
"process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { TOKEN_SECRET: 'token-from-exec' } }));", // pragma: allowlist secret
].join("");
const passwordExecProgram = [
"const fs=require('node:fs');",
`fs.writeFileSync(${JSON.stringify(passwordMarker)},'1');`,
"process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { PASSWORD_SECRET: 'password-from-exec' } }));",
"process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { PASSWORD_SECRET: 'password-from-exec' } }));", // pragma: allowlist secret
].join("");
loadConfig.mockReturnValue({
@@ -316,12 +318,12 @@ describe("resolveGatewayConnection", () => {
const tokenExecProgram = [
"const fs=require('node:fs');",
`fs.writeFileSync(${JSON.stringify(tokenMarker)},'1');`,
"process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { TOKEN_SECRET: 'token-from-exec' } }));",
"process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { TOKEN_SECRET: 'token-from-exec' } }));", // pragma: allowlist secret
].join("");
const passwordExecProgram = [
"const fs=require('node:fs');",
`fs.writeFileSync(${JSON.stringify(passwordMarker)},'1');`,
"process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { PASSWORD_SECRET: 'password-from-exec' } }));",
"process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { PASSWORD_SECRET: 'password-from-exec' } }));", // pragma: allowlist secret
].join("");
loadConfig.mockReturnValue({

View File

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

View File

@@ -15,6 +15,6 @@ describe("maskApiKey", () => {
});
it("masks long values with first and last 8 chars", () => {
expect(maskApiKey("1234567890abcdefghijklmnop")).toBe("12345678...ijklmnop");
expect(maskApiKey("1234567890abcdefghijklmnop")).toBe("12345678...ijklmnop"); // pragma: allowlist secret
});
});

View File

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

View File

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

View File

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

View File

@@ -58,7 +58,7 @@ export const es: TranslationMap = {
subtitle: "Dónde se conecta el panel y cómo se autentica.",
wsUrl: "URL de WebSocket",
token: "Token de la puerta de enlace",
password: "Contraseña (no se guarda)",
password: "Contraseña (no se guarda)", // pragma: allowlist secret
sessionKey: "Clave de sesión predeterminada",
language: "Idioma",
connectHint: "Haz clic en Conectar para aplicar los cambios de conexión.",