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.
| Package | Affected versions | Patched versions |
|---|---|---|
com.sonyericsson.jenkins.plugins.bfa:build-failure-analyzerMaven | < 1.24.2 | 1.24.2 |
Affected products
2- Jenkins project/Jenkins Build Failure Analyzer Pluginv5Range: unspecified
Patches
1f316c885552a[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- github.com/advisories/GHSA-543w-gq76-pw7gghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-16553ghsaADVISORY
- www.openwall.com/lists/oss-security/2019/12/17/1ghsamailing-listx_refsource_MLISTWEB
- github.com/jenkinsci/build-failure-analyzer-plugin/commit/f316c885552ac75289cbb11b2af5757f18784bcbghsaWEB
- jenkins.io/security/advisory/2019-12-17/ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.