VYPR
Moderate severityNVD Advisory· Published Apr 10, 2019· Updated Aug 5, 2024

CVE-2019-1003050

CVE-2019-1003050

Description

Jenkins fails to escape job URLs in the f:validateButton form control, leading to a stored XSS vulnerability exploitable by users who can control job names.

AI Insight

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

Jenkins fails to escape job URLs in the f:validateButton form control, leading to a stored XSS vulnerability exploitable by users who can control job names.

Vulnerability

Details

CVE-2019-1003050 is a cross-site scripting (XSS) vulnerability in Jenkins core affecting versions 2.171 and earlier of the weekly release line and versions 2.164.1 and earlier of the LTS release line. The flaw resides in the f:validateButton form control, which is used to validate job configuration parameters. This control did not properly escape job URLs before rendering them in the Jenkins UI, allowing an attacker to inject arbitrary JavaScript into the page [2].

Exploitation

The vulnerability is exploitable by any Jenkins user who has the ability to control job names. This typically includes users with Job/Create or Job/Configure permissions. By creating or renaming a job with a crafted name containing malicious script content, the attacker can cause the script to execute in the context of any user who views the job configuration page, including administrators [1][2]. No authentication bypass or network-level attack is required; the attacker only needs a valid Jenkins account with sufficient privileges to create or modify jobs.

Impact

Successful exploitation leads to stored XSS, meaning the injected script becomes a permanent part of the affected page and executes for every subsequent visitor. Depending on the privileges of the victim, an attacker could perform administrative actions, exfiltrate session tokens, or deploy malicious plugins. The vulnerability is particularly severe in shared Jenkins environments where multiple users manage jobs, as it can be used to escalate privileges from a low-privileged job creator to a higher-privileged administrator [2][3].

Mitigation

Jenkins has released fixed versions: Jenkins 2.172 (weekly) and Jenkins LTS 2.164.2. Users are strongly advised to upgrade immediately. As part of the fix, the f:validateButton component properly escapes job URLs using htmlEncode to prevent script injection [3]. No workaround exists for unpatched versions. Red Hat's OpenShift Container Platform also included the fix in advisory RHBA-2019:1605 [1]. This CVE is not currently listed in CISA's Known Exploited Vulnerabilities catalog.

AI Insight generated on May 22, 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.164.22.164.2
org.jenkins-ci.main:jenkins-coreMaven
>= 2.165, < 2.1722.172

Affected products

2

Patches

2
d393c7e9ba3e

[SECURITY-1327] Adapt test to new HTML Unit

