Skip to content

fix(security): respect top-level restriction for Telegram group allowFrom wildcard#48804

Closed
yelog wants to merge 2 commits intoopenclaw:mainfrom
yelog:fix/security-audit-group-allowfrom-wildcard
Closed

fix(security): respect top-level restriction for Telegram group allowFrom wildcard#48804
yelog wants to merge 2 commits intoopenclaw:mainfrom
yelog:fix/security-audit-group-allowfrom-wildcard

Conversation

@yelog
Copy link
Copy Markdown
Contributor

@yelog yelog commented Mar 17, 2026

Summary

  • Security audit now correctly handles per-group allowFrom wildcard when top-level groupAllowFrom is already restricted
  • Previously, the audit only checked top-level wildcards but missed group-level ones
  • Fixes false CRITICAL alerts for intentional configurations where group-level wildcard is safe due to top-level restriction

Changes

  1. Added detection of wildcard in groups.<id>.allowFrom and groups.<id>.topics.<topicId>.allowFrom
  2. Added hasTopLevelRestriction check to verify if top-level groupAllowFrom or pairing store restricts access
  3. Only report CRITICAL for group-level wildcard when no top-level restriction exists

Test plan

  • Added test: verifies no CRITICAL when top-level groupAllowFrom is restricted
  • Added test: verifies CRITICAL when no top-level restriction exists

Closes #48687

@yelog yelog requested a review from a team as a code owner March 17, 2026 07:05
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b7581b7e56

ℹ️ 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".

continue;
}

if (groupLevelWildcards.length > 0 && !hasTopLevelRestriction) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Flag per-group Telegram wildcard even with top-level allowlist

This guard suppresses the new critical finding whenever hasTopLevelRestriction is true, but Telegram runtime auth does not intersect top-level groupAllowFrom with per-group/topic overrides; it replaces it (groupAllowOverride ?? groupAllowFrom in extensions/telegram/src/bot-message-context.ts and extensions/telegram/src/bot/helpers.ts). In configurations like groupAllowFrom: ["123456"] plus groups.<id>.allowFrom: ["*"], group messages are still effectively wildcard-authorized, so the audit now returns a false negative for a real command-control exposure.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 17, 2026

Greptile Summary

This PR fixes a false-positive security audit issue where a per-group allowFrom: ["*"] wildcard was always flagged as CRITICAL, even when a top-level groupAllowFrom restriction already limits access globally. The fix adds groupLevelWildcards collection and a hasTopLevelRestriction guard so that the CRITICAL is only raised when there truly is no global gate in place. The approach is sound and the two new tests cover the key scenarios.

Issues to address:

  • Behavioral regression (audit-channel.ts lines 816–827): The top-level wildcard block was moved earlier in the flow, before the invalidTelegramAllowFromEntries check. If a config contains both a top-level * and other non-numeric entries (e.g., @username), those invalid entries will no longer be reported, silently hiding misconfigurations that would resurface after the wildcard is removed.
  • Spurious WARN alongside CRITICAL (audit-channel.ts lines 829–840): When the group-level wildcard CRITICAL is pushed, code falls through to the invalidTelegramAllowFromEntries check. Since * is non-numeric, it will also emit a WARN (channels.telegram.allowFrom.invalid_entries) for the same * entry — confusing output where a CRITICAL and a WARN describe the same root cause.
  • Unverified storeAllowFrom semantics (audit-channel.ts lines 812–814): The pairing store entries are used in hasTopLevelRestriction, which suppresses the group-level CRITICAL. If storeAllowFrom does not actually filter Telegram group senders at runtime (e.g., it only gates DM-paired users), this could produce false negatives, silently allowing group-level wildcard access.

