VYPR
High severityNVD Advisory· Published Dec 17, 2019· Updated Aug 5, 2024

CVE-2019-16553

CVE-2019-16553

Description

A cross-site request forgery vulnerability in Jenkins Build Failure Analyzer Plugin 1.24.1 and earlier allows attackers to have Jenkins evaluate a computationally expensive regular expression.

AI Insight

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

CSRF in Jenkins Build Failure Analyzer Plugin allows attackers to cause server-side evaluation of costly regexes.

Overview

CVE-2019-16553 is a cross-site request forgery (CSRF) vulnerability in the Jenkins Build Failure Analyzer Plugin, versions 1.24.1 and earlier. The plugin fails to require a POST request or validate a specific HTTP method for the doMatchText form validation endpoint, allowing an attacker to trick a Jenkins administrator into unknowingly triggering a computationally expensive regular expression evaluation [1][2].

Exploitation

An attacker does not need direct authentication to the Jenkins instance; instead, they can craft a malicious web page or link that, when visited by an authenticated Jenkins user (with sufficient permissions), sends a forged GET (or other non-POST) request to the vulnerable endpoint. This request causes the plugin to evaluate a user-supplied regular expression server-side, which can be designed to consume significant CPU resources [1][3].

Impact

Successful exploitation results in a denial-of-service (DoS) condition on the Jenkins controller, as the expensive regex evaluation can monopolize CPU time and degrade or deny service to legitimate users. The attack does not allow data exfiltration or privilege escalation, but it can severely impact availability [2][4].

Mitigation

The Jenkins project has released Build Failure Analyzer Plugin version 1.24.2, which now requires doMatchText requests to be sent via POST and includes a CSRF token check. Users should update immediately. No workaround is available beyond restricting access to the Jenkins UI or applying network-level filters [1][2].

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
com.sonyericsson.jenkins.plugins.bfa:build-failure-analyzerMaven
< 1.24.21.24.2

Affected products

2

Patches

1
f316c885552a

[SECURITY-1651] Protect BuildLogIndication doMatchText

