feat: support Bitbucket webhook secrets via X-Hub-Signature validation (#360)#386
Open
cdelmonte-zg wants to merge 1 commit into
Open
feat: support Bitbucket webhook secrets via X-Hub-Signature validation (#360)#386cdelmonte-zg wants to merge 1 commit into
cdelmonte-zg wants to merge 1 commit into
Conversation
When a webhook secret is configured on Bitbucket (Cloud or Server / Data Center), every delivery is signed with an X-Hub-Signature header carrying the hex-encoded HMAC-SHA256 of the request body. The plugin can now validate that signature: a new 'Webhook secret' option in the global configuration selects a Secret text credential holding the secret, and when set, requests with a missing or invalid signature are rejected with HTTP 403 before any payload processing. An unresolvable secret credential fails closed. The signature is computed by Bitbucket before any transport encoding, so it is validated on the decompressed, not-yet-URL-decoded body; the comparison is constant-time. Without a configured secret the behaviour is unchanged and unsigned requests are accepted as before. The receiver no longer caches the global configuration in a static field: the static pinned the GlobalConfiguration instance of whichever Jenkins was alive when the class loaded, going stale across Jenkins instances (e.g. JenkinsRule tests in the same JVM). Fixes #360
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements webhook secret support requested in #360.
What
When a secret is configured on a Bitbucket webhook, Bitbucket Cloud and Bitbucket Server / Data Center sign every delivery with an
X-Hub-Signatureheader of the formsha256=<hex>, where<hex>is the HMAC-SHA256 of the request body computed with the secret as key. Until now the plugin ignored that header: the endpoint is anUnprotectedRootAction, so anyone able to reach Jenkins could POST a forged payload and trigger jobs.This PR adds an optional Webhook secret to the plugin's global configuration, selecting a Secret text credential. When set:
X-Hub-Signatureheader;MessageDigest.isEqual).When no secret is configured, behaviour is unchanged: unsigned requests are accepted as before, and a stray signature header is ignored.
Design notes
getInputStreamwas split accordingly intoreadBody(raw bytes, post-gunzip — the HMAC input) andbodyToString(blank-check + decode, unchanged semantics).static finalpinned theGlobalConfigurationinstance of whichever Jenkins was alive when the class loaded, going stale across Jenkins instances (e.g.JenkinsRuletests in the same JVM). It is now resolved per call, like everywhere else in Jenkins.Tests
SignatureUtilsTest(unit): known-answer vector verified independently withopenssl dgst -sha256 -hmac, case-insensitive hex/prefix, empty body, tampered body, wrong secret, missing/blank header,sha1=prefix, malformed hex.BitBucketPPRHookReceiverSignatureTest(@WithJenkins, e2e over HTTP): signed request accepted (200), unsigned/wrong-signature rejected (403), gzip-compressed delivery validated against the uncompressed bytes (200), unresolvable credential fails closed (403), no configured secret ignores the header (200), signed but malformed payload still rejected by Webhook receiver swallows undeserializable payloads: null payload triggers NPE and silent HTTP 200 instead of a clear 4xx #384 validation (400).Full suite green locally (311 run, 0 failures, 12 pre-existing skips) on JDK 17.
Fixes #360.