Skip to content

fix: return 401 instead of 500 when the auth header is missing - #179

Merged
MarshallOfSound merged 1 commit into
mainfrom
fix/timing-safe-equal-missing-header
Oct 22, 2026
Merged

MarshallOfSound merged 1 commit into
mainfrom
fix/timing-safe-equal-missing-header

Conversation

@claude

@claude claude Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Requested by Samuel Attard · Slack thread

timingSafeEqual called Buffer.from() on req.headers.authorization, which throws a TypeError when the header is missing, so unauthenticated requests to /verify-semver and /unreleased returned 500 instead of 401. A non-string value is now treated as a mismatch (still doing the dummy constant-time compare against the secret), and a new test/helpers.spec.js covers the missing-header case.


Generated by Claude Code

timingSafeEqual called Buffer.from() on req.headers.authorization, which
throws a TypeError when the header is absent, turning an unauthenticated
request to /verify-semver or /unreleased into a 500. Treat a non-string
value as a mismatch (still doing the dummy constant-time compare) so the
routes respond 401.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELojQikqpZ2KVGqjEeNwUe
@claude
claude Bot requested a review from a team as a code owner October 22, 2026 21:03

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this PR and didn't find any bugs. It changes the shared credential check that guards server endpoints, and CODEOWNERS covers it, so a human from the owning team should still take a look.

What was reviewed:

  • The new non-string guard in timingSafeEqual (utils/helpers.js). A missing or null a now returns false. It runs the same dummy crypto.timingSafeEqual(bufferB, bufferB) as the length-mismatch path, so it still fails closed with matching timing.
  • The guard at line 218 for an unset or empty secret still runs first, so a blank secret can never match.
  • The callers in server.js (lines 46 and 292) and in verifySlackRequest (line 274) only test the boolean result, so returning false instead of throwing leads to the existing 401 path.
Extended reasoning...

The change touches timingSafeEqual in utils/helpers.js, the constant-time comparison behind the auth checks in server.js and the Slack signature check. It also adds a node:test spec for that function. The logic is small and fails closed, and the new tests cover missing-header and unset-secret inputs. However, this is security-sensitive auth and crypto code, and the CODEOWNERS file assigns every path to a review team. That is why this defers to a human instead of approving.

@MarshallOfSound
MarshallOfSound enabled auto-merge (squash) October 22, 2026 21:14
@MarshallOfSound
MarshallOfSound merged commit 20f453b into main Oct 22, 2026
8 checks passed
@MarshallOfSound
MarshallOfSound deleted the fix/timing-safe-equal-missing-header branch October 22, 2026 21:33
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.

3 participants