• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sgerrand / ex_humaans / 25317065796
99%

Build:
DEFAULT BRANCH: main
Ran 04 May 2026 11:44AM UTC
Jobs 1
Files 18
Run time 1min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

04 May 2026 11:44AM UTC coverage: 100.0%. Remained the same
25317065796

push

github

web-flow
feat(webhooks): add HMAC-SHA256 signature verification helper (#92)

* feat(webhooks): add HMAC-SHA256 signature verification helper

Webhook consumers all need the same primitive: verify that an incoming
delivery was signed with the endpoint's secret before trusting any of
its content. Bundling this avoids every user having to re-derive
constant-time comparison and signature-stripping logic.

Humaans.Webhooks.verify_signature/3 takes the raw payload, the
signature header value (with or without a sha256= prefix), and the
endpoint secret. It returns :ok | {:error, :invalid_signature |
:missing_signature | :missing_secret}, comparing in constant time via
:crypto.hash_equals/2.

Also adds Humaans.webhooks/0 for symmetry with the other top-level
accessors.

* fix(webhooks): replace :crypto.hash_equals/2 with portable constant-time compare

:crypto.hash_equals/2 is unavailable in OTP 24 and before, breaking mix compile
--warnings-as-errors. Swap in a hand-rolled XOR-accumulator comparison over
equal-length binaries using Bitwise.bor/2 and bxor/2.

* fix(webhooks): match sha256= prefix case-insensitively

Headers like `SHA256=<digest>` left the prefix intact through
`strip_prefix/1`, so the byte-length check in `secure_compare/2`
rejected otherwise valid signatures. Lowercase the signature before
the prefix strip so the pattern match sees a normalised form, and
add regression tests for upper- and mixed-case prefixes.

* fix(webhooks): allow nil secret in verify_signature/3 typespec

The function clauses already accept `nil` and return
`{:error, :missing_secret}`, but the spec advertised `secret` as
`String.t()` only. Widen it to `String.t() | nil` so generated docs
and Dialyzer agree with the supported call shapes.

* docs(webhooks): cover missing_signature and missing_secret in README example

The README's verify_signature/3 example only matched :ok and
{:error, :invalid_signature}, so copy-paste users would hit
CaseClauseError when the header was absent o... (continued)

14 of 14 new or added lines in 2 files covered. (100.0%)

173 of 173 relevant lines covered (100.0%)

21.36 hits per line

Jobs
ID Job ID Ran Files Coverage
1 25317065796.1 04 May 2026 11:44AM UTC 18
100.0
GitHub Action Run
Source Files on build 25317065796
  • Tree
  • List 18
  • Changed 1
  • Source Changed 1
  • Coverage Changed 1
Coverage ∆ File Lines Relevant Covered Missed Hits/Line
  • Back to Repo
  • Github Actions Build #25317065796
  • 9cd0c7ec on github
  • Prev Build on main (#25316048326)
  • Next Build on main (#25323253278)
  • Delete
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc