high

CVE

CVE-2026-117612, CVE-2026-117613, CVE-2026-117614, CVE-2026-117615, CVE-2026-117616, CVE-2026-117617, CVE-2026-117618, CVE-2026-117619, CVE-2026-117620, CVE-2026-117621, CVE-2026-117622, CVE-2026-117623, CVE-2026-117624, CVE-2026-117625, CVE-2026-117626

CWE

CWE-502, CWE-22, CWE-59, CWE-362, CWE-285, CWE-79, CWE-613

Affected Surface

  • Jenkins weekly versions `<= 2.587` (fixed in `2.588`)
  • Jenkins LTS versions `<= 2.580.1` (fixed in `2.580.2`)
  • CI/CD build controllers exposing `Overall/Read`, `Item/Configure`, or `Agent/Configure` permissions to developers, contractors, or automated webhooks

TL;DR

On 28 October 2026, the Jenkins project published Jenkins Security Advisory 2026-10-28 alongside weekly release 2.588 and Long-Term Support (LTS) release 2.580.2. The update patches 14 vulnerabilities in Jenkins core (CVE-2026-117612 through CVE-2026-117620 and CVE-2026-117621 through CVE-2026-117626), 12 of which are rated High severity.

Several of the bugs give users holding only Overall/Read or Item/Configure permissions a path to controller remote code execution or administrator account takeover:

  1. XStream resolves-to JEP-200 filter bypass (SECURITY-4125 / CVE-2026-117612, High): In RobustReflectionConverter.instantiateNewInstance(), when an XML element carried XStream’s resolves-to attribute, Jenkins called mapper.realClass(readResolveValue) directly without checking ClassFilter.DEFAULT.isBlacklisted(...). Any authenticated user with Overall/Read (or any */Configure permission allowing POST config.xml) could instantiate blacklisted gadget classes and reach controller RCE.
  2. Jenkins.getOrLoadNode() HTTP path traversal to arbitrary config.xml overwrite (SECURITY-4188 / CVE-2026-117616, High): Stapler exposed Jenkins.getOrLoadNode(String nodeName) as a web-reachable route to anyone with Overall/Read, and Nodes.load(String name) neither validated name with Jenkins.checkGoodName() nor verified that the <name> inside the loaded XML matched the requested path. A caller with Overall/Read could traverse to any config.xml on the controller filesystem, load it as an agent, and trigger a save that overwrote the target config.xml.
  3. OldDataMonitor user-account overwrite (SECURITY-4226 / CVE-2026-117620, High): When a user submitted XML with obsolete/unrecognized fields via POST config.xml, RobustReflectionConverter recorded the object in OldDataMonitor (“Manage Old Data”) just as if it had been loaded from disk. An attacker with Overall/Read or */Configure could poison OldDataMonitor so that when an administrator reviewed and clicked Save in “Manage Old Data”, Jenkins overwrote a target user account’s config.xml (including its password hash on HudsonPrivateSecurityRealm).
  4. Unauthenticated setup-wizard admin creation race (SECURITY-4122 / CVE-2026-117618), legacy agentCommand RCE (SECURITY-4123 / CVE-2026-117614), and symlink file read/write (SECURITY-4187 / CVE-2026-117615, SECURITY-4248 / CVE-2026-117617): During initial setup, replacing the default admin account momentarily dropped the user count to zero while doCreateFirstAccount failed to check isSetupComplete(), letting an unauthenticated caller race in and create an admin account (CVE-2026-117618). Meanwhile, Slave.getLauncher() converted a legacy agentCommand XML field into a controller CommandLauncher without script approval (CVE-2026-117614), job rename fallback copying followed symlinks (CVE-2026-117615), and parallel pipeline archiveArtifacts steps could race symlink creation to write arbitrary files on the controller (CVE-2026-117617).

What to do now: Upgrade Jenkins weekly to 2.588 and Jenkins LTS to 2.580.2 immediately, and audit $JENKINS_HOME/users/, $JENKINS_HOME/nodes/, and OldDataMonitor entries for unexpected modifications.

Affected versions and CVE summary

