[{"data":1,"prerenderedAt":1237},["ShallowReactive",2],{"content-query-MqSqDi3wDQ":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"layout":10,"head":11,"author":12,"tags":13,"categories":19,"date":20,"faq":21,"excerpt":43,"body":74,"_type":1229,"_id":1230,"_source":1231,"_file":1232,"_stem":1233,"_extension":1234,"sitemap":1235,"readingTime":1236},"/blog/jenkins-vulnerabilities","blog",false,"","Jenkins Vulnerabilities Exploited in the Wild: CVEs and Endpoints","Which Jenkins vulnerabilities were exploited, from CVE-2024-23897 to the 2026 advisory, which endpoint each hit, and how to stop exposing Jenkins publicly.","post",{"title":8},"Karolis Rusenas",[14,15,16,17,18],"jenkins","security","cve","ci-cd","webhooks",[15],"2026-10-28 10:00:00",[22,25,28,31,34,37,40],{"q":23,"a":24},"What are the most serious Jenkins vulnerabilities exploited in the wild?","CVE-2024-23897 (arbitrary file read through the CLI, used in ransomware attacks), CVE-2017-1000353 (unauthenticated remoting CLI deserialization RCE), CVE-2018-1000861 (Stapler URL routing, a building block for pre-auth RCE) and the 2019 Script Security and Pipeline: Groovy sandbox bypasses (CVE-2019-1003029 and CVE-2019-1003030). All five are in the CISA Known Exploited Vulnerabilities catalog.",{"q":26,"a":27},"What is CVE-2024-23897?","A Jenkins CLI flaw disclosed on 2024-01-24. The args4j library expanded an @ followed by a file path into that file's contents, so anyone who could reach the CLI could read files from the controller. Attackers chained it into code execution. It is fixed in Jenkins 2.442, LTS 2.426.3 and LTS 2.440.1.",{"q":29,"a":30},"Do Jenkins webhook endpoints expose these vulnerabilities?","No. The exploited bugs were in the CLI, the Stapler URL router, the script-validation endpoint and job configuration, not in /github-webhook/, /bitbucket-hook/ or /project/NAME. But if you publish those endpoints by exposing the whole controller, the CLI and UI are reachable too, which is how the attacks arrived.",{"q":32,"a":33},"Can I receive GitHub webhooks in Jenkins without exposing Jenkins to the internet?","Yes. The Webhook Relay Jenkins plugin opens an outbound WebSocket to a bucket. GitHub posts to the bucket's public URL and the plugin replays each delivery to /github-webhook/ inside Jenkins, so no inbound port or public IP is needed and /cli, the UI and the setup wizard are not reachable from outside.",{"q":35,"a":36},"Does a webhook relay protect me if my Jenkins is unpatched?","It removes the internet path to the vulnerable CLI, UI and API endpoints, which is how the exploited CVEs were reached. It does not fix them. Anyone already on your network, any logged-in user and any bug in the webhook endpoint itself are still in scope, so keep patching.",{"q":38,"a":39},"When does a relay not help with Jenkins security?","When people or systems must reach Jenkins from outside: UI users (use a VPN or identity-aware proxy instead), agents that connect to the controller from other networks, and a flaw in the webhook handler that a correctly signed delivery can trigger. Verifying the provider's signature at the relay keeps unsigned requests away from that handler too. The relay only closes the exposure that existed because of webhooks.",{"q":41,"a":42},"What should I do first if my Jenkins is public today?","Upgrade to Jenkins 2.588 or LTS 2.580.2, take the controller off the public internet, and if it was reachable while running an affected version treat it as possibly compromised and rotate the credentials it stores.",{"type":44,"children":45},"root",[46,60],{"type":47,"tag":48,"props":49,"children":50},"element","p",{},[51,58],{"type":47,"tag":52,"props":53,"children":54},"strong",{},[55],{"type":56,"value":57},"text","Most Jenkins vulnerabilities that were exploited in the wild hit the CLI, the web UI or the job-configuration API, not the webhook endpoint, and an attacker can only reach them if the controller is on the public internet.",{"type":56,"value":59}," Those controllers are usually public for one reason: GitHub or GitLab has to deliver a webhook. Webhook Relay's Jenkins plugin connects outward and replays only webhook deliveries, so the vulnerable surface is no longer reachable from the internet.",{"type":47,"tag":48,"props":61,"children":62},{},[63,65,72],{"type":56,"value":64},"This is the CVE history and attack-surface view. For the full hardening checklist (authentication, agents, credentials, TLS) see ",{"type":47,"tag":66,"props":67,"children":69},"a",{"href":68},"/blog/securing-jenkins/",[70],{"type":56,"value":71},"Securing Jenkins",{"type":56,"value":73},".",{"type":44,"children":75,"toc":1218},[76,84,93,100,115,394,434,454,460,465,470,476,481,599,612,618,648,817,849,862,911,952,973,979,984,1090,1096,1101,1167,1173,1212],{"type":47,"tag":48,"props":77,"children":78},{},[79,83],{"type":47,"tag":52,"props":80,"children":81},{},[82],{"type":56,"value":57},{"type":56,"value":59},{"type":47,"tag":48,"props":85,"children":86},{},[87,88,92],{"type":56,"value":64},{"type":47,"tag":66,"props":89,"children":90},{"href":68},[91],{"type":56,"value":71},{"type":56,"value":73},{"type":47,"tag":94,"props":95,"children":97},"h2",{"id":96},"jenkins-security-vulnerabilities-that-were-exploited-and-the-endpoint-each-one-hit",[98],{"type":56,"value":99},"Jenkins security vulnerabilities that were exploited, and the endpoint each one hit",{"type":47,"tag":48,"props":101,"children":102},{},[103,105,113],{"type":56,"value":104},"Every row below links to our curated ",{"type":47,"tag":66,"props":106,"children":110},{"href":107,"rel":108},"https://helix.ml/threat-watch",[109],"nofollow",[111],{"type":56,"value":112},"Threat Watch",{"type":56,"value":114}," record, which carries the sources and timeline.",{"type":47,"tag":116,"props":117,"children":118},"table",{},[119,153],{"type":47,"tag":120,"props":121,"children":122},"thead",{},[123],{"type":47,"tag":124,"props":125,"children":126},"tr",{},[127,133,138,143,148],{"type":47,"tag":128,"props":129,"children":130},"th",{},[131],{"type":56,"value":132},"CVE",{"type":47,"tag":128,"props":134,"children":135},{},[136],{"type":56,"value":137},"What an attacker gets",{"type":47,"tag":128,"props":139,"children":140},{},[141],{"type":56,"value":142},"Auth needed",{"type":47,"tag":128,"props":144,"children":145},{},[146],{"type":56,"value":147},"Exploited in the wild",{"type":47,"tag":128,"props":149,"children":150},{},[151],{"type":56,"value":152},"Fixed in",{"type":47,"tag":154,"props":155,"children":156},"tbody",{},[157,191,233,265,297,329,361],{"type":47,"tag":124,"props":158,"children":159},{},[160,171,176,181,186],{"type":47,"tag":161,"props":162,"children":163},"td",{},[164],{"type":47,"tag":66,"props":165,"children":168},{"href":166,"rel":167},"https://helix.ml/threat-watch/i/jenkins-jenkins-command-line-interface-cli-cve-2024-23897",[109],[169],{"type":56,"value":170},"CVE-2024-23897, the Jenkins CLI file read",{"type":47,"tag":161,"props":172,"children":173},{},[174],{"type":56,"value":175},"Arbitrary file read on the controller, chained to code execution (CVSS 9.8)",{"type":47,"tag":161,"props":177,"children":178},{},[179],{"type":56,"value":180},"None for the first lines of a file; Overall/Read for whole files",{"type":47,"tag":161,"props":182,"children":183},{},[184],{"type":56,"value":185},"Yes; in CISA KEV, flagged for ransomware use",{"type":47,"tag":161,"props":187,"children":188},{},[189],{"type":56,"value":190},"2.442, LTS 2.426.3, LTS 2.440.1",{"type":47,"tag":124,"props":192,"children":193},{},[194,204,218,223,228],{"type":47,"tag":161,"props":195,"children":196},{},[197],{"type":47,"tag":66,"props":198,"children":201},{"href":199,"rel":200},"https://helix.ml/threat-watch/i/jenkins-jenkins-cve-2017-1000353",[109],[202],{"type":56,"value":203},"CVE-2017-1000353, the remoting CLI deserialization RCE",{"type":47,"tag":161,"props":205,"children":206},{},[207,209,216],{"type":56,"value":208},"Remote code execution via a serialized ",{"type":47,"tag":210,"props":211,"children":213},"code",{"className":212},[],[214],{"type":56,"value":215},"SignedObject",{"type":56,"value":217}," (CVSS 9.8)",{"type":47,"tag":161,"props":219,"children":220},{},[221],{"type":56,"value":222},"None",{"type":47,"tag":161,"props":224,"children":225},{},[226],{"type":56,"value":227},"Yes; added to CISA KEV in 2025, no campaign named",{"type":47,"tag":161,"props":229,"children":230},{},[231],{"type":56,"value":232},"2.57, LTS 2.46.2",{"type":47,"tag":124,"props":234,"children":235},{},[236,246,251,255,260],{"type":47,"tag":161,"props":237,"children":238},{},[239],{"type":47,"tag":66,"props":240,"children":243},{"href":241,"rel":242},"https://helix.ml/threat-watch/i/jenkins-jenkins-stapler-web-framework-cve-2018-1000861",[109],[244],{"type":56,"value":245},"CVE-2018-1000861, Stapler URL routing",{"type":47,"tag":161,"props":247,"children":248},{},[249],{"type":56,"value":250},"Crafted URLs reach methods never meant to be web-callable; the building block of a public pre-auth RCE chain (CVSS 9.8)",{"type":47,"tag":161,"props":252,"children":253},{},[254],{"type":56,"value":222},{"type":47,"tag":161,"props":256,"children":257},{},[258],{"type":56,"value":259},"Yes; in CISA KEV",{"type":47,"tag":161,"props":261,"children":262},{},[263],{"type":56,"value":264},"2.154, LTS 2.138.4, LTS 2.150.1",{"type":47,"tag":124,"props":266,"children":267},{},[268,278,283,288,292],{"type":47,"tag":161,"props":269,"children":270},{},[271],{"type":47,"tag":66,"props":272,"children":275},{"href":273,"rel":274},"https://helix.ml/threat-watch/i/jenkins-script-security-plugin-cve-2019-1003029",[109],[276],{"type":56,"value":277},"CVE-2019-1003029, Script Security sandbox bypass",{"type":47,"tag":161,"props":279,"children":280},{},[281],{"type":56,"value":282},"Code execution on the controller (CVSS 9.9)",{"type":47,"tag":161,"props":284,"children":285},{},[286],{"type":56,"value":287},"Overall/Read; none if anonymous has it",{"type":47,"tag":161,"props":289,"children":290},{},[291],{"type":56,"value":259},{"type":47,"tag":161,"props":293,"children":294},{},[295],{"type":56,"value":296},"Script Security 1.54",{"type":47,"tag":124,"props":298,"children":299},{},[300,310,315,320,324],{"type":47,"tag":161,"props":301,"children":302},{},[303],{"type":47,"tag":66,"props":304,"children":307},{"href":305,"rel":306},"https://helix.ml/threat-watch/i/jenkins-matrix-project-plugin-cve-2019-1003030",[109],[308],{"type":56,"value":309},"CVE-2019-1003030, Pipeline: Groovy sandbox bypass",{"type":47,"tag":161,"props":311,"children":312},{},[313],{"type":56,"value":314},"Code execution for anyone who controls a pipeline (CVSS 9.9)",{"type":47,"tag":161,"props":316,"children":317},{},[318],{"type":56,"value":319},"Job/Configure, or a writable Jenkinsfile",{"type":47,"tag":161,"props":321,"children":322},{},[323],{"type":56,"value":259},{"type":47,"tag":161,"props":325,"children":326},{},[327],{"type":56,"value":328},"Pipeline: Groovy 2.64",{"type":47,"tag":124,"props":330,"children":331},{},[332,342,347,351,356],{"type":47,"tag":161,"props":333,"children":334},{},[335],{"type":47,"tag":66,"props":336,"children":339},{"href":337,"rel":338},"https://helix.ml/threat-watch/i/jenkins-jenkins-user-interface-ui-cve-2015-5317",[109],[340],{"type":56,"value":341},"CVE-2015-5317, Fingerprints page leak",{"type":47,"tag":161,"props":343,"children":344},{},[345],{"type":56,"value":346},"Job and build names the viewer cannot see (CVSS 7.5)",{"type":47,"tag":161,"props":348,"children":349},{},[350],{"type":56,"value":222},{"type":47,"tag":161,"props":352,"children":353},{},[354],{"type":56,"value":355},"Listed in KEV; we found no source describing real attacks",{"type":47,"tag":161,"props":357,"children":358},{},[359],{"type":56,"value":360},"1.638, LTS 1.625.2",{"type":47,"tag":124,"props":362,"children":363},{},[364,374,379,384,389],{"type":47,"tag":161,"props":365,"children":366},{},[367],{"type":47,"tag":66,"props":368,"children":371},{"href":369,"rel":370},"https://helix.ml/threat-watch/i/jenkins-multiple-vulnerabilities-fixed-in-2-588-lts-2-580-2-rce-arbitrary-file-read-write-xss-cve-2026-117612-et-al",[109],[372],{"type":56,"value":373},"CVE-2026-117612 to 117626, the 2026-10-28 core advisory",{"type":47,"tag":161,"props":375,"children":376},{},[377],{"type":56,"value":378},"15 bugs: RCE for low-privilege users, file read and write, and CVE-2026-117618, a setup-wizard race that lets an unauthenticated attacker create an admin",{"type":47,"tag":161,"props":380,"children":381},{},[382],{"type":56,"value":383},"Mostly low privilege; none for CVE-2026-117618",{"type":47,"tag":161,"props":385,"children":386},{},[387],{"type":56,"value":388},"No exploitation known",{"type":47,"tag":161,"props":390,"children":391},{},[392],{"type":56,"value":393},"2.588, LTS 2.580.2",{"type":47,"tag":48,"props":395,"children":396},{},[397,399,406,408,415,417,424,426,433],{"type":56,"value":398},"Two notes on accuracy. CISA's KEV entry names CVE-2019-1003030 the \"Matrix Project plugin\", but the Jenkins advisory and NVD place it in Pipeline: Groovy; the Matrix Project bug in the same advisory is a different CVE. And the 2024 ransomware link is documented: ",{"type":47,"tag":66,"props":400,"children":403},{"href":401,"rel":402},"https://www.bleepingcomputer.com/news/security/cisa-warns-of-jenkins-rce-bug-exploited-in-ransomware-attacks/",[109],[404],{"type":56,"value":405},"BleepingComputer, citing Juniper, ties RansomEXX to a July 2024 breach",{"type":56,"value":407}," of an Indian bank technology provider. The vendor advisories are ",{"type":47,"tag":66,"props":409,"children":412},{"href":410,"rel":411},"https://www.jenkins.io/security/advisory/2024-01-24/#SECURITY-3314",[109],[413],{"type":56,"value":414},"2024-01-24",{"type":56,"value":416},", ",{"type":47,"tag":66,"props":418,"children":421},{"href":419,"rel":420},"https://www.jenkins.io/security/advisory/2019-03-06/",[109],[422],{"type":56,"value":423},"2019-03-06",{"type":56,"value":425}," and ",{"type":47,"tag":66,"props":427,"children":430},{"href":428,"rel":429},"https://www.jenkins.io/security/advisory/2026-10-28/",[109],[431],{"type":56,"value":432},"2026-10-28",{"type":56,"value":73},{"type":47,"tag":48,"props":435,"children":436},{},[437,439,445,447,453],{"type":56,"value":438},"The pattern is the point. Look at the endpoints: ",{"type":47,"tag":210,"props":440,"children":442},{"className":441},[],[443],{"type":56,"value":444},"/cli",{"type":56,"value":446},", the remoting port, Stapler-routed UI URLs, script-check URLs and job configuration. None is ",{"type":47,"tag":210,"props":448,"children":450},{"className":449},[],[451],{"type":56,"value":452},"/github-webhook/",{"type":56,"value":73},{"type":47,"tag":94,"props":455,"children":457},{"id":456},"why-exposure-is-the-risk-multiplier",[458],{"type":56,"value":459},"Why exposure is the risk multiplier",{"type":47,"tag":48,"props":461,"children":462},{},[463],{"type":56,"value":464},"A flaw needing a login is a problem for insiders. The same flaw on a public controller is a problem for everyone who can run a scanner. CVE-2024-23897 needed no permission to read the first lines of a file, and the 2026 advisory includes an unauthenticated admin-creation race in the setup wizard, so a fresh controller on a reachable network is at risk. Most of the 2026 bugs need only a low-privilege account, which is easy to obtain on a controller with open sign-up or anonymous read.",{"type":47,"tag":48,"props":466,"children":467},{},[468],{"type":56,"value":469},"Jenkins stores credentials and executes build code, so a successful bug is rarely contained to Jenkins. The Threat Watch records for the exploited CVEs give the same guidance: if the controller was reachable while running an affected version, treat it as possibly compromised and rotate the secrets it holds.",{"type":47,"tag":94,"props":471,"children":473},{"id":472},"what-to-expose-and-what-not-to",[474],{"type":56,"value":475},"What to expose and what not to",{"type":47,"tag":48,"props":477,"children":478},{},[479],{"type":56,"value":480},"A provider such as GitHub needs exactly one thing: a URL that accepts its webhook POST. The Jenkins plugins register these paths:",{"type":47,"tag":116,"props":482,"children":483},{},[484,505],{"type":47,"tag":120,"props":485,"children":486},{},[487],{"type":47,"tag":124,"props":488,"children":489},{},[490,495,500],{"type":47,"tag":128,"props":491,"children":492},{},[493],{"type":56,"value":494},"Provider",{"type":47,"tag":128,"props":496,"children":497},{},[498],{"type":56,"value":499},"Webhook path",{"type":47,"tag":128,"props":501,"children":502},{},[503],{"type":56,"value":504},"Needs to be public?",{"type":47,"tag":154,"props":506,"children":507},{},[508,529,550,571],{"type":47,"tag":124,"props":509,"children":510},{},[511,516,524],{"type":47,"tag":161,"props":512,"children":513},{},[514],{"type":56,"value":515},"GitHub",{"type":47,"tag":161,"props":517,"children":518},{},[519],{"type":47,"tag":210,"props":520,"children":522},{"className":521},[],[523],{"type":56,"value":452},{"type":47,"tag":161,"props":525,"children":526},{},[527],{"type":56,"value":528},"Only as a delivery target",{"type":47,"tag":124,"props":530,"children":531},{},[532,537,546],{"type":47,"tag":161,"props":533,"children":534},{},[535],{"type":56,"value":536},"Bitbucket",{"type":47,"tag":161,"props":538,"children":539},{},[540],{"type":47,"tag":210,"props":541,"children":543},{"className":542},[],[544],{"type":56,"value":545},"/bitbucket-hook/",{"type":47,"tag":161,"props":547,"children":548},{},[549],{"type":56,"value":528},{"type":47,"tag":124,"props":551,"children":552},{},[553,558,567],{"type":47,"tag":161,"props":554,"children":555},{},[556],{"type":56,"value":557},"GitLab",{"type":47,"tag":161,"props":559,"children":560},{},[561],{"type":47,"tag":210,"props":562,"children":564},{"className":563},[],[565],{"type":56,"value":566},"/project/NAME",{"type":47,"tag":161,"props":568,"children":569},{},[570],{"type":56,"value":528},{"type":47,"tag":124,"props":572,"children":573},{},[574,586,591],{"type":47,"tag":161,"props":575,"children":576},{},[577,579,584],{"type":56,"value":578},"Everything else (UI, ",{"type":47,"tag":210,"props":580,"children":582},{"className":581},[],[583],{"type":56,"value":444},{"type":56,"value":585},", REST API, script-check URLs, setup wizard, agent port)",{"type":47,"tag":161,"props":587,"children":588},{},[589],{"type":56,"value":590},"n/a",{"type":47,"tag":161,"props":592,"children":593},{},[594],{"type":47,"tag":52,"props":595,"children":596},{},[597],{"type":56,"value":598},"No",{"type":47,"tag":48,"props":600,"children":601},{},[602,604,610],{"type":56,"value":603},"See ",{"type":47,"tag":66,"props":605,"children":607},{"href":606},"/blog/jenkins-webhooks/",[608],{"type":56,"value":609},"the complete Jenkins webhook URL reference",{"type":56,"value":611}," for the exact forms. The goal is that the public side is a Webhook Relay bucket URL, and Jenkins keeps no public address at all.",{"type":47,"tag":94,"props":613,"children":615},{"id":614},"step-by-step-receive-webhooks-without-exposing-jenkins",[616],{"type":56,"value":617},"Step-by-step: receive webhooks without exposing Jenkins",{"type":47,"tag":48,"props":619,"children":620},{},[621,623,630,632,638,640,647],{"type":56,"value":622},"The primary path is the ",{"type":47,"tag":66,"props":624,"children":627},{"href":625,"rel":626},"https://plugins.jenkins.io/webhook-relay",[109],[628],{"type":56,"value":629},"Webhook Relay Jenkins plugin",{"type":56,"value":631},", documented at ",{"type":47,"tag":66,"props":633,"children":635},{"href":634},"/docs/tutorials/cicd/jenkins-plugin/",[636],{"type":56,"value":637},"the Jenkins plugin tutorial",{"type":56,"value":639},". It needs Jenkins 2.479.1 or newer and a Webhook Relay account with an ",{"type":47,"tag":66,"props":641,"children":644},{"href":642,"rel":643},"https://my.webhookrelay.com/tokens",[109],[645],{"type":56,"value":646},"API token",{"type":56,"value":73},{"type":47,"tag":649,"props":650,"children":651},"ol",{},[652,677,726,743,782,807],{"type":47,"tag":653,"props":654,"children":655},"li",{},[656,661,663,668,670,675],{"type":47,"tag":52,"props":657,"children":658},{},[659],{"type":56,"value":660},"Install the plugin.",{"type":56,"value":662}," In ",{"type":47,"tag":52,"props":664,"children":665},{},[666],{"type":56,"value":667},"Manage Jenkins → Plugins → Available",{"type":56,"value":669},", search for ",{"type":47,"tag":52,"props":671,"children":672},{},[673],{"type":56,"value":674},"Webhook Relay",{"type":56,"value":676}," and install it.",{"type":47,"tag":653,"props":678,"children":679},{},[680,685,686,691,693,697,699,704,706,711,713,718,720,725],{"type":47,"tag":52,"props":681,"children":682},{},[683],{"type":56,"value":684},"Configure the connection.",{"type":56,"value":662},{"type":47,"tag":52,"props":687,"children":688},{},[689],{"type":56,"value":690},"Manage Jenkins → System",{"type":56,"value":692},", open the ",{"type":47,"tag":52,"props":694,"children":695},{},[696],{"type":56,"value":674},{"type":56,"value":698}," section. Enter your API Key and API Secret, a bucket name (an existing one, or a new name the plugin creates), pick the ",{"type":47,"tag":52,"props":700,"children":701},{},[702],{"type":56,"value":703},"SCM Webhook Preset",{"type":56,"value":705}," (GitHub, GitLab, Bitbucket or Generic Webhook Trigger), tick ",{"type":47,"tag":52,"props":707,"children":708},{},[709],{"type":56,"value":710},"Enable",{"type":56,"value":712}," and save. ",{"type":47,"tag":52,"props":714,"children":715},{},[716],{"type":56,"value":717},"Connection Status",{"type":56,"value":719}," should read ",{"type":47,"tag":52,"props":721,"children":722},{},[723],{"type":56,"value":724},"Subscribed",{"type":56,"value":73},{"type":47,"tag":653,"props":727,"children":728},{},[729,734,736,741],{"type":47,"tag":52,"props":730,"children":731},{},[732],{"type":56,"value":733},"Get the public URL.",{"type":56,"value":735}," Click ",{"type":47,"tag":52,"props":737,"children":738},{},[739],{"type":56,"value":740},"Get Webhook URL",{"type":56,"value":742},". The plugin resolves the bucket's public input URL.",{"type":47,"tag":653,"props":744,"children":745},{},[746,751,753,759,761,767,769,774,776,781],{"type":47,"tag":52,"props":747,"children":748},{},[749],{"type":56,"value":750},"Point the provider at it.",{"type":56,"value":752}," GitHub: ",{"type":47,"tag":754,"props":755,"children":756},"em",{},[757],{"type":56,"value":758},"Settings → Webhooks → Add webhook → Payload URL",{"type":56,"value":760},", content type ",{"type":47,"tag":210,"props":762,"children":764},{"className":763},[],[765],{"type":56,"value":766},"application/json",{"type":56,"value":768},". GitLab: ",{"type":47,"tag":754,"props":770,"children":771},{},[772],{"type":56,"value":773},"Settings → Webhooks → URL",{"type":56,"value":775},". Bitbucket: ",{"type":47,"tag":754,"props":777,"children":778},{},[779],{"type":56,"value":780},"Repository settings → Webhooks → Add webhook → URL",{"type":56,"value":73},{"type":47,"tag":653,"props":783,"children":784},{},[785,790,792,797,799,805],{"type":47,"tag":52,"props":786,"children":787},{},[788],{"type":56,"value":789},"Configure the job normally.",{"type":56,"value":791}," For GitHub, enable ",{"type":47,"tag":52,"props":793,"children":794},{},[795],{"type":56,"value":796},"GitHub hook trigger for GITScm polling",{"type":56,"value":798}," (",{"type":47,"tag":210,"props":800,"children":802},{"className":801},[],[803],{"type":56,"value":804},"githubPush()",{"type":56,"value":806}," in a declarative pipeline).",{"type":47,"tag":653,"props":808,"children":809},{},[810,815],{"type":47,"tag":52,"props":811,"children":812},{},[813],{"type":56,"value":814},"Close the inbound path.",{"type":56,"value":816}," Remove the port forward, public DNS record or reverse-proxy rule that made Jenkins reachable.",{"type":47,"tag":48,"props":818,"children":819},{},[820,822,827,828,833,835,840,842,848],{"type":56,"value":821},"The plugin subscribes over a persistent outbound WebSocket and replays each delivery to the Jenkins endpoint for your SCM (",{"type":47,"tag":210,"props":823,"children":825},{"className":824},[],[826],{"type":56,"value":452},{"type":56,"value":416},{"type":47,"tag":210,"props":829,"children":831},{"className":830},[],[832],{"type":56,"value":545},{"type":56,"value":834},", or ",{"type":47,"tag":210,"props":836,"children":838},{"className":837},[],[839],{"type":56,"value":566},{"type":56,"value":841}," for GitLab). No inbound ports are opened on Jenkins. The flow is ",{"type":47,"tag":210,"props":843,"children":845},{"className":844},[],[846],{"type":56,"value":847},"GitHub → bucket URL → outbound WebSocket → /github-webhook/",{"type":56,"value":73},{"type":47,"tag":48,"props":850,"children":851},{},[852,854,860],{"type":56,"value":853},"If you prefer a standalone agent, the ",{"type":47,"tag":66,"props":855,"children":857},{"href":856},"/docs/tutorials/cicd/jenkins-github/",[858],{"type":56,"value":859},"Jenkins and GitHub tutorial",{"type":56,"value":861}," uses the CLI:",{"type":47,"tag":863,"props":864,"children":868},"pre",{"className":865,"code":866,"language":867,"meta":7,"style":7},"language-bash shiki shiki-themes github-dark","relay forward --bucket github-jenkins http://localhost:8080/github-webhook/ --no-agent\n","bash",[869],{"type":47,"tag":210,"props":870,"children":871},{"__ignoreMap":7},[872],{"type":47,"tag":873,"props":874,"children":877},"span",{"class":875,"line":876},"line",1,[878,884,890,896,901,906],{"type":47,"tag":873,"props":879,"children":881},{"style":880},"--shiki-default:#B392F0",[882],{"type":56,"value":883},"relay",{"type":47,"tag":873,"props":885,"children":887},{"style":886},"--shiki-default:#9ECBFF",[888],{"type":56,"value":889}," forward",{"type":47,"tag":873,"props":891,"children":893},{"style":892},"--shiki-default:#79B8FF",[894],{"type":56,"value":895}," --bucket",{"type":47,"tag":873,"props":897,"children":898},{"style":886},[899],{"type":56,"value":900}," github-jenkins",{"type":47,"tag":873,"props":902,"children":903},{"style":886},[904],{"type":56,"value":905}," http://localhost:8080/github-webhook/",{"type":47,"tag":873,"props":907,"children":908},{"style":892},[909],{"type":56,"value":910}," --no-agent\n",{"type":47,"tag":48,"props":912,"children":913},{},[914,919,921,927,929,935,937,942,944,950],{"type":47,"tag":52,"props":915,"children":916},{},[917],{"type":56,"value":918},"Lock the destination path.",{"type":56,"value":920}," By default an output appends any extra path the sender adds to the input URL (a POST to ",{"type":47,"tag":210,"props":922,"children":924},{"className":923},[],[925],{"type":56,"value":926},"\u003Cinput URL>/anything",{"type":56,"value":928}," goes to ",{"type":47,"tag":210,"props":930,"children":932},{"className":931},[],[933],{"type":56,"value":934},"\u003Cdestination>/anything",{"type":56,"value":936},"). Outputs created in the dashboard have ",{"type":47,"tag":52,"props":938,"children":939},{},[940],{"type":56,"value":941},"Destination URL path locked",{"type":56,"value":943}," switched on; outputs created with the CLI or API do not, so open the output and turn it on (API/MCP field ",{"type":47,"tag":210,"props":945,"children":947},{"className":946},[],[948],{"type":56,"value":949},"lock_path: true",{"type":56,"value":951},"). With the path locked, every delivery goes to exactly the destination URL and nothing else on Jenkins is reachable through the relay. The plugin path above needs no extra step: with no output on the bucket it always delivers to the preset endpoint.",{"type":47,"tag":48,"props":953,"children":954},{},[955,957,963,965,971],{"type":56,"value":956},"It prints a ",{"type":47,"tag":210,"props":958,"children":960},{"className":959},[],[961],{"type":56,"value":962},"*.hooks.webhookrelay.com",{"type":56,"value":964}," URL for your provider; run the agent as a Docker container or a background service so it reconnects after restarts. The general pattern is in ",{"type":47,"tag":66,"props":966,"children":968},{"href":967},"/docs/webhooks/internal/localhost/",[969],{"type":56,"value":970},"receiving webhooks on localhost",{"type":56,"value":972},". Either way, deliveries appear on the bucket's logs page with the response Jenkins returned.",{"type":47,"tag":94,"props":974,"children":976},{"id":975},"hardening-that-still-matters",[977],{"type":56,"value":978},"Hardening that still matters",{"type":47,"tag":48,"props":980,"children":981},{},[982],{"type":56,"value":983},"Removing exposure does not make Jenkins safe; it removes the cheapest attack route.",{"type":47,"tag":985,"props":986,"children":987},"ul",{},[988,998,1013,1072],{"type":47,"tag":653,"props":989,"children":990},{},[991,996],{"type":47,"tag":52,"props":992,"children":993},{},[994],{"type":56,"value":995},"Patch.",{"type":56,"value":997}," Upgrade to 2.588 or LTS 2.580.2 and keep Script Security and Pipeline: Groovy current. Most of the CVEs above are years old, which means forgotten Jenkins hosts are the real target.",{"type":47,"tag":653,"props":999,"children":1000},{},[1001,1006,1008,1012],{"type":47,"tag":52,"props":1002,"children":1003},{},[1004],{"type":56,"value":1005},"Authenticate.",{"type":56,"value":1007}," Remove Overall/Read from anonymous and keep sign-up off. The checklist is in ",{"type":47,"tag":66,"props":1009,"children":1010},{"href":68},[1011],{"type":56,"value":71},{"type":56,"value":73},{"type":47,"tag":653,"props":1014,"children":1015},{},[1016,1021,1023,1029,1031,1037,1039,1044,1045,1050,1052,1056,1058,1064,1065,1071],{"type":47,"tag":52,"props":1017,"children":1018},{},[1019],{"type":56,"value":1020},"Verify signatures at the relay.",{"type":56,"value":1022}," The public bucket URL is still a URL anyone can post to. Attach an ",{"type":47,"tag":66,"props":1024,"children":1026},{"href":1025},"/docs/webhooks/auth/hmac/",[1027],{"type":56,"value":1028},"HMAC verification function",{"type":56,"value":1030}," to the bucket input and Webhook Relay checks GitHub's ",{"type":47,"tag":210,"props":1032,"children":1034},{"className":1033},[],[1035],{"type":56,"value":1036},"X-Hub-Signature-256",{"type":56,"value":1038}," before anything is forwarded: requests without a valid signature get a 401 at the relay and never reach Jenkins. You can also keep a shared secret in Jenkins (GitHub section, ",{"type":47,"tag":52,"props":1040,"children":1041},{},[1042],{"type":56,"value":1043},"Advanced",{"type":56,"value":416},{"type":47,"tag":52,"props":1046,"children":1047},{},[1048],{"type":56,"value":1049},"Shared secrets",{"type":56,"value":1051},"), as the ",{"type":47,"tag":66,"props":1053,"children":1054},{"href":856},[1055],{"type":56,"value":859},{"type":56,"value":1057}," shows, as a second check. For the details, read ",{"type":47,"tag":66,"props":1059,"children":1061},{"href":1060},"/blog/verify-webhook-signature/",[1062],{"type":56,"value":1063},"how to verify a webhook signature",{"type":56,"value":425},{"type":47,"tag":66,"props":1066,"children":1068},{"href":1067},"/blog/webhook-security/",[1069],{"type":56,"value":1070},"webhook security best practices",{"type":56,"value":73},{"type":47,"tag":653,"props":1073,"children":1074},{},[1075,1080,1082,1088],{"type":47,"tag":52,"props":1076,"children":1077},{},[1078],{"type":56,"value":1079},"Filter.",{"type":56,"value":1081}," A forwarding rule or function on the bucket can drop events you never build on, such as pushes to other branches, so Jenkins never sees them. See ",{"type":47,"tag":66,"props":1083,"children":1085},{"href":1084},"/blog/webhooks-in-ci-cd/",[1086],{"type":56,"value":1087},"webhooks in CI/CD",{"type":56,"value":1089}," for the wider pattern.",{"type":47,"tag":94,"props":1091,"children":1093},{"id":1092},"honest-limits",[1094],{"type":56,"value":1095},"Honest limits",{"type":47,"tag":48,"props":1097,"children":1098},{},[1099],{"type":56,"value":1100},"The relay is not a patch, and it does not help in these cases:",{"type":47,"tag":985,"props":1102,"children":1103},{},[1104,1121,1131,1141,1157],{"type":47,"tag":653,"props":1105,"children":1106},{},[1107,1112,1114,1119],{"type":47,"tag":52,"props":1108,"children":1109},{},[1110],{"type":56,"value":1111},"A flaw in the webhook handler itself.",{"type":56,"value":1113}," The relay forwards webhook traffic to it by design. With ",{"type":47,"tag":66,"props":1115,"children":1116},{"href":1025},[1117],{"type":56,"value":1118},"signature verification at the relay",{"type":56,"value":1120},", only requests your provider signed get that far, so an anonymous attacker cannot reach the handler at all. What remains is attacker-controlled content inside a genuine delivery, such as a branch name or pull request title. Patching fixes that.",{"type":47,"tag":653,"props":1122,"children":1123},{},[1124,1129],{"type":47,"tag":52,"props":1125,"children":1126},{},[1127],{"type":56,"value":1128},"Users who need the UI from outside.",{"type":56,"value":1130}," The relay carries webhooks, not logins. Put the UI behind a VPN or identity-aware proxy.",{"type":47,"tag":653,"props":1132,"children":1133},{},[1134,1139],{"type":47,"tag":52,"props":1135,"children":1136},{},[1137],{"type":56,"value":1138},"Agents and workers connecting from outside.",{"type":56,"value":1140}," Inbound agent connections need their own private network path.",{"type":47,"tag":653,"props":1142,"children":1143},{},[1144,1149,1151,1156],{"type":47,"tag":52,"props":1145,"children":1146},{},[1147],{"type":56,"value":1148},"Insiders and compromised networks.",{"type":56,"value":1150}," Anyone already inside can still reach ",{"type":47,"tag":210,"props":1152,"children":1154},{"className":1153},[],[1155],{"type":56,"value":444},{"type":56,"value":73},{"type":47,"tag":653,"props":1158,"children":1159},{},[1160,1165],{"type":47,"tag":52,"props":1161,"children":1162},{},[1163],{"type":56,"value":1164},"Controllers already breached.",{"type":56,"value":1166}," Closing the door after a compromise does not undo it; rotate credentials and rebuild.",{"type":47,"tag":94,"props":1168,"children":1170},{"id":1169},"more-self-hosted-tools-that-are-public-only-for-webhooks",[1171],{"type":56,"value":1172},"More self-hosted tools that are public only for webhooks",{"type":47,"tag":985,"props":1174,"children":1175},{},[1176,1185,1194,1203],{"type":47,"tag":653,"props":1177,"children":1178},{},[1179],{"type":47,"tag":66,"props":1180,"children":1182},{"href":1181},"/blog/teamcity-vulnerabilities/",[1183],{"type":56,"value":1184},"TeamCity CVE-2023-42793 and CVE-2024-27198",{"type":47,"tag":653,"props":1186,"children":1187},{},[1188],{"type":47,"tag":66,"props":1189,"children":1191},{"href":1190},"/blog/n8n-security/",[1192],{"type":56,"value":1193},"n8n security after CVE-2025-68613",{"type":47,"tag":653,"props":1195,"children":1196},{},[1197],{"type":47,"tag":66,"props":1198,"children":1200},{"href":1199},"/blog/langflow-vulnerabilities/",[1201],{"type":56,"value":1202},"Langflow vulnerabilities, from CVE-2025-3248 on",{"type":47,"tag":653,"props":1204,"children":1205},{},[1206],{"type":47,"tag":66,"props":1207,"children":1209},{"href":1208},"/blog/kestra-security/",[1210],{"type":56,"value":1211},"Kestra security and CVE-2026-49869",{"type":47,"tag":1213,"props":1214,"children":1215},"style",{},[1216],{"type":56,"value":1217},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":1219,"depth":1219,"links":1220},3,[1221,1223,1224,1225,1226,1227,1228],{"id":96,"depth":1222,"text":99},2,{"id":456,"depth":1222,"text":459},{"id":472,"depth":1222,"text":475},{"id":614,"depth":1222,"text":617},{"id":975,"depth":1222,"text":978},{"id":1092,"depth":1222,"text":1095},{"id":1169,"depth":1222,"text":1172},"markdown","content:blog:jenkins-vulnerabilities.md","content","blog/jenkins-vulnerabilities.md","blog/jenkins-vulnerabilities","md",{"loc":4},8,1793261894052]