Skip to content

Harden webhook authentication and outbound fetch handling#13

Merged
kshahbw merged 11 commits into
adapter/contributing-guidefrom
adapter/webhook-callback-auth
Jul 15, 2026
Merged

Harden webhook authentication and outbound fetch handling#13
kshahbw merged 11 commits into
adapter/contributing-guidefrom
adapter/webhook-callback-auth

Conversation

@kshahbw

@kshahbw kshahbw commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Hardens the adapter's authentication and outbound-fetch handling. The adapter now authenticates the webhooks Bandwidth sends it, constrains where it will make server-side requests, and compares credentials in constant time.

What changed

  • Constant-time credential comparison. All REST-facade auth checks route through a single safeEqual helper built on crypto.timingSafeEqual (replaces the previous !== string comparisons).
  • Inbound webhook authentication. All /bw/* routes now require HTTP Basic auth (WEBHOOK_USER / WEBHOOK_PASSWORD), matching Bandwidth's native CallbackCreds mechanism. This is a distinct secret from the Twilio-compat REST credential. The translator stamps the same credentials onto the BXML callback verbs it emits, so Bandwidth presents them on continuation callbacks.
  • Outbound fetch guard. Customer-directed fetches are validated before the request: only http/https, and loopback/link-local/private IP ranges (IPv4, IPv6, IPv4-mapped) are refused. Redirects are disabled and a request timeout is applied.
  • Loopback listen default. The listen host now defaults to 127.0.0.1; set HOST=0.0.0.0 to expose it (containers/production).
  • Optional host allowlist. EGRESS_ALLOW_HOSTS switches outbound fetches to a strict default-deny allowlist for deployments with fixed, known customer hosts.
  • SECURITY.md documenting the auth model, the egress guard (and its defense-in-depth boundary), and setup.

⚠️ Config changes for operators

  • New required env vars: WEBHOOK_USER, WEBHOOK_PASSWORD (also set the matching CallbackCreds on your Bandwidth Voice application).
  • Breaking default: listen host is now 127.0.0.1. Containerized/exposed deployments must set HOST=0.0.0.0.
  • Optional: EGRESS_ALLOW_PRIVATE=1 (local dev against localhost/LAN), EGRESS_ALLOW_HOSTS (strict allowlist).

Testing

Full suite green: 303/303 (npm test), typecheck clean. Adds coverage for the egress guard (range classification, IPv6-literal + IPv4-mapped handling, redirect/timeout), inbound auth (401 on missing/wrong creds, REST facade not unlocked by webhook creds), and callback-credential stamping.

🤖 Generated with Claude Code

kshahbw added 11 commits July 15, 2026 10:50
Strip brackets from IPv6-literal hostnames before the isIP() fast path so
literals like https://[::1]/ are classified against the blocklist instead
of silently falling through, and wrap DNS resolution failures (including
the default resolver's ENOTFOUND throw) in EgressBlockedError so callers
that pattern-match on the typed error get a consistent fail-closed result.
Add an onRequest hook that enforces HTTP Basic auth (via safeEqual) on
every /bw/* route, gated by new required AdapterConfig fields
webhookUser/webhookPassword. The REST facade keeps its own separate
authOk check and is unaffected. Updates all existing tests that hit
/bw/* or build an AdapterConfig to supply the new fields and, where
applicable, the Authorization header, plus the local bench script.
Update stale test count in README and make SECURITY.md / egress-guard.ts
honest about the range denylist not defending against DNS rebinding.
@kshahbw
kshahbw requested review from a team as code owners July 15, 2026 15:56
@bwappsec

bwappsec commented Jul 15, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@kshahbw
kshahbw merged commit d8153cb into adapter/contributing-guide Jul 15, 2026
5 checks passed
@kshahbw
kshahbw deleted the adapter/webhook-callback-auth branch July 15, 2026 17:28
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.

2 participants