VYPR
Moderate severityNVD Advisory· Published Mar 5, 2025· Updated Mar 6, 2025

CVE-2025-27624

CVE-2025-27624

Description

A cross-site request forgery (CSRF) vulnerability in Jenkins 2.499 and earlier, LTS 2.492.1 and earlier allows attackers to have users toggle their collapsed/expanded status of sidepanel widgets (e.g., Build Queue and Build Executor Status widgets).

AI Insight

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

A CSRF vulnerability in Jenkins 2.499 and earlier, LTS 2.492.1 and earlier allows an attacker to toggle sidepanel widget states and inject attacker-controlled content into a user's profile.

Vulnerability

Overview

A cross-site request forgery (CSRF) vulnerability exists in Jenkins versions 2.499 and earlier, as well as LTS 2.492.1 and earlier. The HTTP endpoint responsible for toggling the collapsed/expanded status of sidepanel widgets (such as the Build Queue and Build Executor Status widgets) did not require a POST request. This allowed an attacker to craft a malicious link or page that, when visited by an authenticated Jenkins user, could trigger a state change on the user's sidepanel without their consent [1][3].

Exploitation

Conditions

To exploit this vulnerability, an attacker must trick a logged-in Jenkins user into clicking a crafted link or visiting a malicious webpage. No special network access or authentication is required beyond the victim's existing session. The vulnerable endpoint accepts any arbitrary string as the panel ID, meaning an attacker can supply a crafted paneId parameter. This allows the attacker-controlled value to be stored in the victim's Jenkins user profile, potentially persisting beyond the immediate action [2][3].

Impact

Successful exploitation enables an attacker to manipulate the UI state by collapsing or expanding sidepanel widgets. More significantly, because the endpoint accepts arbitrary panel identifiers, the attacker can inject arbitrary strings into the victim's user profile. This stored content could be used in further attacks, such as injecting script content that might be rendered in other parts of the Jenkins interface (if not properly sanitized). The advisory rates this vulnerability as Medium severity due to the UI manipulation and stored profile injection [1][3].

Mitigation

Jenkins 2.500 and LTS 2.492.2 fix this vulnerability by requiring POST requests for the affected endpoint. The fix also updates the UI links to use a POST-compatible class (collapse post) to ensure CSRF protection is enforced [2][3]. Users are strongly advised to upgrade to these patched versions. No workarounds are mentioned in the advisory for unpatched instances.

AI Insight generated on May 20, 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.main:jenkins-coreMaven
>= 2.493, < 2.5002.500
org.jenkins-ci.main:jenkins-coreMaven
< 2.492.22.492.2

Affected products

5

Patches

1
84ef1a4d4db1

[SECURITY-3498]

https://github.com/jenkinsci/jenkinsDaniel BeckFeb 25, 2025via ghsa
7 files changed · +22 6
  • core/src/main/java/jenkins/model/Jenkins.java+1 0 modified
    @@ -4165,6 +4165,7 @@ public synchronized HttpRedirect doCancelQuietDown() {
             return new HttpRedirect(".");
         }
     
    +    @POST
         public HttpResponse doToggleCollapse() throws ServletException, IOException {
             final StaplerRequest2 request = Stapler.getCurrentRequest2();
             final String paneId = request.getParameter("paneId");
    
  • core/src/main/resources/lib/hudson/executors.jelly+3 1 modified
    @@ -174,7 +174,9 @@ THE SOFTWARE.
               ${executorDetails}
             </span>
           </j:if>
    -      <a class="collapse" href="${rootURL}/toggleCollapse?paneId=executors"
    +      <st:adjunct includes="lib.form.link.link"/>
    +      <!-- TODO improve l:link so the `a` can be changed to `l:link`. -->
    +      <a class="collapse post" href="${rootURL}/toggleCollapse?paneId=executors"
              tooltip="${paneIsCollapsed ? '%Expand' : '%Collapse'}" data-tooltip-append-to-parent="true">
             <j:set var="svgIconId" value="${paneIsCollapsed ? 'chevron-up' : 'chevron-down'}" />
             <l:icon src="symbol-${svgIconId}" />
    
  • core/src/main/resources/lib/layout/pane.jelly+3 1 modified
    @@ -59,7 +59,9 @@ THE SOFTWARE.
           </span>
     
           <j:if test="${attrs.id != null}">
    -        <a class="collapse" href="${rootURL}/toggleCollapse?paneId=${attrs.id}"
    +        <st:adjunct includes="lib.form.link.link"/>
    +        <!-- TODO improve l:link so the `a` can be changed to `l:link`. -->
    +        <a class="collapse post" href="${rootURL}/toggleCollapse?paneId=${attrs.id}"
                title="${paneIsCollapsed ? '%expand' : '%collapse'}">
     
               <j:set var="svgIconId" value="${paneIsCollapsed ? 'chevron-up' : 'chevron-down'}" />
    
  • test/src/test/java/hudson/model/ComputerSetTest.java+1 1 modified
    @@ -173,7 +173,7 @@ public void testTerminatedNodeAjaxExecutorsDoesNotShowTrace() throws Exception {
                     new OfflineCause.ChannelTermination(new RuntimeException(message))
             );
     
    -        WebClient wc = j.createWebClient();
    +        WebClient wc = j.createWebClient().withJavaScriptEnabled(false);
             Page page = wc.getPage(wc.createCrumbedUrl(HasWidgetHelper.getWidget(j.jenkins.getComputer(), ExecutorsWidget.class).orElseThrow().getUrl() + "ajax"));
             String content = page.getWebResponse().getContentAsString();
             assertThat(content, not(containsString(message)));
    
  • test/src/test/java/hudson/model/ComputerTest.java+1 1 modified
    @@ -285,7 +285,7 @@ public void testTerminatedNodeAjaxExecutorsDoesNotShowTrace() throws Exception {
                     new OfflineCause.ChannelTermination(new RuntimeException(message))
             );
     
    -        WebClient wc = j.createWebClient();
    +        WebClient wc = j.createWebClient().withJavaScriptEnabled(false);
             Page page = wc.getPage(wc.createCrumbedUrl(HasWidgetHelper.getWidget(agent.toComputer(), ExecutorsWidget.class).orElseThrow().getUrl() + "ajax"));
             String content = page.getWebResponse().getContentAsString();
             assertThat(content, not(containsString(message)));
    
  • test/src/test/java/jenkins/model/JenkinsTest.java+11 0 modified
    @@ -72,6 +72,7 @@
     import hudson.util.FormValidation;
     import hudson.util.HttpResponses;
     import hudson.util.VersionNumber;
    +import jakarta.servlet.http.HttpServletResponse;
     import java.io.File;
     import java.io.IOException;
     import java.net.HttpURLConnection;
    @@ -130,6 +131,16 @@ public class JenkinsTest {
         @Rule
         public TemporaryFolder tmp = new TemporaryFolder();
     
    +    @Test
    +    @Issue("SECURITY-3498")
    +    public void testPaneToggleCollapse() throws Exception {
    +        try (WebClient wc = j.createWebClient()) {
    +            final FailingHttpStatusCodeException ex = assertThrows(FailingHttpStatusCodeException.class, () -> wc.goTo("toggleCollapse?paneId=foo"));
    +            // @POST responds 404 when the verb is wrong; @RequirePOST would respond 405.
    +            assertThat(ex.getStatusCode(), is(HttpServletResponse.SC_NOT_FOUND));
    +        }
    +    }
    +
         @Test
         @Issue("SECURITY-3073")
         public void verifyUploadedFingerprintFilePermission() throws Exception {
    
  • test/src/test/java/lib/layout/AjaxTest.java+2 2 modified
    @@ -61,7 +61,7 @@ public class AjaxTest {
         @Test
         @Issue("JENKINS-65288")
         public void ajaxPageRenderingPossibleWithoutJellyTrace() throws Exception {
    -        JenkinsRule.WebClient wc = r.createWebClient();
    +        JenkinsRule.WebClient wc = r.createWebClient().withJavaScriptEnabled(false);
             HtmlPage htmlPage = wc.goTo(getExecutorsWidgetAjaxViewUrl());
             r.assertGoodStatus(htmlPage);
         }
    @@ -76,7 +76,7 @@ public void ajaxPageRenderingPossibleWithJellyTrace() throws Exception {
             try {
                 JellyFacet.TRACE = true;
     
    -            JenkinsRule.WebClient wc = r.createWebClient();
    +            JenkinsRule.WebClient wc = r.createWebClient().withJavaScriptEnabled(false);
                 HtmlPage htmlPage = wc.goTo(getExecutorsWidgetAjaxViewUrl());
                 r.assertGoodStatus(htmlPage);
             } finally {
    

Vulnerability mechanics

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

References

4

News mentions

1