VYPR
Moderate severityNVD Advisory· Published Feb 6, 2019· Updated Aug 5, 2024

CVE-2019-1003014

CVE-2019-1003014

Description

An cross-site scripting vulnerability exists in Jenkins Config File Provider Plugin 3.4.1 and earlier in src/main/resources/lib/configfiles/configfiles.jelly that allows attackers with permission to define shared configuration files to execute arbitrary JavaScript when a user attempts to delete the shared configuration file.

AI Insight

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

Jenkins Config File Provider Plugin <=3.4.1 has a stored XSS in the delete action for shared config files, allowing attackers with define permission to execute JS.

Vulnerability

A cross-site scripting (XSS) vulnerability exists in the Jenkins Config File Provider Plugin in versions 3.4.1 and earlier. The flaw resides in the Jelly view file src/main/resources/lib/configfiles/configfiles.jelly, which is rendered when a user attempts to delete a shared configuration file. Attackers with the permission to define shared configuration files can inject arbitrary JavaScript code into the config file, which will be executed in the context of a victim's browser when they delete the file [2][3].

Exploitation

An attacker who has the "Define" permission for shared configuration files can create a malicious config file containing embedded JavaScript. When a user with the "Delete" permission (or any user who can delete the file) clicks the delete action, the injected script executes in the victim's browser. No user interaction is required beyond the delete action itself [2][3].

Impact

Successful exploitation allows arbitrary JavaScript execution in the victim's browser. This can lead to unauthorized actions performed on behalf of the user, such as modifying Jenkins configuration, creating or deleting jobs, or exfiltrating sensitive information. The privilege level required for exploitation is at least the ability to define shared config files, and the impact is limited to the permissions of the victim user who performs the delete action [2][3].

Mitigation

The vulnerability is fixed in Config File Provider Plugin version 3.4.2 and later. Users should upgrade to this version or newer as soon as possible. For OpenShift deployments, the fix is included in the updated Jenkins images provided in RHBA-2019:0326 and RHBA-2019:0327 [1][4]. No known workarounds exist, and the plugin is not listed in CISA's Known Exploited Vulnerabilities catalog [2][3].

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.plugins:config-file-providerMaven
< 3.53.5

Affected products

2

Patches

1
64fba993c897

[SECURITY-1253]

3 files changed · +125 13
  • src/main/resources/lib/configfiles/configfiles.jelly+9 10 modified
    @@ -49,7 +49,7 @@ THE SOFTWARE.
                                          src="${imagesURL}/16x16/document_edit.gif"/>
                                 </a>
                                 <j:out value=" "/>
    -                            <a href="removeConfig?id=${t.id}" onclick="return cfp_confirmDelete('${t.name}')">
    +                            <a href="removeConfig?id=${t.id}" onclick="return cfp_confirmDelete(this)" data-confirm="${t.name}">
                                     <img width="16" height="16" title="${%remove script} ${t.name}"
                                          src="${imagesURL}/16x16/edit-delete.gif"/>
                                 </a>
    @@ -71,16 +71,15 @@ THE SOFTWARE.
                     </j:forEach>
                 </table>
             </j:forEach>
    -
         </div>
         <script>
    -        function cfp_confirmDelete(name) {
    -        if (confirm("Sure you want to delete ["+name+"]?")) {
    -        return true;
    -        }else{
    -        return false;
    -        }
    +        function cfp_confirmDelete(anchor) {
    +            var confirmMessage = anchor.getAttribute("data-confirm");
    +            if (confirm("Sure you want to delete ["+confirmMessage+"]?")) {
    +                return true;
    +            }else{
    +                return false;
    +            }
             }
         </script>
    -
    -</j:jelly>
    \ No newline at end of file
    +</j:jelly>
    
  • src/main/resources/lib/configfiles/providerlist.jelly+4 3 modified
    @@ -47,7 +47,7 @@ THE SOFTWARE.
                                          src="${imagesURL}/16x16/document_edit.gif" />
                                 </a>
                                 <j:out value=" " />
    -                            <a href="removeConfig?id=${t.id}" onclick="return cfp_confirmDelete('${t.name}')">
    +                            <a href="removeConfig?id=${t.id}" onclick="return cfp_confirmDelete(this)" data-confirm="${t.name}">
                                     <img width="16" height="16" title="${%remove script} ${t.name}"
                                          src="${imagesURL}/16x16/edit-delete.gif" />
                                 </a>
    @@ -66,8 +66,9 @@ THE SOFTWARE.
         </div>
     
         <script>
    -        function cfp_confirmDelete(name) {
    -            if (confirm("Sure you want to delete ["+name+"]?")) {
    +        function cfp_confirmDelete(anchor) {
    +            var confirmMessage = anchor.getAttribute("data-confirm");
    +            if (confirm("Sure you want to delete ["+confirmMessage+"]?")) {
                     return true;
                 }else{
                     return false;
    
  • src/test/java/org/jenkinsci/plugins/configfiles/ConfigFilesSEC1253Test.java+112 0 added
    @@ -0,0 +1,112 @@
    +package org.jenkinsci.plugins.configfiles;
    +
    +import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
    +import com.gargoylesoftware.htmlunit.html.HtmlForm;
    +import com.gargoylesoftware.htmlunit.html.HtmlInput;
    +import com.gargoylesoftware.htmlunit.html.HtmlPage;
    +import com.gargoylesoftware.htmlunit.html.HtmlRadioButtonInput;
    +import org.jenkinsci.plugins.configfiles.custom.CustomConfig;
    +import org.junit.Rule;
    +import org.junit.Test;
    +import org.jvnet.hudson.test.Issue;
    +import org.jvnet.hudson.test.JenkinsRule;
    +
    +import java.util.concurrent.atomic.AtomicReference;
    +
    +import static org.hamcrest.CoreMatchers.is;
    +import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
    +import static org.hamcrest.collection.IsEmptyCollection.empty;
    +import static org.junit.Assert.assertNotNull;
    +import static org.junit.Assert.assertThat;
    +
    +public class ConfigFilesSEC1253Test {
    +
    +    private static final String CONFIG_ID = "ConfigFilesTestId";
    +
    +    @Rule
    +    public JenkinsRule j = new JenkinsRule();
    +
    +    @Test
    +    @Issue("SECURITY-1253")
    +    public void regularCaseStillWorking() throws Exception {
    +        GlobalConfigFiles store = j.getInstance().getExtensionList(GlobalConfigFiles.class).get(GlobalConfigFiles.class);
    +        assertNotNull(store);
    +        assertThat(store.getConfigs(), empty());
    +
    +        JenkinsRule.WebClient wc = j.createWebClient();
    +        HtmlPage createConfig = wc.goTo("configfiles/selectProvider");
    +        HtmlRadioButtonInput groovyRadioButton = createConfig.getDocumentElement().getOneHtmlElementByAttribute("input", "value", "org.jenkinsci.plugins.configfiles.groovy.GroovyScript");
    +        groovyRadioButton.click();
    +        HtmlForm addConfigForm = createConfig.getFormByName("addConfig");
    +
    +        HtmlPage createGroovyConfig = j.submit(addConfigForm);
    +        HtmlInput configIdInput = createGroovyConfig.getElementByName("config.id");
    +        configIdInput.setValueAttribute(CONFIG_ID);
    +
    +        HtmlInput configNameInput = createGroovyConfig.getElementByName("config.name");
    +        configNameInput.setValueAttribute("Regular name");
    +
    +        HtmlForm saveConfigForm = createGroovyConfig.getForms().stream()
    +                .filter(htmlForm -> htmlForm.getActionAttribute().equals("saveConfig"))
    +                .findFirst()
    +                .orElseThrow(() -> new RuntimeException("No config with action [saveConfig] in that page"));
    +        j.submit(saveConfigForm);
    +
    +        assertThat(store.getConfigs(), hasSize(1));
    +
    +        HtmlPage configFiles = wc.goTo("configfiles");
    +        HtmlAnchor removeAnchor = configFiles.getDocumentElement().getOneHtmlElementByAttribute("a", "href", "removeConfig?id=" + CONFIG_ID);
    +
    +        AtomicReference<Boolean> confirmCalled = new AtomicReference<>(false);
    +        wc.setConfirmHandler((page, s) -> {
    +            confirmCalled.set(true);
    +            return true;
    +        });
    +
    +        assertThat(confirmCalled.get(), is(false));
    +
    +        removeAnchor.click();
    +
    +        assertThat(confirmCalled.get(), is(true));
    +
    +        assertThat(store.getConfigs(), empty());
    +    }
    +
    +    @Test
    +    @Issue("SECURITY-1253")
    +    public void xssPrevention() throws Exception {
    +        GlobalConfigFiles store = j.getInstance().getExtensionList(GlobalConfigFiles.class).get(GlobalConfigFiles.class);
    +        assertNotNull(store);
    +        assertThat(store.getConfigs(), empty());
    +
    +        CustomConfig config = new CustomConfig(CONFIG_ID, "GroovyConfig')+alert('asw", "comment", "content");
    +        store.save(config);
    +
    +        assertThat(store.getConfigs(), hasSize(1));
    +
    +        JenkinsRule.WebClient wc = j.createWebClient();
    +
    +        HtmlPage configFiles = wc.goTo("configfiles");
    +        HtmlAnchor removeAnchor = configFiles.getDocumentElement().getOneHtmlElementByAttribute("a", "href", "removeConfig?id=" + CONFIG_ID);
    +
    +        AtomicReference<Boolean> confirmCalled = new AtomicReference<>(false);
    +        AtomicReference<Boolean> alertCalled = new AtomicReference<>(false);
    +        wc.setConfirmHandler((page, s) -> {
    +            confirmCalled.set(true);
    +            return true;
    +        });
    +        wc.setAlertHandler((page, s) -> {
    +            alertCalled.set(true);
    +        });
    +
    +        assertThat(confirmCalled.get(), is(false));
    +        assertThat(alertCalled.get(), is(false));
    +
    +        removeAnchor.click();
    +
    +        assertThat(confirmCalled.get(), is(true));
    +        assertThat(alertCalled.get(), is(false));
    +
    +        assertThat(store.getConfigs(), empty());
    +    }
    +}
    

Vulnerability mechanics

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

References

6

News mentions

0

No linked articles in our index yet.