Skip to content

fix(mattermost): bind interaction callbacks to channel and post context#38057

Merged
vincentkoc merged 2 commits intomainfrom
vincentkoc-code/mattermost-interaction-hmac-binding
Mar 6, 2026
Merged

fix(mattermost): bind interaction callbacks to channel and post context#38057
vincentkoc merged 2 commits intomainfrom
vincentkoc-code/mattermost-interaction-hmac-binding

Conversation

@vincentkoc
Copy link
Copy Markdown
Member

Summary

Describe the problem and fix in 2–5 bullets:

  • Problem: Mattermost interaction callbacks accepted any valid button context token even if the callback payload channel or post metadata was swapped.
  • Why it matters: a leaked/replayed callback context could steer a button click into the wrong channel/post and the synthetic follow-up message was marked CommandAuthorized: true.
  • What changed: button sends now sign the resolved channel id into the interaction context; callback handling rejects signed-channel mismatches, rejects post/channel mismatches, and rejects actions that are not present on the original post.
  • What did NOT change (scope boundary): this does not change Mattermost slash command auth, gateway auth policy, or button UX for valid callbacks.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • Mattermost interactive button callbacks now fail closed when the callback payload channel/post/action does not match the signed button context.
  • Synthetic button-click follow-up messages are no longer treated as pre-authorized text commands.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (Yes)
  • Command/tool execution surface changed? (Yes)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:
    The send path resolves the target channel id before signing button context, which can add one pre-send Mattermost lookup for name/user targets. In exchange, callbacks are bound to the intended channel and validated against the fetched original post before any synthetic follow-up dispatch.

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local pnpm worktree
  • Model/provider: n/a
  • Integration/channel (if any): Mattermost plugin
  • Relevant config (redacted): n/a

Steps

  1. Create a Mattermost interactive message with buttons.
  2. Replay or tamper with the callback payload so channel_id or post_id no longer matches the original button context/post.
  3. Send the callback to the interaction endpoint.

Expected

  • The callback is rejected before synthetic follow-up dispatch.

Actual

  • Before this patch, only the button context token was checked; payload channel/post metadata could diverge.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: focused Mattermost tests for interactions, send, and channel passed after the patch.
  • Edge cases checked: signed channel mismatch, fetched post channel mismatch, missing action on original post, valid non-localhost callback.
  • What you did not verify: live Mattermost end-to-end callback flow against a real server.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert commit Mattermost: harden interaction callback binding.
  • Files/config to restore: the Mattermost interaction/send files in this PR.
  • Known bad symptoms reviewers should watch for: valid button clicks unexpectedly returning Channel mismatch, Post/channel mismatch, or Unknown action.

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk: extra target-resolution lookup before sending button messages may add latency for username/channel-name targets.
    • Mitigation: limited to the button send path; existing send logic already performs the same resolution before delivery.
  • Risk: stricter callback validation could surface existing broken Mattermost deployments that were relying on mismatched callback metadata.
    • Mitigation: validation is limited to signed channel id plus original-post checks, which should hold for valid Mattermost callbacks.

@vincentkoc vincentkoc self-assigned this Mar 6, 2026
@openclaw-barnacle openclaw-barnacle bot added channel: mattermost Channel integration: mattermost size: M maintainer Maintainer-authored PR labels Mar 6, 2026
@vincentkoc vincentkoc marked this pull request as ready for review March 6, 2026 16:06
@vincentkoc vincentkoc merged commit a274ef9 into main Mar 6, 2026
27 checks passed
@vincentkoc vincentkoc deleted the vincentkoc-code/mattermost-interaction-hmac-binding branch March 6, 2026 16:08
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: 2dbcd6e4f8

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

