home intel cve-2026-42523-jenkins-github-plugin-stored-xss
CVE Analysis 2026-04-29 · 8 min read

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#github-plugin#stored-xss#javascript-injection#privilege-escalation
Technical mode — for security professionals
▶ Vulnerability overview — CVE-2026-42523 · Vulnerability
ATTACKERCross-platformVULNERABILITYCVE-2026-42523CRITICALSYSTEM COMPROMISEDNo confirmed exploits

Vulnerability Overview

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
  • Affected versions: ≤ 1.46.0
  • Fixed version: 1.47.0 (per Jenkins Security Advisory 2026-04-29, SECURITY-3704)
  • 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:

grep -r "" $JENKINS_HOME/jobs/*/config.xml
grep -r "