Skip to content

fix(workflow): let a webhook body fill a built-in trigger field it names - #5922

Open
Chessing234 wants to merge 3 commits into
block:mainfrom
Chessing234:fix/webhook-trigger-field-shadowing
Open

fix(workflow): let a webhook body fill a built-in trigger field it names#5922
Chessing234 wants to merge 3 commits into
block:mainfrom
Chessing234:fix/webhook-trigger-field-shadowing

Conversation

@Chessing234

Copy link
Copy Markdown
Contributor

Fixes #5769.

TriggerContext::get_field matches the built-in names before it consults webhook_fields, so a webhook body key called text — or author, timestamp, emoji, message_id — resolves to the built-in's empty default. A webhook trigger populates none of those, so {{trigger.text}} renders empty, the run still succeeds, and the posted value simply vanishes from the message. Renaming the key to anything non-reserved works, which is what makes it look like a template bug rather than a shadowing one.

The fall-back to webhook_fields happens only when the built-in is empty, so a body still cannot spoof a field the trigger actually set — a message_posted trigger's author stays authoritative even if the payload names one. There is a test for exactly that.

evaluate_condition had the same split: it registered the webhook fields as trigger_<key> and then overwrote them with the built-ins, including the empty ones, so a condition on trigger_text compared against "" while a template on the same field would now read the body. Both go through get_field now, so the two agree.

Related: #3503 fixes the manual-trigger half of this in command_executor.rs by mapping known --inputs keys onto the built-in fields. This is the same root cause a layer down — with get_field fixed, the manual path resolves too, without the caller-side mapping.

Verified locally: cargo test -p buzz-workflow --lib (160 passed, 5 of them new), cargo clippy -p buzz-workflow --all-targets clean, cargo fmt --all --check clean. I did not run a webhook end to end against a relay — the two new relay-facing claims (the body lands in webhook_fields, channel_id is the only built-in a webhook populates) are read from bridge.rs:1879-1894.

`TriggerContext::get_field` matched the built-in names before consulting
`webhook_fields`, so a webhook body key called `text` (or `author`,
`timestamp`, `emoji`, `message_id`) resolved to the built-in's empty
default and the posted value was unreachable from a template. A webhook
trigger populates none of those fields, so `{{trigger.text}}` rendered
empty with no error and the line silently vanished from the message.

Fall back to `webhook_fields` only when the built-in is empty, so a body
still cannot spoof a field the trigger actually set.

Signed-off-by: Taksh <takshkothari09@gmail.com>
`evaluate_condition` registered the webhook fields as `trigger_<key>` and
then overwrote them with the built-ins, including the empty ones. So a
condition on `trigger_text` for a webhook trigger compared against "" even
though the body carried a `text` key, while a template on the same field
now reads it.

Resolve both through `get_field` so the two agree; a populated built-in
still wins, which is what the ordering was there to guarantee.

Signed-off-by: Taksh <takshkothari09@gmail.com>
Templates and conditions both: a body key named `text` resolves for a
webhook trigger, a body key cannot shadow a built-in the trigger set, and
an absent field still resolves empty rather than being left literal.

Signed-off-by: Taksh <takshkothari09@gmail.com>
@Chessing234
Chessing234 requested a review from a team as a code owner August 15, 2026 03:19

@themiguelamador themiguelamador left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No findings. I traced every TriggerContext producer and verified the precedence change across event, manual, scheduled, and webhook runs. The linked issue's text reproduction is fixed for both templates and condition evaluation, while populated server-derived fields such as author and channel_id still take precedence over same-named payload fields.

Verified with all 160 runnable buzz-workflow library tests (2 Postgres-only tests ignored by the crate), strict Clippy, rustfmt, and git diff --check.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Webhook body keys named after built-in trigger fields are silently swallowed — {{trigger.text}} always renders empty for webhook triggers

2 participants