CVEAdvisory IDSeverityMinimum privilege requiredSummary & vulnerable componentFixed in
CVE-2026-117612SECURITY-4125HighOverall/Read (authenticated) or */ConfigureRobustReflectionConverter.instantiateNewInstance() skipped ClassFilter.DEFAULT on resolves-to attributes, allowing arbitrary class instantiation and RCE2.588, 2.580.2
CVE-2026-117616SECURITY-4188HighOverall/ReadStapler-exposed Jenkins.getOrLoadNode() and Nodes.load() allowed path traversal to load and overwrite arbitrary config.xml files on the controller2.588, 2.580.2
CVE-2026-117620SECURITY-4226HighOverall/ReadOldDataMonitor.report() accepted user-submitted XML errors, letting attackers overwrite admin user accounts (including passwords) when an admin saved Old Data2.588, 2.580.2
CVE-2026-117618SECURITY-4122HighUnauthenticated (during setup wizard)HudsonPrivateSecurityRealm.doCreateFirstAccount() did not check isSetupComplete(), allowing an unauthenticated race to create an admin user while the initial user was replaced2.588, 2.580.2
CVE-2026-117614SECURITY-4123HighAgent/ConfigureSlave.getLauncher() converted deprecated <agentCommand> XML fields into hudson.slaves.CommandLauncher without Script Security approval, executing commands on the controller JVM2.588, 2.580.2
CVE-2026-117615SECURITY-4187HighItem/Create + Item/ConfigureAbstractItem.renameTo() fell back to copying the job directory when renameTo() failed, following symlinks to copy arbitrary controller files into the job workspace2.588, 2.580.2
CVE-2026-117617SECURITY-4248HighItem/ConfigureTOCTOU symlink race during artifact extraction (FilePath / Util) allowed parallel pipeline branches to write arbitrary files on the controller filesystem2.588, 2.580.2
CVE-2026-117613SECURITY-4225HighItem/ConfigureRobustReflectionConverter did not verify runtime element types of deserialized collections/maps against generic field declarations, enabling stored XSS2.588, 2.580.2
CVE-2026-117624SECURITY-4243High*/ConfigureIncomplete PersistenceRoot check (SECURITY-3972 bypass) allowed top-level configuration types (User) to be deserialized as nested objects2.588, 2.580.2
CVE-2026-117619SECURITY-4189HighValid API tokenRequests authenticated via HTTP Basic + API token could mint a persistent “remember me” cookie that survived API token revocation or expiration2.588, 2.580.2
CVE-2026-117621, 117622, 117623SECURITY-4232, 4249, 4229HighAgent process (4232, 4249) or Overall/Read (4229)Stored/agent-to-controller XSS via workspace browser links, agent exceptions implementing Stapler HttpResponse, and unescaped breadcrumb overflow tooltips2.588, 2.580.2
CVE-2026-117625, 117626SECURITY-4206, 4237MediumOverall/ReadMissing permission check on HTTP proxy configuration endpoint (4206) and path-traversal metadata leak in workspace / userContent file browser (4237)2.588, 2.580.2

Technical analysis

1. CVE-2026-117612 (SECURITY-4125): XStream resolves-to attribute bypassed ClassFilter.DEFAULT

To defend against XStream deserialization gadgets that execute in #readResolve(), Jenkins enforces JEP-200 (hudson.remoting.ClassFilter.DEFAULT) through a custom Mapper wrapper inside XStream2. However, inside core/src/main/java/hudson/util/RobustReflectionConverter.java, instantiateNewInstance() looked up the resolves-to XML attribute and passed its value straight to mapper.realClass(readResolveValue) without checking ClassFilter.DEFAULT:

