#7501: feat(hooks): expose rawBody in webhook transform context
gateway
size: XS
## Summary
This PR adds the raw request body string to the webhook transform context, enabling proper HMAC signature verification in custom transforms.
## Problem
Webhook providers (Zoho, GitHub, Stripe, etc.) sign their payloads with HMAC. To verify these signatures, the transform function needs access to the **exact raw body string** that was signed, not the parsed JSON object.
Currently, `applyHookMappings` receives only `{ payload, headers, url, path }` - the parsed payload loses the original string representation needed for HMAC verification.
## Solution
1. Modified `readJsonBody()` in `hooks.ts` to return `rawBody` alongside the parsed value
2. Added `rawBody?: string` to `HookMappingContext` type in `hooks-mapping.ts`
3. Pass `rawBody` through to `applyHookMappings()` in `server-http.ts`
4. Support `{{rawBody}}` template expression in transforms
## Usage
In a transform script:
```javascript
export default async function transform(ctx) {
const signature = ctx.headers['x-webhook-signature'];
const computed = crypto.createHmac('sha256', secret).update(ctx.rawBody).digest('hex');
if (signature !== computed) {
return { action: null }; // reject invalid signature
}
// ... process valid webhook
}
```
Or in template mappings:
```json
{
"message": "Raw body: {{rawBody}}"
}
```
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR threads the request’s raw JSON body through the hooks pipeline by:
- Extending `readJsonBody` to return `{ value, rawBody }`.
- Adding `rawBody?: string` to `HookMappingContext` and exposing it in template rendering via `{{rawBody}}`.
- Passing the `rawBody` from `server-http` into `applyHookMappings` so transform scripts can do HMAC verification.
Overall, it fits cleanly into the existing hooks mapping/transform architecture by enriching the mapping context without changing mapping resolution or dispatch behavior.
<h3>Confidence Score: 3/5</h3>
- Mostly safe to merge, but the new `rawBody` value is not actually raw due to trimming, which can break intended HMAC verification use-cases.
- The change is small and localized to hooks request parsing and mapping context. However, the `.trim()` on the stored `rawBody` is a functional mismatch with the PR’s stated goal (byte-for-byte payload needed for signature verification) and can cause false signature failures depending on provider/payload formatting.
- src/gateway/hooks.ts
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#9403: feat(hooks): Support application/x-www-form-urlencoded content-type...
by chipgpt · 2026-02-05
79.1%
#7545: feat(hooks): add message:received hook for pre-turn automation
by wangtian24 · 2026-02-02
70.0%
#19785: fix(gateway): support query parameter tokens for webhooks
by cfdude · 2026-02-18
68.9%
#23447: Gateway: harden hook ingress content-type validation
by bmendonca3 · 2026-02-22
68.1%
#6405: feat(security): Add HTTP API security hooks for plugin scanning
by masterfung · 2026-02-01
68.0%
#11597: feat(hooks): implement message:received hook
by gnufoo · 2026-02-08
68.0%
#9914: fix(hooks): resolve bundled hook dist paths and packaging checks
by zimmra · 2026-02-05
67.7%
#8121: fix(gateway): remove query parameter token support for hooks
by yubrew · 2026-02-03
67.6%
#23765: Gateway hooks: enforce JSON content type and strict payload keys
by bmendonca3 · 2026-02-22
66.9%
#7580: feat: add message:received internal hook with prompt injection
by rodrigoschott · 2026-02-03
66.9%