Skip to content

fix: close an empty-secret webhook bypass and an unbounded token wait - #61

Merged
jferrl merged 2 commits into
mainfrom
harden/timeouts-webhook
Sep 17, 2026
Merged

jferrl merged 2 commits into
mainfrom
harden/timeouts-webhook

Conversation

@jferrl

@jferrl jferrl commented Sep 17, 2026 •

Copy link
Copy Markdown
Owner

Summary

An empty webhook secret is used as an HMAC key rather than refused. HMAC accepts a zero-length key, so a deployment that passes a missing config value verifies every delivery against a key anyone can reproduce, and forged payloads reach the downstream handler as authentic. Separately the HTTP client bounds dial, TLS handshake and idle connections, but nothing bounds the wait for a response header, so a server that accepts a connection and then goes quiet hangs token retrieval forever. Both now fail closed.

Changes

  • Verify returns a new ErrMissingSecret for an empty secret, so Middleware answers 401 instead of passing the delivery downstream
  • ResponseHeaderTimeout is set to 30s on the default transport, with a test asserting every transport stage stays bounded
  • FuzzVerify asserted that a freshly computed signature always verifies and seeded an empty secret, so it encoded this bug as correct behaviour; it now requires ErrMissingSecret in that case and keeps the round-trip property for real secrets
  • Corrects a Middleware comment claiming the default error handler writes no body

Rationale

ResponseHeaderTimeout rather than a whole-request client.Timeout, which would also sever legitimately slow response bodies. An empty secret fails closed rather than panicking at construction, so a misconfigured deployment surfaces as failed deliveries instead of taking the server down on deploy.

Migration Notes

Verify and Middleware now reject an empty secret. Any caller depending on the previous behaviour was accepting forged deliveries.

HMAC accepts a zero-length key without complaint, so a deployment that
called Middleware(nil) or read a missing secret from the environment
verified every delivery against a key anyone can reproduce. Forged
payloads reached the downstream handler as authentic.

Verify now returns ErrMissingSecret when the secret is empty. A
misconfigured deployment fails closed and surfaces as failed deliveries
rather than silently accepting forgeries.

FuzzVerify asserted that a freshly computed signature always verifies
against its inputs, and its seed corpus included an empty secret, so the
invariant encoded the vulnerability as correct behaviour. It now demands
ErrMissingSecret for an empty secret and keeps the round-trip property
for real ones.

Also corrects the Middleware comment claiming the default error handler
writes no body; it writes a short plain-text reason.
Dial, TLS handshake and idle connections were all capped, but nothing
limited how long the transport would wait for a response header. A server
that accepted the connection and then went quiet hung Token() forever:
the default context is context.Background(), and the token cache holds
its mutex across a refresh, so one stalled request blocked every
concurrent caller.

Sets ResponseHeaderTimeout to 30s rather than a whole-request
client.Timeout, which would also cut off legitimately slow bodies.
@codecov

codecov Bot commented Sep 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.47%. Comparing base (c69bfba) to head (29dc077).

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #61      +/-   ##
==========================================
+ Coverage   96.44%   96.47%   +0.03%     
==========================================
  Files           4        4              
  Lines         281      284       +3     
==========================================
+ Hits          271      274       +3     
  Misses         10       10              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jferrl
jferrl merged commit 343d6fd into main Sep 17, 2026
10 checks passed
@jferrl
jferrl deleted the harden/timeouts-webhook branch September 18, 2026 09:01
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.

1 participant