fix(workflow): resolve nested webhook body fields in templates - #7037
Open
sekura-ai wants to merge 1 commit into
Open
fix(workflow): resolve nested webhook body fields in templates#7037sekura-ai wants to merge 1 commit into
sekura-ai wants to merge 1 commit into
Conversation
Webhook trigger context was built by walking only the top level of the
POSTed JSON body, stringifying any nested object. A provider payload
whose interesting fields are nested — a GitHub `release` hook, where the
tag lives at `release.tag_name` — had nothing usable to address, and
`{{trigger.release.tag_name}}` did not resolve either because nested
paths were never registered. Unknown variables are emitted literally by
design, so workflows posted messages containing a raw `{{trigger.ref}}`.
Flatten webhook bodies into dotted keys so provider payloads are
addressable in place: nested paths (`release.tag_name`), array indices
(`commits.0.message`), and containers still kept as compact JSON under
their own key so `{{trigger.release}}` renders the subtree. Flattening is
capped at 6 levels and 512 fields to bound the trigger context persisted
with every run.
Dotted keys are not valid evalexpr identifiers, so `if:`/`filter:`
expressions see the same fields with separators collapsed to underscores
(`trigger_release_tag_name`). Sanitized names are collected through a
BTreeMap so a collision resolves identically on every run, and the
existing guard preventing webhook fields from shadowing standard
`trigger_*` variables now runs after sanitization.
Signed-off-by: Tikka Nagi <tikka@sekura.ai>
🔐 Codex Security Review
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A GitHub release notification workflow posted its message with the placeholder intact:
Two separate causes stacked up:
refis not in areleasepayload. GitHub'sreleasehook (action: "published") carries the tag atrelease.tag_name; onlypushpayloads have a top-levelref.{{trigger.release.tag_name}}would not have resolved either — there was no correct thing to write.The literal output is the resolver behaving as designed: an unknown
{{...}}is emitted verbatim rather than as an empty string (executor.rs), so a visible placeholder means the field was absent from the body.Fix
Flatten webhook bodies into dotted keys so provider payloads are addressable in place, with no pre-flattening proxy:
{{trigger.action}}published{{trigger.release.tag_name}}v1.4.0{{trigger.repository.owner.login}}block{{trigger.commits.0.message}}{{trigger.release}}Flattening is capped at 6 levels deep / 512 fields to bound the trigger context persisted with every run — a
pushwith many commits is otherwise unbounded.For
if:/filter:expressions, dotted keys are not valid evalexpr identifiers, so the same fields are exposed with separators collapsed to underscores:Sanitized names are collected through a
BTreeMapso two keys that sanitize to the same identifier resolve deterministically across runs, and the existing guard preventing webhook fields from shadowing standardtrigger_*variables now runs after sanitization — closing a gap where a key literally namedtrigger.authorwould previously have sanitized intotrigger_authorand spoofed the signed trigger author.Both relay call sites (
bridge.rswebhook route,command_executor.rsevent trigger) now delegate to the shared helper instead of each open-coding the top-level walk.Caller impact
The workflow YAML that triggered this becomes:
No existing template breaks: top-level scalars keep their bare names, and objects/arrays still render under their own key as before (as compact JSON).
Testing
cargo test -p buzz-workflow --lib— 162 passed / 0 failed. Six new tests cover nested paths, array indices, non-object bodies, the depth/count caps, template resolution, condition registration, and the anti-spoofing guard. All new logic lives in this crate.cargo clippy -p buzz-relay --lib— clean.cargo fmt --all --check— clean.Not run locally:
buzz-relaytest binaries failed to link on a full disk (that crate's test tree pullsiroh/hf-xet/mesh-llm-hf-hub). The two relay changes are three-line delegations to the tested helper, but they are unexercised here and I would want CI to confirm them. Integration suite (just test) also not run — needs Postgres + Redis.Deploy note
This is relay-side, so it needs a relay release to take effect. There is no template-only workaround for a
releasehook: before this change, only top-level scalars resolved, andactionis the sole useful one in that payload.🤖 Generated with Claude Code
https://claude.ai/code/session_019pLy6t6bJAbuqs9PfruBi8