2 files changed · +109 1
  • src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/BuildLogIndication.java+7 1 modified
    @@ -30,6 +30,7 @@
     import com.fasterxml.jackson.annotation.JsonProperty;
     import com.fasterxml.jackson.annotation.JsonTypeInfo;
     import com.sonyericsson.jenkins.plugins.bfa.Messages;
    +import com.sonyericsson.jenkins.plugins.bfa.PluginImpl;
     import com.sonyericsson.jenkins.plugins.bfa.model.BuildLogFailureReader;
     import com.sonyericsson.jenkins.plugins.bfa.model.FailureReader;
     import hudson.Extension;
    @@ -44,6 +45,7 @@
     import jenkins.model.Jenkins;
     import org.kohsuke.stapler.DataBoundConstructor;
     import org.kohsuke.stapler.QueryParameter;
    +import org.kohsuke.stapler.interceptor.RequirePOST;
     
     import java.io.IOException;
     import java.util.regex.Matcher;
    @@ -248,10 +250,12 @@ public String getDisplayName() {
              *         the string does not match the pattern,
              *         {@link FormValidation#error(java.lang.String) } otherwise.
              */
    +        @RequirePOST
             public FormValidation doMatchText(
                     @QueryParameter("pattern") final String testPattern,
                     @QueryParameter("testText") String testText,
                     @QueryParameter("textSourceIsUrl") final boolean textSourceIsUrl) {
    +            Jenkins.get().checkPermission(PluginImpl.UPDATE_PERMISSION);
                 if (textSourceIsUrl) {
                     testText = testText.replaceAll("/\\./", "/").replaceAll("/view/change-requests", "");
                     Matcher urlMatcher = URL_PATTERN.matcher(testText);
    @@ -332,7 +336,9 @@ && isValidBuildId(urlParts[2])) {
                     return FormValidation.error(Messages.InvalidURL_Error());
                 } else {
                     try {
    -                    if (testText.matches(testPattern)) {
    +                    final Pattern pattern = Pattern.compile(testPattern);
    +                    final Matcher matcher = pattern.matcher(new FailureReader.InterruptibleCharSequence(testText));
    +                    if (matcher.matches()) {
                             return FormValidation.ok(Messages.StringMatchesPattern());
                         }
                         return FormValidation.warning(Messages.StringDoesNotMatchPattern());
    
  • src/test/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/BuildLogIndicationTest.java+102 0 modified
    @@ -24,7 +24,13 @@
      */
     package com.sonyericsson.jenkins.plugins.bfa.model.indication;
     
    +import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
    +import com.gargoylesoftware.htmlunit.HttpMethod;
    +import com.gargoylesoftware.htmlunit.Page;
    +import com.gargoylesoftware.htmlunit.WebRequest;
    +import com.gargoylesoftware.htmlunit.util.UrlUtils;
     import com.sonyericsson.jenkins.plugins.bfa.Messages;
    +import com.sonyericsson.jenkins.plugins.bfa.PluginImpl;
     import com.sonyericsson.jenkins.plugins.bfa.model.BuildLogFailureReader;
     import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause;
     import com.sonyericsson.jenkins.plugins.bfa.model.FailureReader;
    @@ -39,21 +45,30 @@
     import hudson.model.Cause;
     import hudson.model.FreeStyleBuild;
     import hudson.model.FreeStyleProject;
    +import hudson.model.Item;
     import hudson.model.Result;
    +import hudson.security.SecurityRealm;
     import hudson.util.FormValidation;
    +import jenkins.model.Jenkins;
     import org.junit.Rule;
     import org.junit.Test;
    +import org.jvnet.hudson.test.Issue;
    +import org.jvnet.hudson.test.JenkinsRule;
    +import org.jvnet.hudson.test.MockAuthorizationStrategy;
     import org.jvnet.hudson.test.MockBuilder;
     
     import java.io.BufferedReader;
    +import java.io.IOException;
     import java.util.ArrayList;
     import java.util.List;
     import java.util.concurrent.Future;
     import java.util.concurrent.TimeUnit;
     
    +import static org.hamcrest.Matchers.is;
     import static org.junit.Assert.assertEquals;
     import static org.junit.Assert.assertNotNull;
     import static org.junit.Assert.assertNull;
    +import static org.junit.Assert.assertThat;
     
     /**
      * Tests for the BuildLogIndication.
    @@ -276,4 +291,91 @@ public void testDoMatchTextUrlInvalid() {
             assertEquals(Messages.InvalidURL_Error(), formValidation.getMessage());
             assertEquals(FormValidation.Kind.ERROR, formValidation.kind);
         }
    +
    +    // CS IGNORE MagicNumber FOR NEXT 200 LINES. REASON: test data.
    +
    +    @Test @Issue("SECURITY-1651")
    +    public void testDoMatchNotHttpGetAccessible() throws Exception {
    +        lockDown();
    +        final JenkinsRule.WebClient webClient = j.createWebClient();
    +        webClient.assertFails("descriptorByName/"
    +                + BuildLogIndication.class.getName()
    +                + "/matchText?"
    +                + "pattern=[a-z]+&"
    +                + "testText=hello&"
    +                + "textSourceIsUrl=false",
    +                405);
    +    }
    +
    +    @Test @Issue("SECURITY-1651")
    +    public void testDoMatchHttpPostAccessible() throws Exception {
    +        lockDown();
    +        final JenkinsRule.WebClient webClient = j.createWebClient();
    +        post(webClient, "descriptorByName/"
    +                        + BuildLogIndication.class.getName()
    +                        + "/matchText?"
    +                        + "pattern=[a-z]+&"
    +                        + "testText=hello&"
    +                        + "textSourceIsUrl=false",
    +                null, 403);
    +    }
    +
    +    @Test @Issue("SECURITY-1651")
    +    public void testDoMatchHttpPostAccessibleWithPermission() throws Exception {
    +        lockDown();
    +        final JenkinsRule.WebClient webClient = j.createWebClient().login("bob");
    +        post(webClient, "descriptorByName/"
    +                        + BuildLogIndication.class.getName()
    +                        + "/matchText?"
    +                        + "pattern=[a-z]+&"
    +                        + "testText=hello&"
    +                        + "textSourceIsUrl=false",
    +                null, null);
    +    }
    +
    +    /**
    +     * Performs an HTTP POST request to the relative url.
    +     *
    +     * @param webClient the client
    +     * @param relative the url relative to the context path
    +     * @param expectedContentType if expecting specific content type or null if not
    +     * @param expectedStatus if expecting a failing http status code or null if not
    +     * @throws IOException if so
    +     */
    +    private static void post(JenkinsRule.WebClient webClient, String relative,
    +                             String expectedContentType, Integer expectedStatus) throws IOException {
    +        WebRequest request = new WebRequest(
    +                UrlUtils.toUrlUnsafe(webClient.getContextPath() + relative),
    +                HttpMethod.POST);
    +        try {
    +            Page p = webClient.getPage(request);
    +            if (expectedContentType != null) {
    +                assertThat(p.getWebResponse().getContentType(), is(expectedContentType));
    +            }
    +        } catch (FailingHttpStatusCodeException e) {
    +            if (expectedStatus != null) {
    +                assertEquals(expectedStatus.intValue(), e.getStatusCode());
    +            } else {
    +                throw e;
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Lock down the instance.
    +     */
    +    private void lockDown() {
    +        SecurityRealm securityRealm = j.createDummySecurityRealm();
    +        j.getInstance().setSecurityRealm(securityRealm);
    +        j.getInstance().setAuthorizationStrategy(
    +                new MockAuthorizationStrategy().grant(Jenkins.READ).everywhere().toAuthenticated());
    +        j.jenkins.setCrumbIssuer(null); //Not really testing csrf right now
    +        j.getInstance().setAuthorizationStrategy(
    +                new MockAuthorizationStrategy().grant(Item.READ, Item.DISCOVER).everywhere().toAuthenticated()
    +                        .grant(PluginImpl.VIEW_PERMISSION).everywhere().toAuthenticated()
    +                        .grant(Jenkins.READ, Item.DISCOVER).everywhere().toEveryone()
    +                        .grant(Item.CONFIGURE).everywhere().to("bob")
    +                        .grant(PluginImpl.UPDATE_PERMISSION).everywhere().to("bob")
    +                        .grant(Jenkins.ADMINISTER).everywhere().to("alice"));
    +    }
     }
    

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.