Confidence Score: 3/5

  • The core logic is directionally correct but has a potential false-negative risk in the hasTopLevelRestriction check and a behavioral regression around invalid-entry reporting.
  • The primary goal of the PR (suppressing CRITICAL for group-level wildcards when top-level restriction exists) is implemented correctly and is tested. However, the reordering of checks introduces a regression where co-present invalid entries go unreported when a top-level wildcard is present, and the storeAllowFrom-based restriction path may produce audit false negatives if the store doesn't actually gate group access at runtime. These concerns lower confidence below a clean merge.
  • src/security/audit-channel.ts — specifically the hasTopLevelRestriction definition and the reordering of the wildcard check relative to invalidTelegramAllowFromEntries.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/security/audit-channel.ts
Line: 816-827

Comment:
**Early `continue` suppresses invalid-entry warnings**

Moving the top-level wildcard block before `invalidTelegramAllowFromEntries` means that if a config has **both** a top-level `*` wildcard **and** other non-numeric entries (e.g., `@username` alongside `*`), those invalid entries will no longer be reported. Previously the `invalidTelegramAllowFromEntries` check ran before the `continue`, so both findings were emitted.

While the wildcard is the dominant concern, silently dropping the invalid-entry WARN could hide misconfigured allowlists that users still need to fix after removing the wildcard. Consider reporting invalid entries even in the wildcard path:

