CVE-2019-16554
Description
A missing permission check in Jenkins Build Failure Analyzer Plugin 1.24.1 and earlier allows attackers with Overall/Read permission to have Jenkins evaluate a computationally expensive regular expression.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Missing permission check in Build Failure Analyzer Plugin lets attackers with Overall/Read trigger costly regex evaluation.
The Jenkins Build Failure Analyzer Plugin up to version 1.24.1 does not perform a required permission check for the doMatchText endpoint. This missing permission check allows any user with the Overall/Read permission—a standard low-level permission—to invoke the endpoint without proper authorization. The plugin's intended design should restrict such actions to users with higher privileges, but no such check was implemented.
An attacker can exploit this by sending a crafted request to the doMatchText form validation method, which evaluates a user-supplied regular expression against build log text. Since the endpoint lacks an authentication gate, an attacker with only read-level access can submit an arbitrary regex. If the regex is computationally expensive (e.g., a catastrophic backtracking pattern), the evaluation can consume excessive CPU resources on the Jenkins controller. No additional privileges or network position are required beyond the ability to send an HTTP request to the Jenkins instance.
The impact is a denial-of-service condition: repeated or complex regex submissions can degrade Jenkins performance or cause prolonged unresponsiveness. The vulnerability does not expose confidential data or allow code execution, but it can disrupt Jenkins operations. The CVSS v3.1 base score is 6.5 (Medium), vector AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H, reflecting the availability impact and the network-based low-complexity attack with low privileges.
The issue was fixed in Build Failure Analyzer Plugin version 1.24.2, released on 2019-12-17 as part of the Jenkins security advisory [1][2][3]. The fix adds a permission check to ensure that only users with Item/Read permission on the relevant job can access the doMatchText endpoint. Administrators should upgrade to version 1.24.2 or later to mitigate this vulnerability.
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-x9gc-p2qp-4p7vghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-16554ghsaADVISORY
- 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.