fix(telegram): resolve status SecretRefs with provider-safe env checks

Landed from #39130 by @neocody.

Co-authored-by: Cody <25426121+neocody@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-03-07 20:50:07 +00:00
parent 2015ab3194
commit 330579ef96
3 changed files with 141 additions and 7 deletions

View File

@@ -0,0 +1,79 @@
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import { withEnv } from "../test-utils/env.js";
import { inspectTelegramAccount } from "./account-inspect.js";
describe("inspectTelegramAccount SecretRef resolution", () => {
it("resolves default env SecretRef templates in read-only status paths", () => {
withEnv({ TG_STATUS_TOKEN: "123:token" }, () => {
const cfg: OpenClawConfig = {
channels: {
telegram: {
botToken: "${TG_STATUS_TOKEN}",
},
},
};
const account = inspectTelegramAccount({ cfg, accountId: "default" });
expect(account.tokenSource).toBe("env");
expect(account.tokenStatus).toBe("available");
expect(account.token).toBe("123:token");
});
});
it("respects env provider allowlists in read-only status paths", () => {
withEnv({ TG_NOT_ALLOWED: "123:token" }, () => {
const cfg: OpenClawConfig = {
secrets: {
defaults: {
env: "secure-env",
},
providers: {
"secure-env": {
source: "env",
allowlist: ["TG_ALLOWED"],
},
},
},
channels: {
telegram: {
botToken: "${TG_NOT_ALLOWED}",
},
},
};
const account = inspectTelegramAccount({ cfg, accountId: "default" });
expect(account.tokenSource).toBe("env");
expect(account.tokenStatus).toBe("configured_unavailable");
expect(account.token).toBe("");
});
});
it("does not read env values for non-env providers", () => {
withEnv({ TG_EXEC_PROVIDER: "123:token" }, () => {
const cfg: OpenClawConfig = {
secrets: {
defaults: {
env: "exec-provider",
},
providers: {
"exec-provider": {
source: "exec",
command: "/usr/bin/env",
},
},
},
channels: {
telegram: {
botToken: "${TG_EXEC_PROVIDER}",
},
},
};
const account = inspectTelegramAccount({ cfg, accountId: "default" });
expect(account.tokenSource).toBe("env");
expect(account.tokenStatus).toBe("configured_unavailable");
expect(account.token).toBe("");
});
});
});

View File

@@ -1,9 +1,14 @@
import fs from "node:fs";
import type { OpenClawConfig } from "../config/config.js";
import { hasConfiguredSecretInput, normalizeSecretInputString } from "../config/types.secrets.js";
import {
coerceSecretRef,
hasConfiguredSecretInput,
normalizeSecretInputString,
} from "../config/types.secrets.js";
import type { TelegramAccountConfig } from "../config/types.telegram.js";
import { resolveAccountWithDefaultFallback } from "../plugin-sdk/account-resolution.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
import { resolveDefaultSecretProviderAlias } from "../secrets/ref-contract.js";
import {
mergeTelegramAccountConfig,
resolveDefaultTelegramAccountId,
@@ -55,12 +60,58 @@ function inspectTokenFile(pathValue: unknown): {
}
}
function inspectTokenValue(value: unknown): {
function canResolveEnvSecretRefInReadOnlyPath(params: {
cfg: OpenClawConfig;
provider: string;
id: string;
}): boolean {
const providerConfig = params.cfg.secrets?.providers?.[params.provider];
if (!providerConfig) {
return params.provider === resolveDefaultSecretProviderAlias(params.cfg, "env");
}
if (providerConfig.source !== "env") {
return false;
}
const allowlist = providerConfig.allowlist;
return !allowlist || allowlist.includes(params.id);
}
function inspectTokenValue(params: { cfg: OpenClawConfig; value: unknown }): {
token: string;
tokenSource: "config" | "none";
tokenSource: "config" | "env" | "none";
tokenStatus: TelegramCredentialStatus;
} | null {
const token = normalizeSecretInputString(value);
// Try to resolve env-based SecretRefs from process.env for read-only inspection
const ref = coerceSecretRef(params.value, params.cfg.secrets?.defaults);
if (ref?.source === "env") {
if (
!canResolveEnvSecretRefInReadOnlyPath({
cfg: params.cfg,
provider: ref.provider,
id: ref.id,
})
) {
return {
token: "",
tokenSource: "env",
tokenStatus: "configured_unavailable",
};
}
const envValue = process.env[ref.id];
if (envValue && envValue.trim()) {
return {
token: envValue.trim(),
tokenSource: "env",
tokenStatus: "available",
};
}
return {
token: "",
tokenSource: "env",
tokenStatus: "configured_unavailable",
};
}
const token = normalizeSecretInputString(params.value);
if (token) {
return {
token,
@@ -68,7 +119,7 @@ function inspectTokenValue(value: unknown): {
tokenStatus: "available",
};
}
if (hasConfiguredSecretInput(value)) {
if (hasConfiguredSecretInput(params.value, params.cfg.secrets?.defaults)) {
return {
token: "",
tokenSource: "config",
@@ -102,7 +153,7 @@ function inspectTelegramAccountPrimary(params: {
};
}
const accountToken = inspectTokenValue(accountConfig?.botToken);
const accountToken = inspectTokenValue({ cfg: params.cfg, value: accountConfig?.botToken });
if (accountToken) {
return {
accountId,
@@ -130,7 +181,10 @@ function inspectTelegramAccountPrimary(params: {
};
}
const channelToken = inspectTokenValue(params.cfg.channels?.telegram?.botToken);
const channelToken = inspectTokenValue({
cfg: params.cfg,
value: params.cfg.channels?.telegram?.botToken,
});
if (channelToken) {
return {
accountId,