```typescript
// report invalid entries first, then bail
if (invalidTelegramAllowFromEntries.size > 0) {
  // ... push warn finding ...
}
if (storeHasWildcard || groupAllowFromHasWildcard) {
  findings.push({ checkId: "channels.telegram.groups.allowFrom.wildcard", ... });
  continue;
}
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/security/audit-channel.ts
Line: 829-840

Comment:
**`*` wildcard generates spurious invalid-entry WARN alongside CRITICAL**

When a group-level wildcard is detected and the CRITICAL finding is pushed (lines 830–840), the code falls through to the `invalidTelegramAllowFromEntries` check at line 842. Because `*` is non-numeric, `collectInvalidTelegramAllowFromEntries` will have already collected it, so a redundant WARN finding (`channels.telegram.allowFrom.invalid_entries`) will also be emitted for every group that has `allowFrom: ["*"]`.

This produces confusing output: a CRITICAL for the wildcard plus a WARN for `*` being non-numeric. Consider either:
1. Filtering `*` out of `invalidTelegramAllowFromEntries` before/during collection, or
2. Adding a `continue` after the CRITICAL push (while still reporting legitimate invalid entries via a pre-check, similar to the suggestion above).

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/security/audit-channel.ts
Line: 812-814

Comment:
**Verify `storeAllowFrom` acts as a global gate for group access**

`storeAllowFrom` (the pairing store) is used here to mark a "top-level restriction" that suppresses the group-level wildcard CRITICAL. If a non-empty, non-wildcard `storeAllowFrom` does **not** actually filter Telegram group senders at runtime (e.g., it only gates DM-paired users), then this could produce false negatives — a group with `allowFrom: ["*"]` would be silently allowed despite an unrelated store entry.

The remediation text only mentions `channels.telegram.groupAllowFrom` as the global mechanism. It would be worth confirming that `storeAllowFrom` entries are also enforced before group-level `allowFrom` overrides in the Telegram runtime, and if not, removing `storeAllowFrom.length > 0 && !storeHasWildcard` from `hasTopLevelRestriction`.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: b7581b7

Comment on lines +816 to +827
if (storeHasWildcard || groupAllowFromHasWildcard) {
findings.push({
checkId: "channels.telegram.groups.allowFrom.wildcard",
severity: "critical",
title: "Telegram group allowlist contains wildcard",
detail:
'Telegram group sender allowlist contains "*", which allows any group member to run /… commands and control directives.',
remediation:
'Remove "*" from channels.telegram.groupAllowFrom and pairing store; prefer explicit numeric Telegram user IDs.',
});
continue;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Early continue suppresses invalid-entry warnings

Moving the top-level wildcard block before invalidTelegramAllowFromEntries means that if a config has both a top-level * wildcard and other non-numeric entries (e.g., @username alongside *), those invalid entries will no longer be reported. Previously the invalidTelegramAllowFromEntries check ran before the continue, so both findings were emitted.

While the wildcard is the dominant concern, silently dropping the invalid-entry WARN could hide misconfigured allowlists that users still need to fix after removing the wildcard. Consider reporting invalid entries even in the wildcard path:

// report invalid entries first, then bail
if (invalidTelegramAllowFromEntries.size > 0) {
  // ... push warn finding ...
}
if (storeHasWildcard || groupAllowFromHasWildcard) {
  findings.push({ checkId: "channels.telegram.groups.allowFrom.wildcard", ... });
  continue;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/security/audit-channel.ts
Line: 816-827

Comment:
**Early `continue` suppresses invalid-entry warnings**

Moving the top-level wildcard block before `invalidTelegramAllowFromEntries` means that if a config has **both** a top-level `*` wildcard **and** other non-numeric entries (e.g., `@username` alongside `*`), those invalid entries will no longer be reported. Previously the `invalidTelegramAllowFromEntries` check ran before the `continue`, so both findings were emitted.

While the wildcard is the dominant concern, silently dropping the invalid-entry WARN could hide misconfigured allowlists that users still need to fix after removing the wildcard. Consider reporting invalid entries even in the wildcard path:

```typescript
// report invalid entries first, then bail
if (invalidTelegramAllowFromEntries.size > 0) {
  // ... push warn finding ...
}
if (storeHasWildcard || groupAllowFromHasWildcard) {
  findings.push({ checkId: "channels.telegram.groups.allowFrom.wildcard", ... });
  continue;
}
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +829 to +840
if (groupLevelWildcards.length > 0 && !hasTopLevelRestriction) {
findings.push({
checkId: "channels.telegram.groups.group_allowFrom.wildcard",
severity: "critical",
title: "Telegram group allowlist contains wildcard",
detail:
`Per-group sender allowlist contains "*" at:\n${groupLevelWildcards.map((p) => `- ${p}`).join("\n")}\n` +
'This allows any group member to run /… commands and control directives.',
remediation:
'Remove "*" from per-group allowFrom, or set channels.telegram.groupAllowFrom to restrict access globally.',
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 * wildcard generates spurious invalid-entry WARN alongside CRITICAL

When a group-level wildcard is detected and the CRITICAL finding is pushed (lines 830–840), the code falls through to the invalidTelegramAllowFromEntries check at line 842. Because * is non-numeric, collectInvalidTelegramAllowFromEntries will have already collected it, so a redundant WARN finding (channels.telegram.allowFrom.invalid_entries) will also be emitted for every group that has allowFrom: ["*"].

This produces confusing output: a CRITICAL for the wildcard plus a WARN for * being non-numeric. Consider either:

  1. Filtering * out of invalidTelegramAllowFromEntries before/during collection, or
  2. Adding a continue after the CRITICAL push (while still reporting legitimate invalid entries via a pre-check, similar to the suggestion above).
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/security/audit-channel.ts
Line: 829-840

Comment:
**`*` wildcard generates spurious invalid-entry WARN alongside CRITICAL**

When a group-level wildcard is detected and the CRITICAL finding is pushed (lines 830–840), the code falls through to the `invalidTelegramAllowFromEntries` check at line 842. Because `*` is non-numeric, `collectInvalidTelegramAllowFromEntries` will have already collected it, so a redundant WARN finding (`channels.telegram.allowFrom.invalid_entries`) will also be emitted for every group that has `allowFrom: ["*"]`.

This produces confusing output: a CRITICAL for the wildcard plus a WARN for `*` being non-numeric. Consider either:
1. Filtering `*` out of `invalidTelegramAllowFromEntries` before/during collection, or
2. Adding a `continue` after the CRITICAL push (while still reporting legitimate invalid entries via a pre-check, similar to the suggestion above).

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +812 to +814
const hasTopLevelRestriction =
(groupAllowFrom.length > 0 && !groupAllowFromHasWildcard) ||
(storeAllowFrom.length > 0 && !storeHasWildcard);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Verify storeAllowFrom acts as a global gate for group access

storeAllowFrom (the pairing store) is used here to mark a "top-level restriction" that suppresses the group-level wildcard CRITICAL. If a non-empty, non-wildcard storeAllowFrom does not actually filter Telegram group senders at runtime (e.g., it only gates DM-paired users), then this could produce false negatives — a group with allowFrom: ["*"] would be silently allowed despite an unrelated store entry.

The remediation text only mentions channels.telegram.groupAllowFrom as the global mechanism. It would be worth confirming that storeAllowFrom entries are also enforced before group-level allowFrom overrides in the Telegram runtime, and if not, removing storeAllowFrom.length > 0 && !storeHasWildcard from hasTopLevelRestriction.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/security/audit-channel.ts
Line: 812-814

Comment:
**Verify `storeAllowFrom` acts as a global gate for group access**

`storeAllowFrom` (the pairing store) is used here to mark a "top-level restriction" that suppresses the group-level wildcard CRITICAL. If a non-empty, non-wildcard `storeAllowFrom` does **not** actually filter Telegram group senders at runtime (e.g., it only gates DM-paired users), then this could produce false negatives — a group with `allowFrom: ["*"]` would be silently allowed despite an unrelated store entry.

The remediation text only mentions `channels.telegram.groupAllowFrom` as the global mechanism. It would be worth confirming that `storeAllowFrom` entries are also enforced before group-level `allowFrom` overrides in the Telegram runtime, and if not, removing `storeAllowFrom.length > 0 && !storeHasWildcard` from `hasTopLevelRestriction`.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8bfd777f74

ℹ️ 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".

continue;
}

if (groupLevelWildcards.length > 0 && !hasTopLevelRestriction) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not suppress per-group wildcard findings with global allowlist

The new !hasTopLevelRestriction gate hides a real command-control exposure: Telegram runtime authorization replaces groupAllowFrom when a group/topic override exists (groupAllowOverride ?? groupAllowFrom in extensions/telegram/src/bot-message-context.ts and extensions/telegram/src/bot/helpers.ts), so groups.<id>.allowFrom: ["*"] still authorizes any member even if top-level groupAllowFrom is restricted. In configs that set a global allowlist plus per-group/topic wildcard, the audit now returns a false negative instead of the critical finding.

Useful? React with 👍 / 👎.

yelog added 2 commits March 17, 2026 16:22
…From wildcard

Security audit now correctly handles per-group allowFrom wildcard when
top-level groupAllowFrom is already restricted. Previously, the audit
only checked top-level wildcards but missed group-level ones, causing
false CRITICAL alerts for intentional configurations.

Closes openclaw#48687
…udit

- Report invalid entries before wildcard check to avoid suppression
- Add continue after group-level wildcard CRITICAL to prevent redundant WARN
- Simplify hasTopLevelRestriction to only use groupAllowFrom (more conservative)
@yelog yelog force-pushed the fix/security-audit-group-allowfrom-wildcard branch from 8bfd777 to 3414c3c Compare March 17, 2026 08:23
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3414c3c7d5

ℹ️ 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".

continue;
}

if (groupLevelWildcards.length > 0 && !hasTopLevelRestriction) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Flag per-group wildcard even when top-level list is set

This condition suppresses a real critical exposure: Telegram runtime authorization does not intersect top-level groupAllowFrom with per-group/topic allowFrom; it replaces it via groupAllowOverride ?? groupAllowFrom (see extensions/telegram/src/bot-message-context.ts and extensions/telegram/src/bot/helpers.ts). In configs like groupAllowFrom: ["123456"] plus groups.<id>.allowFrom: ["*"], group messages are still wildcard-authorized, so the audit now produces a false negative instead of reporting the command-control risk.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

healthcheck: security audit falsely flags intentional group-level allowFrom wildcard as critical

1 participant