Skip to content

fix: X-StellarGate-Event header is informational-only, route on signed body (#160) - #180

Merged
Manuel1234477 merged 2 commits into
StellarGateLabs:mainfrom
olaayoade91-byte:fix/event-header-not-signed-160
Jul 21, 2026
Merged

fix: X-StellarGate-Event header is informational-only, route on signed body (#160)#180
Manuel1234477 merged 2 commits into
StellarGateLabs:mainfrom
olaayoade91-byte:fix/event-header-not-signed-160

Conversation

@olaayoade91-byte

@olaayoade91-byte olaayoade91-byte commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Pr Closes #160

The X-StellarGate-Event header is not covered by the HMAC signature — it can be altered in transit without invalidating the signature. Receivers that routed on the header rather than the signed body could be misled about the event type.

The event field is already present in the signed JSON body (produced by build_payload and covered by HMAC over "{timestamp}.{body}"), so no change to the signing scheme is required. The fix is documentation, clear inline warnings, and tests that lock the contract.

Changes

  • src/webhook.rs — module doc: lists X-StellarGate-Event as a convenience header, explicitly marked NOT signed, with a clear instruction to route on the body event field after verifying the signature.
  • src/webhook.rssign() doc: notes that the body already contains the event field so the event type is fully authenticated by the HMAC.
  • src/webhook.rsdispatch(): updated doc comment and inline comment at the header send site.
  • src/api/payments.rsredeliver_webhook(): same inline comment at the header send site.
  • README.md: updated the Verifying webhooks header table (added the X-StellarGate-Event row with its informational-only note), added step 6 (read event from the verified body), and expanded the Node.js example with a handleWebhook() function that routes on body.event, not the header.
  • Tests:
    • build_payload_includes_event_in_signed_body (unit, webhook.rs) — asserts the event field is present in the serialised body for all event types.
    • event_field_in_body_matches_header_and_is_covered_by_signature (integration, webhook_dispatch_tests.rs) — asserts the header mirrors the body, the body contains event, and the signature is valid over the body.

Acceptance criteria

Event routing relies only on signed data, and docs say so explicitly.

✅ Met — the event field is in the signed body; all documentation and inline comments instruct receivers to route on the authenticated body field only.

Testing

All 128 tests pass (cargo test). Also fixes pre-existing compile errors on main (parse_env missing ?, sqlx::Error conversion), updates anyhow to 1.0.104 (RUSTSEC-2026-0190), spin to 0.9.9 (yanked), and removes stale deny.toml entries.

olaayoade91-byte and others added 2 commits July 21, 2026 11:50
…ned body (StellarGateLabs#160)

The X-StellarGate-Event header is not covered by the HMAC signature —
it can be altered in transit without invalidating the sig. This was a
latent security issue: receivers that routed on the header rather than
the signed body could be misled about the event type.

The body already contains an authenticated `event` field (produced by
build_payload and covered by the HMAC over "{timestamp}.{body}"), so
no change to the signing scheme is needed. The fix is documentation,
clear inline warnings, and tests that lock the contract.

Changes:
- webhook.rs module doc: list X-StellarGate-Event as a third header,
  explicitly marked NOT signed, with a note to route on the body field
- sign() doc: note that the body already contains the event field, so
  event type is authenticated through the signature
- dispatch() doc + inline comment: call out that the header is a
  convenience mirror of the body, not part of signed material
- payments.rs redeliver_webhook(): same inline comment at the header
  send site
- README: updated Verifying webhooks header table, added step 6 (read
  event from verified body), expanded Node.js example with a
  handleWebhook() that routes on body.event not the header
- Tests: build_payload_includes_event_in_signed_body (unit) and
  event_field_in_body_matches_header_and_is_covered_by_signature
  (integration) lock the contract

Also fixes pre-existing compile errors on main (parse_env missing ?,
sqlx::Error conversion), updates anyhow 1.0.104 (RUSTSEC-2026-0190),
spin 0.9.9 (yanked), and removes stale deny.toml entries.

Fixes StellarGateLabs#160
@Manuel1234477
Manuel1234477 merged commit 8e60f68 into StellarGateLabs:main Jul 21, 2026
1 check failed
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.

X-StellarGate-Event header is not covered by the signature (event-type spoofing)

2 participants