fix(telegram): resolve env SecretRef bot tokens before startup#56784
fix(telegram): resolve env SecretRef bot tokens before startup#56784davetanaka wants to merge 1 commit intoopenclaw:mainfrom
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ee8834f115
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const resolved = await resolveConfiguredSecretInputString({ | ||
| config: params.cfg, | ||
| env: process.env, | ||
| value: envToken, | ||
| path: "TELEGRAM_BOT_TOKEN", |
There was a problem hiding this comment.
Resolve non-template SecretRef env tokens before returning
This branch still passes raw TELEGRAM_BOT_TOKEN values through when they are not parsed as a built-in SecretRef shape, so strings like op://... are returned unchanged and used as the bot token. That means the startup failure this change targets can still occur in production even though the new test passes, because the test mocks resolveConfiguredSecretInputString to return a resolved token for that input instead of exercising real parsing behavior.
Useful? React with 👍 / 👎.
Greptile SummaryThis PR adds a Issues found:
Confidence Score: 3/5Not safe to merge without addressing the explicit SecretRef fallback — the primary new code path silently passes unresolved SecretRef strings to Telegram in a common scenario. A P1 logic defect exists in the explicit-token branch of resolveMonitorTelegramToken: the equality check used to decide whether to throw (explicitToken === params.resolvedAccountToken) is almost never true when an explicit SecretRef is passed via opts.token, so the function silently returns the unresolved op://... string. This directly undermines the PR's stated goal. Additionally there is a P2 env-cleanup gap in the test. extensions/telegram/src/monitor.ts (explicit-token SecretRef fallback logic, lines 96–101) and extensions/telegram/src/monitor.test.ts (missing vi.unstubAllEnvs() in afterEach).
|
| Filename | Overview |
|---|---|
| extensions/telegram/src/monitor.ts | Adds resolveMonitorTelegramToken helper to resolve SecretRef-style bot tokens before startup. The env-token path (source=env) is correct; the explicit-token path has a logic flaw where a failed SecretRef resolution falls back to returning the unresolved string instead of throwing. |
| extensions/telegram/src/monitor.test.ts | Adds mock wiring and regression test for env SecretRef resolution. The test correctly covers the primary use case but uses vi.stubEnv without a corresponding vi.unstubAllEnvs() in afterEach, leaking the env stub to subsequent tests. |
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/telegram/src/monitor.ts
Line: 96-101
Comment:
**Unresolved explicit SecretRef silently passed to Telegram**
When `opts.token` is a SecretRef string (e.g. `op://vault/telegram`) and `resolveConfiguredSecretInputString` cannot resolve it, the code reaches line 101 only if `explicitToken !== params.resolvedAccountToken`. In practice this condition is almost always false — the explicit token passed via `opts.token` and `account.token` typically come from different sources — so the `throw` on line 97 is never reached for a failed explicit SecretRef. The result is that `return explicitToken` silently returns the unresolved `op://...` string as the bot token, causing the same `404 Not Found` / `deleteWebhook failed` failures this PR claims to prevent.
Consider throwing unconditionally when `resolved.value` is falsy for the explicit-token path, instead of relying on the `explicitToken === resolvedAccountToken` equality check:
```suggestion
if (resolved.value) {
return resolved.value;
}
throw new Error(
`Telegram bot token unavailable: ${resolved.unresolvedRefReason ?? "explicit token SecretRef is unresolved."}`,
);
}
```
If you intentionally want to allow plaintext fallback for non-SecretRef values, `resolveConfiguredSecretInputString` should return the original plaintext as `resolved.value` for non-SecretRef inputs, making the fallback unreachable for valid tokens.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/telegram/src/monitor.test.ts
Line: 424
Comment:
**`vi.stubEnv` not cleaned up — env leaks to subsequent tests**
`vi.stubEnv("TELEGRAM_BOT_TOKEN", ...)` stubs the env variable for the remainder of the test suite unless `vi.unstubAllEnvs()` is called. The current `afterEach` block only calls `consoleErrorSpy?.mockRestore()`, so `TELEGRAM_BOT_TOKEN` stays set to `"op://secrets/telegram/credential"` for every test that runs after this one.
The repo testing guideline explicitly requires: _"Write tests to clean up timers, env, globals, mocks, sockets, temp dirs, and module state so `--isolate=false` stays green._"
Add `vi.unstubAllEnvs()` to the existing `afterEach`:
```ts
afterEach(() => {
consoleErrorSpy?.mockRestore();
vi.unstubAllEnvs();
});
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(telegram): resolve env SecretRef bot..." | Re-trigger Greptile
| if (explicitToken === params.resolvedAccountToken) { | ||
| throw new Error( | ||
| `Telegram bot token unavailable: ${resolved.unresolvedRefReason ?? "explicit token SecretRef is unresolved."}`, | ||
| ); | ||
| } | ||
| return explicitToken; |
There was a problem hiding this comment.
Unresolved explicit SecretRef silently passed to Telegram
When opts.token is a SecretRef string (e.g. op://vault/telegram) and resolveConfiguredSecretInputString cannot resolve it, the code reaches line 101 only if explicitToken !== params.resolvedAccountToken. In practice this condition is almost always false — the explicit token passed via opts.token and account.token typically come from different sources — so the throw on line 97 is never reached for a failed explicit SecretRef. The result is that return explicitToken silently returns the unresolved op://... string as the bot token, causing the same 404 Not Found / deleteWebhook failed failures this PR claims to prevent.
Consider throwing unconditionally when resolved.value is falsy for the explicit-token path, instead of relying on the explicitToken === resolvedAccountToken equality check:
| if (explicitToken === params.resolvedAccountToken) { | |
| throw new Error( | |
| `Telegram bot token unavailable: ${resolved.unresolvedRefReason ?? "explicit token SecretRef is unresolved."}`, | |
| ); | |
| } | |
| return explicitToken; | |
| if (resolved.value) { | |
| return resolved.value; | |
| } | |
| throw new Error( | |
| `Telegram bot token unavailable: ${resolved.unresolvedRefReason ?? "explicit token SecretRef is unresolved."}`, | |
| ); | |
| } |
If you intentionally want to allow plaintext fallback for non-SecretRef values, resolveConfiguredSecretInputString should return the original plaintext as resolved.value for non-SecretRef inputs, making the fallback unreachable for valid tokens.
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/telegram/src/monitor.ts
Line: 96-101
Comment:
**Unresolved explicit SecretRef silently passed to Telegram**
When `opts.token` is a SecretRef string (e.g. `op://vault/telegram`) and `resolveConfiguredSecretInputString` cannot resolve it, the code reaches line 101 only if `explicitToken !== params.resolvedAccountToken`. In practice this condition is almost always false — the explicit token passed via `opts.token` and `account.token` typically come from different sources — so the `throw` on line 97 is never reached for a failed explicit SecretRef. The result is that `return explicitToken` silently returns the unresolved `op://...` string as the bot token, causing the same `404 Not Found` / `deleteWebhook failed` failures this PR claims to prevent.
Consider throwing unconditionally when `resolved.value` is falsy for the explicit-token path, instead of relying on the `explicitToken === resolvedAccountToken` equality check:
```suggestion
if (resolved.value) {
return resolved.value;
}
throw new Error(
`Telegram bot token unavailable: ${resolved.unresolvedRefReason ?? "explicit token SecretRef is unresolved."}`,
);
}
```
If you intentionally want to allow plaintext fallback for non-SecretRef values, `resolveConfiguredSecretInputString` should return the original plaintext as `resolved.value` for non-SecretRef inputs, making the fallback unreachable for valid tokens.
How can I resolve this? If you propose a fix, please make it concise.| }); | ||
|
|
||
| it("resolves env SecretRef token before starting the provider", async () => { | ||
| vi.stubEnv("TELEGRAM_BOT_TOKEN", "op://secrets/telegram/credential"); |
There was a problem hiding this comment.
vi.stubEnv not cleaned up — env leaks to subsequent tests
vi.stubEnv("TELEGRAM_BOT_TOKEN", ...) stubs the env variable for the remainder of the test suite unless vi.unstubAllEnvs() is called. The current afterEach block only calls consoleErrorSpy?.mockRestore(), so TELEGRAM_BOT_TOKEN stays set to "op://secrets/telegram/credential" for every test that runs after this one.
The repo testing guideline explicitly requires: "Write tests to clean up timers, env, globals, mocks, sockets, temp dirs, and module state so --isolate=false stays green."
Add vi.unstubAllEnvs() to the existing afterEach:
afterEach(() => {
consoleErrorSpy?.mockRestore();
vi.unstubAllEnvs();
});Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/telegram/src/monitor.test.ts
Line: 424
Comment:
**`vi.stubEnv` not cleaned up — env leaks to subsequent tests**
`vi.stubEnv("TELEGRAM_BOT_TOKEN", ...)` stubs the env variable for the remainder of the test suite unless `vi.unstubAllEnvs()` is called. The current `afterEach` block only calls `consoleErrorSpy?.mockRestore()`, so `TELEGRAM_BOT_TOKEN` stays set to `"op://secrets/telegram/credential"` for every test that runs after this one.
The repo testing guideline explicitly requires: _"Write tests to clean up timers, env, globals, mocks, sockets, temp dirs, and module state so `--isolate=false` stays green._"
Add `vi.unstubAllEnvs()` to the existing `afterEach`:
```ts
afterEach(() => {
consoleErrorSpy?.mockRestore();
vi.unstubAllEnvs();
});
```
How can I resolve this? If you propose a fix, please make it concise.|
This PR was green on March 29, but |
|
Thanks for this Telegram SecretRef fix proposal, @davetanaka. Outcome: this PR is superseded by #66818, which landed a broader version of the same runtime fix. Why #66818 covers this: Telegram token resolution now supports env-backed SecretRefs at runtime, preserves strict behavior for unresolved non-env refs, and enforces env provider policy checks. |
OpenClaw Upstream PR Draft - Telegram SecretRef
Why this PR matters
This is a small but meaningful community contribution.
Recommended PR title
fix(telegram): resolve env SecretRef bot tokens before provider startupAlternative:
fix(telegram): support SecretRef-style TELEGRAM_BOT_TOKEN during monitor startupSuggested PR body
Short issue-style summary
If you want a short companion issue or note first:
Suggested submission flow
Scope to include
Include:
extensions/telegram/src/monitor.tsextensions/telegram/src/monitor.test.tsDo not include:
.enveditsdist/patching for live recoveryConfidence note
Good to mention if asked: