# Bitdefender Webhooks

Bitdefender GravityZone (endpoint security) pushes security events to your server through the
**Event Push Service**. You turn it on with the `setPushEventSettings` API using
`serviceType: "jsonRPC"`. Payloads are not signed. The credential is the `Authorization`
header you configure in `serviceSettings.authorization`.

## When to Use This Skill

* How do I receive Bitdefender GravityZone webhooks (Event Push Service)?
* How do I verify the `Authorization` header on Bitdefender push events?
* Does Bitdefender sign webhook payloads? (No: shared `Authorization` credential only)
* Why does my Bitdefender webhook endpoint return 401?
* How do I handle `addEvents` JSON-RPC messages and the `module` values (`av`, `aph`, `fw`)?

## Verification (core)

`serviceSettings.authorization` is sent verbatim as the `Authorization` header (the docs'
example is `Bearer sfasdfw34243`). Paste the authorization value exactly as set in
GravityZone, including any "Bearer " or "Basic " prefix, into `BITDEFENDER_AUTHORIZATION`, and
compare the **whole header** to it. Do not strip, parse or base64-decode the prefix:

```javascript
const crypto = require('crypto');

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyBitdefenderRequest(headers, expected = process.env.BITDEFENDER_AUTHORIZATION) {
  if (!expected) return false;                 // never accept when unconfigured
  const received = headers['authorization'];   // full value, e.g. "Bearer sfasdfw34243"
  return Boolean(received) && safeEqual(received, expected);
}

```

Return `401` when the header is missing or does not match, `400` for a body that is not an
`addEvents` call, and `200` once handled. Read the body raw (`express.raw({ type: '*/*' })`) so
any content type works, and parse it only after the check. In Python use
`hmac.compare_digest` on bytes (encode both sides; a non-ASCII `str` raises).

**What this does and does not give you:** a static shared credential authenticates the
sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS
and rotate the credential with `setPushEventSettings` if it leaks. See
[references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/bitdefender-webhooks/references/verification.md).

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/bitdefender-webhooks/examples/express/),
> [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/bitdefender-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/bitdefender-webhooks/examples/fastapi/).

## Message Shape and Event Modules

Every push is a JSON-RPC 2.0 call to `addEvents` with a batch of events:

```json
{"jsonrpc": "2.0", "method": "addEvents", "params": {"events": [{"module": "av", "...": "..."}]}, "id": 1694701009244}

```

The event type is each event's `module` value (exact strings from the docs):

| `module` | Event |
| --- | --- |
| `av` | Antimalware detection |
| `aph` | Antiphishing |
| `avc` | Advanced Threat Control (ATC) |
| `fw` | Firewall |
| `hd` | Hyper Detect |
| `dp` | Data Protection |
| `network-sandboxing` | Sandbox Analyzer detection |

Others include `modules`, `registration`, `task-status`, `uc`, `install`, `uninstall`,
`antiexploit`, `network-monitor`, `ransomware-mitigation` and `new-incident`. See
[references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/bitdefender-webhooks/references/overview.md). Respond with any `2xx`; anything else counts
as an error.

## Environment Variables

```bash
BITDEFENDER_AUTHORIZATION=Bearer your-secret   # full header value, prefix included

```

## Local Development

```bash
npx hookdeck-cli listen 3000 bitdefender --path /webhooks/bitdefender

```

No account required. Use the printed URL as `serviceSettings.url`. GravityZone requires an
HTTPS endpoint with TLS 1.2 or higher.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/bitdefender-webhooks/references/overview.md) - Event Push Service, message shape, event modules
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/bitdefender-webhooks/references/setup.md) - `setPushEventSettings` request and the authorization value
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/bitdefender-webhooks/references/verification.md) - Header check in Node, Python and Next.js, limits, gotchas
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/bitdefender-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests