CVE-2019-10438
Description
Missing permission check in Jenkins CRX Content Package Deployer Plugin allows attackers with Overall/Read to capture stored credentials via attacker-controlled URL and credential IDs.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Missing permission check in Jenkins CRX Content Package Deployer Plugin allows attackers with Overall/Read to capture stored credentials via attacker-controlled URL and credential IDs.
Vulnerability
Overview
CVE-2019-10438 is a missing permission check in the Jenkins CRX Content Package Deployer Plugin versions 1.8.1 and earlier. The plugin's form validation method did not verify that the user had the necessary permissions, allowing any user with Overall/Read access to trigger a connection to an attacker-specified URL using attacker-specified credential IDs [1][2].
Exploitation
An attacker must have Overall/Read permission on the Jenkins instance and obtain valid credential IDs through another method (e.g., a separate vulnerability). The form validation method also lacked a POST requirement, making it vulnerable to CSRF attacks [1]. This combination enables the attacker to craft a request that causes the plugin to connect to an attacker-controlled server using the specified credentials.
Impact
Successful exploitation allows the attacker to capture credentials stored in Jenkins by having the plugin authenticate to an attacker-controlled server with those credentials. This can lead to credential theft and further compromise of the Jenkins environment [1][2].
Mitigation
The vulnerability is fixed in CRX Content Package Deployer Plugin version 1.9. The fix adds proper permission checks and enforces POST requests on the affected method [1][3]. Users should upgrade to version 1.9 or later immediately.
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.
| Package | Affected versions | Patched versions |
|---|---|---|
org.jenkins-ci.plugins:crx-content-package-deployerMaven | < 1.9 | 1.9 |
Affected products
2- Range: 1.8.1 and earlier
Patches
11313c422170aadd @RequirePOST annotation and checkPermission configure
12 files changed · +100 −72
src/main/java/org/jenkinsci/plugins/graniteclient/BuildPackageBuilder.java+12 −7 modified@@ -27,10 +27,6 @@ package org.jenkinsci.plugins.graniteclient; -import java.io.IOException; -import javax.annotation.Nonnull; -import javax.servlet.ServletException; - import com.cloudbees.plugins.credentials.common.AbstractIdCredentialsListBoxModel; import hudson.Extension; import hudson.FilePath; @@ -53,6 +49,11 @@ import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.interceptor.RequirePOST; + +import javax.annotation.Nonnull; +import javax.servlet.ServletException; +import java.io.IOException; /** * Implementation of the "Build a Content Package on CRX" build step @@ -89,7 +90,7 @@ public BuildPackageBuilder(String packageId, String baseUrl, String credentialsI } public void perform(@Nonnull Run<?, ?> build, @Nonnull FilePath workspace, @Nonnull Launcher launcher, - @Nonnull TaskListener listener) throws InterruptedException, IOException { + @Nonnull TaskListener listener) throws InterruptedException, IOException { Result result = Result.SUCCESS; Result buildResult = build.getResult(); @@ -269,18 +270,22 @@ public boolean isApplicable(Class<? extends AbstractProject> aClass) { return true; } + @RequirePOST public AbstractIdCredentialsListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, @QueryParameter("baseUrl") String baseUrl, @QueryParameter("value") String value) { + context.checkPermission(Item.CONFIGURE); return GraniteCredentialsListBoxModel.fillItems(value, context, baseUrl); } - public FormValidation doTestConnection(@QueryParameter("baseUrl") final String baseUrl, + @RequirePOST + public FormValidation doTestConnection(@AncestorInPath Item context, + @QueryParameter("baseUrl") final String baseUrl, @QueryParameter("credentialsId") final String credentialsId, @QueryParameter("requestTimeout") final long requestTimeout, @QueryParameter("serviceTimeout") final long serviceTimeout) throws IOException, ServletException { - + context.checkPermission(Item.CONFIGURE); return BaseUrlUtil.testOneConnection(baseUrl, credentialsId, requestTimeout, serviceTimeout); }
src/main/java/org/jenkinsci/plugins/graniteclient/DeployPackagesBuilder.java+35 −30 modified@@ -27,23 +27,6 @@ package org.jenkinsci.plugins.graniteclient; -import static org.jenkinsci.plugins.graniteclient.BaseUrlUtil.splitByNewline; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import javax.annotation.Nonnull; -import javax.servlet.ServletException; - import com.cloudbees.plugins.credentials.common.AbstractIdCredentialsListBoxModel; import hudson.Extension; import hudson.FilePath; @@ -70,6 +53,24 @@ import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.interceptor.RequirePOST; + +import javax.annotation.Nonnull; +import javax.servlet.ServletException; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.jenkinsci.plugins.graniteclient.BaseUrlUtil.splitByNewline; /** * Implementation of the "Deploy Content Packages to CRX" build step @@ -354,17 +355,17 @@ private Map<PackId, FilePath> selectPackages(@Nonnull final Run<?, ?> build, Collections.sort( listed, Collections.reverseOrder( - new Comparator<FilePath>() { - public int compare(FilePath left, FilePath right) { - try { - return Long.compare(left.lastModified(), right.lastModified()); - } catch (Exception e) { - listener.error("Failed to compare a couple files: %s", e.getMessage()); + new Comparator<FilePath>() { + public int compare(FilePath left, FilePath right) { + try { + return Long.compare(left.lastModified(), right.lastModified()); + } catch (Exception e) { + listener.error("Failed to compare a couple files: %s", e.getMessage()); + } + return 0; + } } - return 0; - } - } - )); + )); for (FilePath path : listed) { PackId packId = path.act(new IdentifyPackageCallable()); @@ -485,9 +486,11 @@ public boolean configure(StaplerRequest req, JSONObject json) throws FormExcepti return true; } + @RequirePOST public AbstractIdCredentialsListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, @QueryParameter("baseUrls") String baseUrls, @QueryParameter("value") String value) { + context.checkPermission(Item.CONFIGURE); List<String> _baseUrls = splitByNewline(baseUrls); if (!_baseUrls.isEmpty()) { @@ -497,12 +500,14 @@ public AbstractIdCredentialsListBoxModel doFillCredentialsIdItems(@AncestorInPat } } - public FormValidation doTestConnection(@QueryParameter("baseUrls") final String baseUrls, + @RequirePOST + public FormValidation doTestConnection(@AncestorInPath Item context, + @QueryParameter("baseUrls") final String baseUrls, @QueryParameter("credentialsId") final String credentialsId, @QueryParameter("requestTimeout") final long requestTimeout, @QueryParameter("serviceTimeout") final long serviceTimeout) throws IOException, ServletException { - + context.checkPermission(Item.CONFIGURE); return BaseUrlUtil.testManyConnections(baseUrls, credentialsId, requestTimeout, serviceTimeout); } @@ -515,7 +520,7 @@ public ListBoxModel doFillAcHandlingItems() { ACHandling.MERGE, ACHandling.OVERWRITE, ACHandling.CLEAR) - ) { + ) { model.add(mode.getLabel(), mode.name()); } return model;
src/main/java/org/jenkinsci/plugins/graniteclient/DownloadPackagesBuilder.java+14 −9 modified@@ -27,13 +27,6 @@ package org.jenkinsci.plugins.graniteclient; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nonnull; -import javax.servlet.ServletException; - import com.cloudbees.plugins.credentials.common.AbstractIdCredentialsListBoxModel; import hudson.Extension; import hudson.FilePath; @@ -55,6 +48,14 @@ import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.interceptor.RequirePOST; + +import javax.annotation.Nonnull; +import javax.servlet.ServletException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * Implementation of the "Download Content Packages from CRX" build step @@ -268,18 +269,22 @@ public boolean isApplicable(Class<? extends AbstractProject> aClass) { return true; } + @RequirePOST public AbstractIdCredentialsListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, @QueryParameter("baseUrl") String baseUrl, @QueryParameter("value") String value) { + context.checkPermission(Item.CONFIGURE); return GraniteCredentialsListBoxModel.fillItems(value, context, baseUrl); } - public FormValidation doTestConnection(@QueryParameter("baseUrl") final String baseUrl, + @RequirePOST + public FormValidation doTestConnection(@AncestorInPath Item context, + @QueryParameter("baseUrl") final String baseUrl, @QueryParameter("credentialsId") final String credentialsId, @QueryParameter("requestTimeout") final long requestTimeout, @QueryParameter("serviceTimeout") final long serviceTimeout) throws IOException, ServletException { - + context.checkPermission(Item.CONFIGURE); return BaseUrlUtil.testOneConnection(baseUrl, credentialsId, requestTimeout, serviceTimeout); }
src/main/java/org/jenkinsci/plugins/graniteclient/GraniteAHCFactory.java+3 −0 modified@@ -55,6 +55,7 @@ import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.interceptor.RequirePOST; /** * Global extension and configurable factory for {@link AsyncHttpClient} instances @@ -144,8 +145,10 @@ public String getDisplayName() { return "CRX Content Package Deployer - HTTP Client"; } + @RequirePOST public AbstractIdCredentialsListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, @QueryParameter("value") String value) { + context.checkPermission(Item.CONFIGURE); return GraniteCredentialsListBoxModel.fillItems(value, context); }
src/main/java/org/jenkinsci/plugins/graniteclient/PackageChoiceParameterDefinition.java+14 −9 modified@@ -27,13 +27,6 @@ package org.jenkinsci.plugins.graniteclient; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import javax.servlet.ServletException; - import com.cloudbees.plugins.credentials.common.AbstractIdCredentialsListBoxModel; import hudson.Extension; import hudson.model.Item; @@ -52,6 +45,14 @@ import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.interceptor.RequirePOST; + +import javax.servlet.ServletException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; /** * Implementation of the "CRX Content Package Choice Parameter" type @@ -68,18 +69,22 @@ public String getDisplayName() { return "CRX Content Package Choice Parameter"; } - public FormValidation doTestConnection(@QueryParameter("baseUrl") final String baseUrl, + @RequirePOST + public FormValidation doTestConnection(@AncestorInPath Item context, + @QueryParameter("baseUrl") final String baseUrl, @QueryParameter("credentialsId") final String credentialsId, @QueryParameter("requestTimeout") final long requestTimeout, @QueryParameter("serviceTimeout") final long serviceTimeout) throws IOException, ServletException { - + context.checkPermission(Item.CONFIGURE); return BaseUrlUtil.testOneConnection(baseUrl, credentialsId, requestTimeout, serviceTimeout); } + @RequirePOST public AbstractIdCredentialsListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, @QueryParameter("baseUrl") String baseUrl, @QueryParameter("value") String value) { + context.checkPermission(Item.CONFIGURE); return GraniteCredentialsListBoxModel.fillItems(value, context, baseUrl); }
src/main/java/org/jenkinsci/plugins/graniteclient/ReplicatePackagesBuilder.java+16 −11 modified@@ -27,15 +27,6 @@ package org.jenkinsci.plugins.graniteclient; -import static org.jenkinsci.plugins.graniteclient.BaseUrlUtil.splitByNewline; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nonnull; -import javax.servlet.ServletException; - import com.cloudbees.plugins.credentials.common.AbstractIdCredentialsListBoxModel; import hudson.Extension; import hudson.FilePath; @@ -57,6 +48,16 @@ import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.interceptor.RequirePOST; + +import javax.annotation.Nonnull; +import javax.servlet.ServletException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.jenkinsci.plugins.graniteclient.BaseUrlUtil.splitByNewline; /** * Implementation of the "Replicate Content Packages from CRX" build step @@ -240,9 +241,11 @@ public boolean isApplicable(Class<? extends AbstractProject> aClass) { return true; } + @RequirePOST public AbstractIdCredentialsListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, @QueryParameter("baseUrls") String baseUrls, @QueryParameter("value") String value) { + context.checkPermission(Item.CONFIGURE); List<String> _baseUrls = splitByNewline(baseUrls); if (!_baseUrls.isEmpty()) { @@ -252,12 +255,14 @@ public AbstractIdCredentialsListBoxModel doFillCredentialsIdItems(@AncestorInPat } } - public FormValidation doTestConnection(@QueryParameter("baseUrls") final String baseUrls, + @RequirePOST + public FormValidation doTestConnection(@AncestorInPath Item context, + @QueryParameter("baseUrls") final String baseUrls, @QueryParameter("credentialsId") final String credentialsId, @QueryParameter("requestTimeout") final long requestTimeout, @QueryParameter("serviceTimeout") final long serviceTimeout) throws IOException, ServletException { - + context.checkPermission(Item.CONFIGURE); return BaseUrlUtil.testManyConnections(baseUrls, credentialsId, requestTimeout, serviceTimeout); }
src/main/resources/org/jenkinsci/plugins/graniteclient/BuildPackageBuilder/config.jelly+1 −1 modified@@ -54,7 +54,7 @@ <f:advanced title="Connection Options"> <f:entry title="Credentials" field="credentialsId"> - <creds:select /> + <creds:select checkMethod="post"/> </f:entry> <f:validateButton
src/main/resources/org/jenkinsci/plugins/graniteclient/DeployPackagesBuilder/config.jelly+1 −1 modified@@ -69,7 +69,7 @@ <f:advanced title="Connection Options"> <f:entry title="Credentials" field="credentialsId"> - <creds:select /> + <creds:select checkMethod="post"/> </f:entry> <f:validateButton
src/main/resources/org/jenkinsci/plugins/graniteclient/DownloadPackagesBuilder/config.jelly+1 −1 modified@@ -51,7 +51,7 @@ <f:advanced title="Connection Options"> <f:entry title="Credentials" field="credentialsId"> - <creds:select /> + <creds:select checkMethod="post"/> </f:entry> <f:validateButton
src/main/resources/org/jenkinsci/plugins/graniteclient/GraniteAHCFactory/global.jelly+1 −1 modified@@ -30,7 +30,7 @@ <f:section title="CRX Content Package Deployer - HTTP Client" name="GraniteAHCFactory"> <f:entry title="Default Credentials" field="credentialsId"> - <creds:select /> + <creds:select checkMethod="post"/> </f:entry> <f:entry title="Preempt Login Base URL Patterns" field="preemptLoginForBaseUrls">
src/main/resources/org/jenkinsci/plugins/graniteclient/PackageChoiceParameterDefinition/config.jelly+1 −1 modified@@ -68,7 +68,7 @@ <f:advanced title="Connection Options"> <f:entry title="Credentials" field="credentialsId"> - <creds:select /> + <creds:select checkMethod="post"/> </f:entry> <f:validateButton
src/main/resources/org/jenkinsci/plugins/graniteclient/ReplicatePackagesBuilder/config.jelly+1 −1 modified@@ -43,7 +43,7 @@ <f:advanced title="Connection Options"> <f:entry title="Credentials" field="credentialsId"> - <creds:select /> + <creds:select checkMethod="post"/> </f:entry> <f:validateButton
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-jww4-2793-9gmgghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-10438ghsaADVISORY
- github.com/jenkinsci/crx-content-package-deployer-plugin/commit/1313c422170a064dab0f9359324ff27e30a9f4a5ghsaWEB
- jenkins.io/security/advisory/2019-10-16/mitrex_refsource_CONFIRM
- jenkins.io/security/advisory/2019-10-16/ghsaWEB
News mentions
0No linked articles in our index yet.