https://github.com/jenkinsci/jenkinsDaniel BeckMar 26, 2019via ghsa
1 file changed · +4 2
  • test/src/test/java/lib/form/ValidateButtonSEC1327Test.java+4 2 modified
    @@ -24,7 +24,9 @@
     package lib.form;
     
     import com.gargoylesoftware.htmlunit.html.HtmlButton;
    +import com.gargoylesoftware.htmlunit.html.HtmlElement;
     import com.gargoylesoftware.htmlunit.html.HtmlPage;
    +import com.gargoylesoftware.htmlunit.html.DomNodeList;
     import hudson.model.FreeStyleProject;
     import hudson.model.Job;
     import hudson.model.JobProperty;
    @@ -72,8 +74,8 @@ private void checkValidateButtonWork(String projectName) throws Exception {
             HtmlPage htmlPage = wc.goTo(p.getUrl() + "/configure");
             assertThat(htmlPage.getWebResponse().getStatusCode(), is(200));
     
    -        List<HtmlButton> inputs = htmlPage.getDocumentElement().getHtmlElementsByTagName("button");
    -        HtmlButton validateButton = inputs.stream()
    +         DomNodeList<HtmlElement> inputs = htmlPage.getDocumentElement().getElementsByTagName("button");
    +         HtmlButton validateButton = (HtmlButton) inputs.stream()
                     .filter(i -> i.getTextContent().contains("testInjection"))
                     .findFirst()
                     .orElseThrow(() -> new AssertionError("Validate button not found"));
    
8eb632dda219

[SECURITY-1327]

https://github.com/jenkinsci/jenkinsWadeck FollonierMar 26, 2019via ghsa
4 files changed · +160 1
  • core/src/main/resources/lib/form/validateButton.jelly+5 1 modified
    @@ -48,7 +48,11 @@ THE SOFTWARE.
       </st:documentation>
       <f:entry>
         <div style="float:right">
    -      <input type="button" value="${title}" class="yui-button validate-button" onclick="validateButton('${descriptor.descriptorFullUrl}/${h.jsStringEscape(method)}','${h.jsStringEscape(with)}',this)" />
    +      <input type="button" value="${title}" class="yui-button validate-button" 
    +             data-validate-button-descriptor-url="${descriptor.descriptorFullUrl}"
    +             data-validate-button-method="${method}"
    +             data-validate-button-with="${with}"
    +             onclick="safeValidateButton(this)" />
         </div>
         <div style="display:none;">
           <img src="${imagesURL}/spinner.gif" /> ${attrs.progress}
    
  • test/src/test/java/lib/form/ValidateButtonSEC1327Test.java+102 0 added
    @@ -0,0 +1,102 @@
    +/*
    + * The MIT License
    + *
    + * Copyright (c) 2004-2009, Sun Microsystems, Inc.
    + *
    + * Permission is hereby granted, free of charge, to any person obtaining a copy
    + * of this software and associated documentation files (the "Software"), to deal
    + * in the Software without restriction, including without limitation the rights
    + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    + * copies of the Software, and to permit persons to whom the Software is
    + * furnished to do so, subject to the following conditions:
    + *
    + * The above copyright notice and this permission notice shall be included in
    + * all copies or substantial portions of the Software.
    + *
    + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    + * THE SOFTWARE.
    + */
    +package lib.form;
    +
    +import com.gargoylesoftware.htmlunit.html.HtmlButton;
    +import com.gargoylesoftware.htmlunit.html.HtmlPage;
    +import hudson.model.FreeStyleProject;
    +import hudson.model.Job;
    +import hudson.model.JobProperty;
    +import hudson.model.JobPropertyDescriptor;
    +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.TestExtension;
    +import org.kohsuke.stapler.StaplerRequest;
    +
    +import java.util.List;
    +import java.util.concurrent.atomic.AtomicReference;
    +
    +import static org.hamcrest.CoreMatchers.is;
    +import static org.hamcrest.CoreMatchers.nullValue;
    +import static org.junit.Assert.assertThat;
    +
    +//TODO merge with ValidateButtonTest once security release done
    +public class ValidateButtonSEC1327Test {
    +    @Rule
    +    public JenkinsRule j = new JenkinsRule();
    +
    +    @Test
    +    public void regularUsageOfUsingDescriptorUrl() throws Exception {
    +        checkValidateButtonWork("okName");
    +    }
    +
    +    @Test
    +    @Issue("SECURITY-1327")
    +    public void xssUsingDescriptorUrl() throws Exception {
    +        checkValidateButtonWork("TESTawsCC','a',this)+alert(1)+validateButton('aaa");
    +    }
    +    
    +    private void checkValidateButtonWork(String projectName) throws Exception {
    +        FreeStyleProject p = j.createFreeStyleProject(projectName);
    +        JenkinsRule.WebClient wc = j.createWebClient();
    +        ValidateProperty.DescriptorImpl descriptor = j.jenkins.getDescriptorByType(ValidateProperty.DescriptorImpl.class);
    +
    +        AtomicReference<String> alertReceived = new AtomicReference<>();
    +        wc.setAlertHandler((page, s) -> alertReceived.set(s));
    +
    +        assertThat(alertReceived.get(), nullValue());
    +
    +        HtmlPage htmlPage = wc.goTo(p.getUrl() + "/configure");
    +        assertThat(htmlPage.getWebResponse().getStatusCode(), is(200));
    +
    +        List<HtmlButton> inputs = htmlPage.getDocumentElement().getHtmlElementsByTagName("button");
    +        HtmlButton validateButton = inputs.stream()
    +                .filter(i -> i.getTextContent().contains("testInjection"))
    +                .findFirst()
    +                .orElseThrow(() -> new AssertionError("Validate button not found"));
    +
    +        assertThat(alertReceived.get(), nullValue());
    +        assertThat(descriptor.called, is(false));
    +
    +        validateButton.click();
    +
    +        assertThat(alertReceived.get(), nullValue());
    +
    +        wc.waitForBackgroundJavaScript(5000);
    +        assertThat(descriptor.called, is(true));
    +    }
    +
    +    public static class ValidateProperty extends JobProperty<Job<?,?>> {
    +        @TestExtension({"regularUsageOfUsingDescriptorUrl", "xssUsingDescriptorUrl"})
    +        public static class DescriptorImpl extends JobPropertyDescriptor {
    +            public boolean called = false;
    +
    +            public void doSomething(StaplerRequest req) {
    +                called = true;
    +            }
    +        }
    +    }
    +}
    
  • test/src/test/resources/lib/form/ValidateButtonSEC1327Test/ValidateProperty/config.jelly+29 0 added
    @@ -0,0 +1,29 @@
    +<!--
    +The MIT License
    +
    +Copyright (c) 2019, CloudBees, Inc.
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in
    +all copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    +THE SOFTWARE.
    +-->
    +<?jelly escape-by-default='true'?>
    +<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:f="/lib/form">
    +    <div id="test-panel">
    +        <f:validateButton title="testInjection" method="something" />
    +    </div>
    +</j:jelly>
    
  • war/src/main/webapp/scripts/hudson-behavior.js+24 0 modified
    @@ -529,6 +529,16 @@ function makeButton(e,onclick) {
         Element.addClassName(be,clsName);
         if(n) // copy the name
             be.setAttribute("name",n);
    +
    +    // keep the data-* attributes from the source
    +    var length = e.attributes.length;
    +    for (var i = 0; i < length; i++) {
    +        var attribute = e.attributes[i];
    +        var attributeName = attribute.name;
    +        if (attributeName.startsWith('data-')) {
    +            btn._button.setAttribute(attributeName, attribute.value);
    +        }
    +    }
         return btn;
     }
     
    @@ -2876,6 +2886,20 @@ function applySafeRedirector(url) {
     }
     
     // logic behind <f:validateButton />
    +function safeValidateButton(yuiButton) {
    +    var button = yuiButton._button;
    +    var descriptorUrl = button.getAttribute('data-validate-button-descriptor-url');
    +    var method = button.getAttribute('data-validate-button-method');
    +    var checkUrl = descriptorUrl + "/" + method;
    +
    +    // optional, by default = empty string
    +    var paramList = button.getAttribute('data-validate-button-with') || '';
    +    
    +    validateButton(checkUrl, paramList, yuiButton);
    +}
    +
    +// this method should not be called directly, only get called by safeValidateButton
    +// kept "public" for legacy compatibility
     function validateButton(checkUrl,paramList,button) {
       button = button._button;
     
    

Vulnerability mechanics

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

References

8

News mentions

0

No linked articles in our index yet.