--- a/core/src/main/java/hudson/util/RobustReflectionConverter.java
+++ b/core/src/main/java/hudson/util/RobustReflectionConverter.java
@@ -660,8 +661,17 @@ private Class determineWhichClassDefinesField(HierarchicalStreamReader reader) {
     protected Object instantiateNewInstance(HierarchicalStreamReader reader, UnmarshallingContext context) {
         String readResolveValue = reader.getAttribute(mapper.aliasForAttribute("resolves-to"));
-
-        Class type = readResolveValue != null ? mapper.realClass(readResolveValue) : context.getRequiredType();
+        Class type;
+        if (readResolveValue != null) {
+            type = mapper.realClass(readResolveValue);
+            String name = type.getName();
+            if (ClassFilter.DEFAULT.isBlacklisted(name) || ClassFilter.DEFAULT.isBlacklisted(type)) {
+                final String msg = "Refusing to instantiate resolves-to type '" + name + "'";
+                throw new CriticalXStreamException(new XStreamException(msg));
+            }
+        } else {
+            type = context.getRequiredType();
+        }

Commit 5c7550aba5 adds the explicit ClassFilter.DEFAULT.isBlacklisted() check on resolves-to classes and also wires XStream’s native TypePermission system into XStream2 as defense in depth.

2. CVE-2026-117616 (SECURITY-4188): Jenkins.getOrLoadNode() Stapler traversal and config.xml overwrite

In core/src/main/java/jenkins/model/Jenkins.java, the public method getOrLoadNode(String nodeName) lacked the @StaplerNotDispatchable annotation, allowing Stapler’s URL router to invoke it on HTTP requests from any user with Overall/Read.

When called, Nodes.load(String name) constructed getConfigFile(name) ($JENKINS_HOME/nodes/<name>/config.xml) without calling Jenkins.checkGoodName(name) and without verifying that the deserialized Node.getNodeName() matched the name parameter:

--- a/core/src/main/java/jenkins/model/Jenkins.java
+++ b/core/src/main/java/jenkins/model/Jenkins.java
@@ -2168,6 +2168,7 @@ public JDK getJDK(String name) {
     @CheckForNull
     @Restricted(Beta.class)
+    @StaplerNotDispatchable
     public Node getOrLoadNode(String nodeName) {
         return getNodesObject().getOrLoad(nodeName);
     }
--- a/core/src/main/java/jenkins/model/Nodes.java
+++ b/core/src/main/java/jenkins/model/Nodes.java
@@ -408,10 +408,17 @@ public Node getOrLoad(String name) {
     @CheckForNull
     public Node load(String name) {
+        if (ENFORCE_NAME_RESTRICTIONS) {
+            Jenkins.checkGoodName(name);
+        }
+
         try {
             XmlFile xmlFile = getConfigFile(name);
             if (xmlFile.exists()) {
                 Node n = (Node) xmlFile.read();
+                if (!name.equals(n.getNodeName())) {
+                    throw new IOException("Node name " + n.getNodeName() + " in " + xmlFile + " does not match " + name);
+                }

By passing a relative traversal path (../../...) to getOrLoadNode, an attacker with Overall/Read could make Jenkins read any config.xml file on the controller filesystem as a Node whose <name> pointed at another path, register it in nodes, and cause subsequent saves to overwrite the target config.xml (commit c77d00fd3b).

3. CVE-2026-117620 (SECURITY-4226) and CVE-2026-117618 (SECURITY-4122): administrator account takeover via OldDataMonitor and setup-wizard race

Two separate bugs targeted Jenkins’s user management:

  • OldDataMonitor poisoning (CVE-2026-117620, commit 2516dd6846): When RobustReflectionConverter encountered an unrecognized XML element on a Saveable object, it called OldDataMonitor.report(Saveable, ...) so an administrator could click “Discard Unreadable Data” in Manage Jenkins -> Manage Old Data, which calls .save() on every recorded Saveable. Because OldDataMonitor.report() was not restricted to XmlFile loads from disk, user-submitted XML containing an embedded User object with a bogus field registered that attacker-crafted User instance in OldDataMonitor. When an administrator later clicked the cleanup button in Manage Old Data, Jenkins saved the attacker’s User object to disk, overwriting the administrator’s password hash on controllers using Jenkins’s built-in user database.
  • Unauthenticated setup-wizard race (CVE-2026-117618, commit 5c180d1edc): In HudsonPrivateSecurityRealm.java, doCreateFirstAccount() only checked if (hasSomeUser()) before letting an unauthenticated HTTP request create an admin account. When an administrator completed the initial setup wizard by creating a named admin account, Jenkins deleted the initial admin user before writing the new one, opening a window where hasSomeUser() returned false while isSetupComplete() was also false:
--- a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
+++ b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
@@ -398,7 +398,7 @@ private String getErrorMessages(SignupInfo si) {
     public synchronized void doCreateFirstAccount(StaplerRequest2 req, StaplerResponse2 rsp)
             throws IOException, ServletException {
-        if (hasSomeUser()) {
+        if (!Jenkins.get().getInstallState().isSetupComplete() || hasSomeUser()) {
             rsp.sendError(SC_UNAUTHORIZED, "First user was already created");
             return;
         }
  • Legacy <agentCommand> bypasses Script Security (CVE-2026-117614, commit dee305a355): For backward compatibility with Jenkins < 1.216, Slave.java kept a @Deprecated @XStreamDeserializable private transient String agentCommand field. If an attacker with Agent/Configure submitted an agent config.xml with launcher omitted and <agentCommand>...</agentCommand> set, Slave.getLauncher() reflectively instantiated hudson.slaves.CommandLauncher(agentCommand, null) and called save() without checking whether an administrator had approved the command in Script Security. 2.588 and 2.580.2 delete the agentCommand field and migration block entirely.
  • Symlink following on job rename (CVE-2026-117615, commit 37a111f9cb) and parallel artifact extraction (CVE-2026-117617, commit 4a2ba37967): When AbstractItem.renameTo() failed to move a job directory atomically (for example while a file inside the directory was held open), it fell back to copying the directory tree and followed symbolic links on the controller filesystem (CVE-2026-117615). And when extracting archived build artifacts onto the controller, FilePath only checked for symlinks before creating parent directories rather than immediately before opening the destination file, allowing two parallel Pipeline branches running archiveArtifacts to race a symlink into place and write arbitrary files outside the build directory (CVE-2026-117617).

Detection and remediation

# 1. Check running Jenkins version from the CLI, HTTP response header, or WAR manifest
curl -sI http://localhost:8080/login | grep -i 'X-Jenkins:' || true

# 2. Check Dockerfiles, Helm charts, and Kubernetes manifests for unpatched jenkins/jenkins tags
rg -n 'jenkins/jenkins(:|@|\s|$)' Dockerfile* docker-compose*.yml k8s/ helm/ .github/ 2>/dev/null

# 3. Audit $JENKINS_HOME for legacy <agentCommand> entries, resolves-to attributes, or suspicious symlinks
rg -n 'resolves-to|<agentCommand>' "${JENKINS_HOME:-/var/lib/jenkins}"/jobs/ "${JENKINS_HOME:-/var/lib/jenkins}"/nodes/ "${JENKINS_HOME:-/var/lib/jenkins}"/users/ 2>/dev/null
find "${JENKINS_HOME:-/var/lib/jenkins}/jobs" -type l -ls 2>/dev/null
  1. Upgrade Jenkins controllers immediately:
    • Upgrade Jenkins weekly deployments to 2.588 or later.
    • Upgrade Jenkins LTS deployments to 2.580.2 or later (jenkins/jenkins:2.580.2-lts or jenkins/jenkins:2.588).
  2. Audit OldDataMonitor and user accounts before clicking Save: Before using Manage Jenkins -> Manage Old Data -> Discard Unreadable Data on an unpatched controller, inspect the listed objects for hudson.model.User entries (CVE-2026-117620), and verify $JENKINS_HOME/users/*/config.xml for unauthorized accounts created during setup (CVE-2026-117618).
  3. Invalidate sessions if API tokens were revoked (CVE-2026-117619): If you revoked or rotated a compromised Jenkins API token while running <= 2.587 or <= 2.580.1, also rotate the user’s password or seed to invalidate any persistent “remember me” cookie minted with that token.

Timeline

  • 2026-10-21 (12:14 to 12:15 UTC): Security fix commits 0679690b8c through c41ccc102c (SECURITY-4122 through SECURITY-4249) are committed to the Jenkins security branches and master / stable-2.580.
  • 2026-10-26: Release tags jenkins-2.580.2 (11:33 UTC) and jenkins-2.588 (15:08 UTC) are prepared.
  • 2026-10-28 (16:19 UTC): The Jenkins project publishes Security Advisory 2026-10-28 and posts the announcement for CVE-2026-117612 through CVE-2026-117626 to oss-security (2026/10/28/6).

From research to remediation

Check whether this pattern exists in your codebase

Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.

References