VYPR
Moderate severityNVD Advisory· Published Jul 27, 2022· Updated Aug 3, 2024

CVE-2022-36887

CVE-2022-36887

Description

CSRF in Jenkins Job Configuration History Plugin lets attackers delete or restore config history entries without authentication.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CSRF in Jenkins Job Configuration History Plugin lets attackers delete or restore config history entries without authentication.

Vulnerability

Overview

The Jenkins Job Configuration History Plugin, up to version 1155.v28a_46a_cc06a_5, is vulnerable to cross-site request forgery (CSRF). This flaw allows an attacker to craft malicious web requests that, when a Jenkins administrator visits a page controlled by the attacker, can delete entries from job, agent, and system configuration history, or restore older versions of those configurations [1][3]. The root cause is that the plugin's endpoints (doRestore, doDeleteRevision) lacked proper CSRF protection, missing the @POST annotation that enforces a valid Crumb token [4].

Exploitation

Method

To exploit this CSRF vulnerability, an attacker must trick a Jenkins user with the necessary permissions (configure or delete entry rights) into clicking a crafted link or visiting a specially designed webpage. The attack does not require network access to Jenkins beyond what is available to the target user. The attacker constructs a cross-site request that invokes the unprotected doRestore or doDeleteRevision methods, passing a timestamp parameter to target a specific history entry [1][4]. Because the plugin did not validate the origin or require a confirmation token for state-changing actions, the forged request is executed in the authenticated session.

Impact

A successful CSRF attack can lead to unauthorized deletion of configuration history entries. This could remove forensic evidence of configuration changes, disrupt rollback capabilities, or restore an older, potentially less secure configuration. The impact is limited to actions available to the victim's permissions; attackers cannot gain full administrative control but can corrupt the configuration history record [1][3].

Mitigation

The vulnerability is fixed in Job Configuration History Plugin version 1156.v536a_97b_8d649 and later [2]. The fix adds @POST annotation to the vulnerable methods, ensuring they only accept POST requests with a valid CSRF token. Users should upgrade to the latest version. No workarounds are provided beyond ensuring Jenkins is configured to require a crumb issuer for form submissions [1][4].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.jenkins-ci.plugins:jobConfigHistoryMaven
< 1156.v536a_97b_8d6491156.v536a_97b_8d649

Affected products

2

Patches

1
536a97b8d649

[SECURITY-2766] (#216)

https://github.com/jenkinsci/job-config-history-pluginAlexander BrandesJul 12, 2022via ghsa
4 files changed · +12 2
  • src/main/java/hudson/plugins/jobConfigHistory/ComputerConfigHistoryAction.java+3 0 modified
    @@ -35,6 +35,7 @@
     import org.kohsuke.stapler.StaplerResponse;
    
     import org.kohsuke.stapler.export.Exported;
    
     import org.kohsuke.stapler.export.ExportedBean;
    
    +import org.kohsuke.stapler.verb.POST;
    
     
    
     import java.io.IOException;
    
     import java.text.ParseException;
    
    @@ -382,6 +383,7 @@ private XmlFile getOldConfigXml(String timestamp) {
          * @param rsp Outgoing StaplerResponse
    
          * @throws IOException If something goes wrong
    
          */
    
    +    @POST
    
         public final void doRestore(StaplerRequest req, StaplerResponse rsp) throws IOException {
    
             checkConfigurePermission();
    
             final String timestamp = req.getParameter("timestamp");
    
    @@ -413,6 +415,7 @@ public final void doForwardToRestoreQuestion(StaplerRequest req, StaplerResponse
             rsp.sendRedirect("restoreQuestion?timestamp=" + timestamp);
    
         }
    
     
    
    +    @POST
    
         public final void doDeleteRevision(StaplerRequest req) {
    
             checkDeleteEntryPermission();
    
             final String timestamp = req.getParameter("timestamp");
    
    
  • src/main/java/hudson/plugins/jobConfigHistory/JobConfigHistoryProjectAction.java+3 0 modified
    @@ -36,6 +36,7 @@
     import org.kohsuke.stapler.StaplerResponse;
    
     import org.kohsuke.stapler.export.Exported;
    
     import org.kohsuke.stapler.export.ExportedBean;
    
    +import org.kohsuke.stapler.verb.POST;
    
     
    
     import javax.xml.transform.Source;
    
     import javax.xml.transform.stream.StreamSource;
    
    @@ -463,6 +464,7 @@ private XmlFile getOldConfigXml(String timestamp) {
          * @param rsp Outgoing StaplerResponse
    
          * @throws IOException If something goes wrong
    
          */
    
    +    @POST
    
         public final void doRestore(StaplerRequest req, StaplerResponse rsp)
    
                 throws IOException {
    
             checkConfigurePermission();
    
    @@ -492,6 +494,7 @@ public final void doForwardToRestoreQuestion(StaplerRequest req,
             rsp.sendRedirect("restoreQuestion?timestamp=" + timestamp);
    
         }
    
     
    
    +    @POST
    
         public final void doDeleteRevision(StaplerRequest req, StaplerResponse rsp) {
    
             checkDeleteEntryPermission();
    
             final String timestamp = req.getParameter("timestamp");
    
    
  • src/main/java/hudson/plugins/jobConfigHistory/JobConfigHistoryRootAction.java+3 0 modified
    @@ -39,6 +39,7 @@
     import org.kohsuke.stapler.StaplerResponse;
    
     import org.kohsuke.stapler.export.Exported;
    
     import org.kohsuke.stapler.export.ExportedBean;
    
    +import org.kohsuke.stapler.verb.POST;
    
     
    
     import javax.servlet.ServletException;
    
     import java.io.ByteArrayInputStream;
    
    @@ -662,6 +663,7 @@ public boolean checkParameters(String name, String timestamp) {
          * @param rsp Outgoing StaplerResponse
    
          * @throws IOException If something goes wrong
    
          */
    
    +    @POST
    
         public final void doRestore(StaplerRequest req, StaplerResponse rsp)
    
                 throws IOException {
    
             getAccessControlledObject().checkPermission(Item.CONFIGURE);
    
    @@ -743,6 +745,7 @@ public final void doForwardToRestoreQuestion(StaplerRequest req,
             rsp.sendRedirect("restoreQuestion?name=" + name);
    
         }
    
     
    
    +    @POST
    
         public final void doDeleteRevision(StaplerRequest req, StaplerResponse rsp) {
    
             checkDeleteEntryPermission();
    
             final String timestamp = req.getParameter("timestamp");
    
    
  • src/main/webapp/deleteRevisionAndTableEntry.js+3 2 modified
    @@ -11,9 +11,10 @@ function removeEntryFromTable(id, timestamp, name, message) {
             if (name != null) {
                 url += "&name=" + name;
             }
    -        xmlHttp.open("GET", url, true);
    +        xmlHttp.open("POST", url, true);
    +        xmlHttp.setRequestHeader(document.head.getAttribute('data-crumb-header'), document.head.getAttribute('data-crumb-value'));
             xmlHttp.send(null);
         } else {
             return false;
         }
    -}
    \ No newline at end of file
    +}
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.