break;
}
}
if (clickedButtonName === actionId) {
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 Track action presence separately from action label

This check treats clickedButtonName === actionId as proof that the action was not found, but clickedButtonName is also the actual display label. If a valid button action has a name that exactly matches its id (a common pattern in integrations), the loop sets clickedButtonName to that same value and this branch incorrectly returns 403 Unknown action for a legitimate callback. Use a separate foundAction boolean (or store the matched action object) so label equality does not trigger false rejections.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 6, 2026

Greptile Summary

This PR hardens Mattermost interactive button callbacks by: (1) signing the resolved channel ID into each button's HMAC context at send time, (2) rejecting callbacks where the signed channel or fetched-post channel diverges from the payload's channel_id, (3) rejecting actions absent from the original post's attachment list, and (4) removing the implicit CommandAuthorized: true flag from synthetic button-click messages. The changes are well-scoped and the new tests cover the primary rejection paths.

Key issue found:

  • Logic bug in action-existence check (interactions.ts lines 415–421): the "not found" sentinel clickedButtonName === actionId incorrectly fires a 403 when a button's display name happens to equal its id. The loop correctly finds and updates the name, but the sentinel check still evaluates to true, rejecting legitimate button clicks. A null-initialized variable or boolean flag is needed instead.

Confidence Score: 2/5

  • The PR has a logic bug that will cause valid button clicks to be incorrectly rejected when action name equals action id.
  • The security hardening logic for signed channels, post/channel validation, and implicit authorization removal is sound and well-tested. However, a sentinel pattern bug in the action-existence check (interactions.ts lines 415–421) will reject legitimate button clicks when a button's display name equals its id. This is not a security bypass but a correctness issue that breaks valid user interactions. The core security improvements are solid, but this bug needs fixing before merge.
  • extensions/mattermost/src/mattermost/interactions.ts (action-existence check sentinel pattern)

Last reviewed commit: 2dbcd6e

mrosmarin added a commit to mrosmarin/openclaw that referenced this pull request Mar 6, 2026
* main:
  Mattermost: harden interaction callback binding (openclaw#38057)
  WhatsApp: honor outbound mediaMaxMb (openclaw#38097)
  openai-image-gen: validate --background and --style options (openclaw#36762)
  Docs: align BlueBubbles media cap wording
  Telegram/Discord: honor outbound mediaMaxMb uploads (openclaw#38065)
  CI: run changed-scope on main pushes
  Skills/nano-banana-pro: clarify MEDIA token comment (openclaw#38063)
  nano-banana-pro: respect explicit --resolution when editing images (openclaw#36880)
  CI: drop unused install-smoke bootstrap
  fix(nano-banana-pro): remove space after MEDIA: token in generate_image.py (openclaw#18706)
  docs: context engine
  docs(config): list the context engine plugin slot
  docs(plugins): add context-engine manifest kind example
  docs(plugins): document context engine slots and registration
  docs(protocol): document slash-delimited schema lookup plugin ids
  docs(tools): document slash-delimited config schema lookup paths
  fix(session): tighten direct-session webchat routing matching (openclaw#37867)
  feature(context): extend plugin system to support custom context management (openclaw#22201)
  Gateway: allow slash-delimited schema lookup paths
ant1eicher pushed a commit to ant1eicher/openclaw that referenced this pull request Mar 6, 2026
Saitop pushed a commit to NomiciAI/openclaw that referenced this pull request Mar 8, 2026
jenawant pushed a commit to jenawant/openclaw that referenced this pull request Mar 10, 2026
dhoman pushed a commit to dhoman/chrono-claw that referenced this pull request Mar 11, 2026
senw-developers pushed a commit to senw-developers/va-openclaw that referenced this pull request Mar 17, 2026
V-Gutierrez pushed a commit to V-Gutierrez/openclaw-vendor that referenced this pull request Mar 17, 2026
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 20, 2026
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 20, 2026
alexey-pelykh added a commit to remoteclaw/remoteclaw that referenced this pull request Mar 20, 2026
* fix(subagents): recover announce cleanup after kill/complete race

(cherry picked from commit 2f86ae7)

* feat(hooks): emit compaction lifecycle hooks (openclaw#16788)

(cherry picked from commit 71ec421)

* fix(agent): harden undici stream timeouts for long openai-completions runs

(cherry picked from commit 05fb16d)

* fix(agents): allow configured ollama endpoints without dummy api keys

(cherry picked from commit 36afd1b)

* feat(openai): add gpt-5.4 support for API and Codex OAuth (openclaw#36590)

Co-authored-by: Tyler Yust <[email protected]>
(cherry picked from commit 5d4b040)

* Fix failover for zhipuai 1310 Weekly/Monthly Limit Exhausted (openclaw#33813)

Co-authored-by: zhouhe-xydt <[email protected]>
Co-authored-by: altaywtf <[email protected]>
(cherry picked from commit a65d70f)

* fix(failover): classify HTTP 402 as rate_limit when payload indicates usage limit (openclaw#30484) (openclaw#36802)

Co-authored-by: Val Alexander <[email protected]>
(cherry picked from commit 01b2017)

* feat(onboarding): add web search to onboarding flow (openclaw#34009)

* add web search to onboarding flow

* remove post onboarding step (now redundant)

* post-onboarding nudge if no web search set up

* address comments

* fix test mocking

* add enabled: false assertion to the no-key test

* --skip-search cli flag

* use provider that a user has a key for

* add assertions, replace the duplicated switch blocks

* test for quickstart fast-path with existing config key

* address comments

* cover quickstart falls through to key test

* bring back key source

* normalize secret inputs instead of direct string trimming

* preserve enabled: false if it's already set

* handle missing API keys in flow

* doc updates

* hasExistingKey to detect both plaintext strings and SecretRef objects

* preserve enabled state only on the "keep current" paths

* add test for preserving

* better gate flows

* guard against invalid provider values in config

* Update src/commands/configure.wizard.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* format fix

* only mentions env var when it's actually available

* search apiKey fields now typed as SecretInput

* if no provider check if any search provider key is detectable

* handle both kimi keys

* remove .filter(Boolean)

* do not disable web_search after user enables it

* update resolveSearchProvider

* fix(onboarding): skip search key prompt in ref mode

* fix: add onboarding web search step (openclaw#34009) (thanks @kesku)

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Shadow <[email protected]>
(cherry picked from commit 3d7bc59)

* fix(ci): restore protocol and schema checks (openclaw#37470)

(cherry picked from commit ee6f7b1)

* Infra: require explicit opt-in for prerelease npm installs (openclaw#38117)

* Infra: tighten npm registry spec parsing

* Infra: block implicit prerelease npm installs

* Plugins: cover prerelease install policy

* Infra: add npm registry spec tests

* Hooks: cover prerelease install policy

* Docs: clarify plugin guide version policy

* Docs: clarify plugin install version policy

* Docs: clarify hooks install version policy

* Docs: clarify hook pack version policy

(cherry picked from commit f392b81)

* ci: trigger workflow

* fix: resolve cherry-pick adaptation failures from upstream web search commit

- Add missing hasConfiguredSecretInput() to types.secrets.ts
- Add SecretInput import to types.tools.ts for apiKey fields
- Replace OpenClawConfig with RemoteClawConfig in onboard-search
- Remove non-existent DEFAULT_SECRET_PROVIDER_ALIAS import
- Remove invalid 'provider' field from SecretRef literal
- Stub resolveSyntheticLocalProviderAuth (models.providers was gutted)
- Replace undefined registerRun/emitLifecycleEnd test helpers with
  existing mod.registerSubagentRun/lifecycleHandler patterns

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* Plugins: clarify registerHttpHandler migration errors (openclaw#36794)

* Changelog: note plugin HTTP route migration diagnostics

* Tests: cover registerHttpHandler migration diagnostics

* Plugins: clarify registerHttpHandler migration errors

* Tests: cover registerHttpHandler diagnostic edge cases

* Plugins: tighten registerHttpHandler migration hint

(cherry picked from commit d4021f4)

* Plugins: avoid false integrity drift prompts on unpinned updates (openclaw#37179)

* Plugins: skip drift prompts for unpinned updates

* Plugins: cover unpinned integrity update behavior

(cherry picked from commit 428d176)

* docs(protocol): document slash-delimited schema lookup plugin ids

(cherry picked from commit f788ba1)

* docs(plugins): document context engine slots and registration

(cherry picked from commit eb2eeba)

* docs(plugins): add context-engine manifest kind example

(cherry picked from commit 7cc3376)

* docs(config): list the context engine plugin slot

(cherry picked from commit 5470337)

* fix: Windows: openclaw plugins install fails with spawn EINVAL

Fixes openclaw#7631

(cherry picked from commit d000316)

* fix(whatsapp): remove implicit [openclaw] self-chat prefix

(cherry picked from commit 4d9134f)

* WhatsApp: honor outbound mediaMaxMb (openclaw#38097)

* WhatsApp: add media cap helper

* WhatsApp: cap outbound media loads

* WhatsApp: align auto-reply media caps

* WhatsApp: add outbound media cap test

* WhatsApp: update auto-reply cap tests

* Docs: update WhatsApp media caps

* Changelog: note WhatsApp media cap fix

(cherry picked from commit 222d635)

* fix(mattermost): allow reachable interaction callback URLs (openclaw#37543)

Merged via squash.

Prepared head SHA: 4d59373
Co-authored-by: mukhtharcm <[email protected]>
Co-authored-by: mukhtharcm <[email protected]>
Reviewed-by: @mukhtharcm

(cherry picked from commit 4a80d48)

* Mattermost: harden interaction callback binding (openclaw#38057)

(cherry picked from commit a274ef9)

* chore: code/dead tests cleanup (openclaw#38286)

* Discord: assert bot-self filter queue guard

* Tests: remove dead gateway SIGTERM placeholder

(cherry picked from commit 38f46e8)

* Delete changelog/fragments directory

(cherry picked from commit 8f69e07)

* fix(feishu): restore explicit media request timeouts

(cherry picked from commit b127333)

* fix: adapt upstream naming to fork conventions

Replace OpenClawConfig with RemoteClawConfig, openclaw/plugin-sdk with
remoteclaw/plugin-sdk, and fix import paths for fork type exports.

* fix: resolve type errors from upstream cherry-picks

- Add missing RemoteClawConfig import in discord test
- Use spread with as-any for feishu SDK timeout property
- Remove allowUnresolvedSecretRef from mattermost test

* ci: re-trigger CI after force push

* fix: revert unintended onboarding changes from linter bleed

* ci: sync PR head with branch

---------

Co-authored-by: Vignesh Natarajan <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: dorukardahan <[email protected]>
Co-authored-by: Tyler Yust <[email protected]>
Co-authored-by: zhouhe-xydt <[email protected]>
Co-authored-by: zhouhe-xydt <[email protected]>
Co-authored-by: altaywtf <[email protected]>
Co-authored-by: Xinhua Gu <[email protected]>
Co-authored-by: Val Alexander <[email protected]>
Co-authored-by: Kesku <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Shadow <[email protected]>
Co-authored-by: Altay <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: 0xlin2023 <[email protected]>
Co-authored-by: Muhammed Mukhthar CM <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: mattermost Channel integration: mattermost maintainer Maintainer-authored PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant