VYPR
Moderate severityNVD Advisory· Published Apr 18, 2019· Updated Aug 4, 2024

CVE-2019-10305

CVE-2019-10305

Description

A missing permission check in Jenkins XebiaLabs XL Deploy Plugin in the Credential#doValidateUserNamePassword form validation method allows attackers with Overall/Read permission to initiate a connection to an attacker-specified server.

AI Insight

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

Missing permission check in Jenkins XebiaLabs XL Deploy Plugin allows users with Overall/Read to initiate connections to attacker-specified servers.

Vulnerability

CVE-2019-10305 is a missing permission check vulnerability in the Jenkins XebiaLabs XL Deploy Plugin. The Credential#doValidateUserNamePassword form validation method did not verify that the user had the required permissions before allowing a connection to an attacker-specified server. This issue is described in the official advisory [1][3].

Exploitation

An attacker with only Overall/Read permission (the lowest permission level) can exploit this flaw. No special authentication or network position is required beyond that. The method also lacked a POST request requirement, making it potentially exploitable via cross-site request forgery (CSRF) as well, though the primary impact is the unauthorized connection initiation. The fix introduced in commit [2] adds a @RequirePOST annotation and requires Overall/Administer permissions for the method.

Impact

By exploiting this vulnerability, an attacker can initiate a connection from the Jenkins server to an arbitrary server of their choosing. This could be used to exfiltrate data, perform port scanning, or leverage Jenkins as a pivot point for further attacks. The attack does not directly leak credentials, but the connection itself can be abused for reconnaissance or to interact with internal services.

Mitigation

The Jenkins security advisory [3] recommends updating the XebiaLabs XL Deploy Plugin to a version that includes the fix. The fix is present in plugin version 7.0.0 or later, as indicated by the commit history. Users should upgrade as soon as possible to prevent exploitation.

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
com.xebialabs.deployit.ci:deployit-pluginMaven
< 7.5.57.5.5

Affected products

3

Patches

1
5acf9d797fe0

Merge pull request #62 from FellipeAvanci/XLINT-629

https://github.com/jenkinsci/xldeploy-pluginXebiaLabs CI ServersMay 16, 2019via ghsa
8 files changed · +72 47
  • src/main/java/com/xebialabs/deployit/ci/Credential.java+22 26 modified
    @@ -35,10 +35,9 @@
     import java.util.logging.Logger;
     
     import com.cloudbees.plugins.credentials.CredentialsMatchers;
    -import com.cloudbees.plugins.credentials.common.IdCredentials;
    +import com.cloudbees.plugins.credentials.CredentialsProvider;
     import com.cloudbees.plugins.credentials.common.StandardUsernameListBoxModel;
     import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
    -import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
     import com.cloudbees.plugins.credentials.domains.SchemeRequirement;
     import com.google.common.base.Function;
     import com.google.common.base.Strings;
    @@ -49,6 +48,7 @@
     import org.kohsuke.stapler.AncestorInPath;
     import org.kohsuke.stapler.DataBoundConstructor;
     import org.kohsuke.stapler.QueryParameter;
    +import org.kohsuke.stapler.interceptor.RequirePOST;
     
     import hudson.Extension;
     import hudson.model.AbstractDescribableImpl;
    @@ -115,15 +115,6 @@ public boolean isUseGlobalCredential() {
             return useGlobalCredential;
         }
     
    -    public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Project context) {
    -        // TODO: also add requirement on host derived from URL ?
    -        List<StandardUsernamePasswordCredentials> creds = lookupCredentials(StandardUsernamePasswordCredentials.class, context,
    -                ACL.SYSTEM,
    -                HTTP_SCHEME, HTTPS_SCHEME);
    -
    -        return new StandardUsernameListBoxModel().withAll(creds);
    -    }
    -
         public String getSecondaryServerUrl() {
             if (secondaryServerInfo != null) {
                 return secondaryServerInfo.secondaryServerUrl;
    @@ -152,14 +143,6 @@ public String resolveProxyUrl(String defaultUrl) {
             return defaultUrl;
         }
     
    -    public boolean showSecondaryServerSettings() {
    -        return secondaryServerInfo != null && secondaryServerInfo.showSecondaryServerSettings();
    -    }
    -
    -    public boolean showGolbalCredentials() {
    -        return useGlobalCredential;
    -    }
    -
         @Override
         public String toString() {
             return name;
    @@ -200,10 +183,6 @@ public SecondaryServerInfo(String secondaryServerUrl, String secondaryProxyUrl)
                 this.secondaryProxyUrl = secondaryProxyUrl;
             }
     
    -        public boolean showSecondaryServerSettings() {
    -            return !Strings.isNullOrEmpty(secondaryServerUrl) || !Strings.isNullOrEmpty(secondaryProxyUrl);
    -        }
    -
             public String resolveServerUrl(String defaultUrl) {
                 if (!Strings.isNullOrEmpty(secondaryServerUrl)) {
                     return secondaryServerUrl;
    @@ -241,7 +220,7 @@ public int hashCode() {
             }
         }
     
    -    public static StandardUsernamePasswordCredentials lookupSystemCredentials(String credentialsId, ItemGroup<?> item) 
    +    public static StandardUsernamePasswordCredentials lookupSystemCredentials(String credentialsId, ItemGroup<?> item)
         {
             StandardUsernamePasswordCredentials result = null;
     
    @@ -261,7 +240,7 @@ public static StandardUsernamePasswordCredentials lookupSystemCredentials(String
                 LOGGER.fine(String.format("[XLD] using credentails '%s'", result.getId()));
             }
     
    -        return result; 
    +        return result;
         }
     
         @Extension
    @@ -273,14 +252,23 @@ public String getDisplayName() {
     
             public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Project context) {
                 // TODO: also add requirement on host derived from URL ?
    +
    +            if (context == null && !Jenkins.getActiveInstance().hasPermission(Jenkins.ADMINISTER) ||
    +                    context != null && !context.hasPermission(context.EXTENDED_READ) &&
    +                            !context.hasPermission(CredentialsProvider.USE_ITEM)) {
    +                return new StandardUsernameListBoxModel();
    +            }
    +
                 List<StandardUsernamePasswordCredentials> creds = lookupCredentials(StandardUsernamePasswordCredentials.class, context,
                         ACL.SYSTEM,
                         HTTP_SCHEME, HTTPS_SCHEME);
     
                 return new StandardUsernameListBoxModel().withAll(creds);
             }
     
    +        @RequirePOST
             private FormValidation validateOptionalUrl(String url) {
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
                 try {
                     if (!Strings.isNullOrEmpty(url)) {
                         new URL(url);
    @@ -291,11 +279,15 @@ private FormValidation validateOptionalUrl(String url) {
                 return ok();
             }
     
    +        @RequirePOST
             public FormValidation doCheckSecondaryServerUrl(@QueryParameter String secondaryServerUrl) {
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
                 return validateOptionalUrl(secondaryServerUrl);
             }
     
    +        @RequirePOST
             public FormValidation doCheckSecondaryProxyUrl(@QueryParameter String secondaryProxyUrl) {
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
                 return validateOptionalUrl(secondaryProxyUrl);
             }
     
    @@ -306,8 +298,10 @@ public static Credential fromStapler(@QueryParameter String name, @QueryParamete
                 return new Credential(name, username, password, credentialsId, new SecondaryServerInfo(secondaryServerUrl, secondaryProxyUrl), useGlobalCredential);
             }
     
    +        @RequirePOST
             public FormValidation doValidateUserNamePassword(@QueryParameter String deployitServerUrl, @QueryParameter String deployitClientProxyUrl, @QueryParameter String username,
                                                              @QueryParameter Secret password, @QueryParameter String secondaryServerUrl, @QueryParameter String secondaryProxyUrl) throws IOException {
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
                 try {
                     String serverUrl = Strings.isNullOrEmpty(secondaryServerUrl) ? deployitServerUrl : secondaryServerUrl;
                     String proxyUrl = Strings.isNullOrEmpty(secondaryProxyUrl) ? deployitClientProxyUrl : secondaryProxyUrl;
    @@ -324,12 +318,14 @@ public FormValidation doValidateUserNamePassword(@QueryParameter String deployit
                 }
             }
     
    +        @RequirePOST
             private FormValidation validateConnection(String serverUrl, String proxyUrl, String username, String password) throws Exception {
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
                 DeployitServer deployitServer = DeployitServerFactory.newInstance(serverUrl, proxyUrl, username, password, 10, DeployitServer.DEFAULT_SOCKET_TIMEOUT);
                 ServerInfo serverInfo = deployitServer.getServerInfo();
                 deployitServer.newCommunicator();
                 return FormValidation.ok("Your XL Deploy instance [%s] is alive, and your credentials are valid!", serverInfo.getVersion());
             }
         }
     
    -}
    +}
    \ No newline at end of file
    
  • src/main/java/com/xebialabs/deployit/ci/DeployitNotifier.java+26 2 modified
    @@ -46,15 +46,16 @@
     import java.util.HashMap;
     import java.util.List;
     import java.util.Map;
    -import java.util.logging.Level;
     import java.util.logging.Logger;
     
    +
     import net.sf.json.JSONObject;
     
     import org.kohsuke.stapler.AncestorInPath;
     import org.kohsuke.stapler.DataBoundConstructor;
     import org.kohsuke.stapler.QueryParameter;
     import org.kohsuke.stapler.StaplerRequest;
    +import org.kohsuke.stapler.interceptor.RequirePOST;
     
     import com.google.common.base.Strings;
     
    @@ -67,6 +68,7 @@
     import static hudson.util.FormValidation.error;
     import static hudson.util.FormValidation.ok;
     import static hudson.util.FormValidation.warning;
    +import jenkins.model.Jenkins;
     
     /**
      * Runs XL Deploy tasks after the build has completed.
    @@ -253,6 +255,7 @@ public int getConnectionPoolSize() {
                 return connectionPoolSize;
             }
     
    +
             private FormValidation validateOptionalUrl(String url) {
                 try {
                     if (!Strings.isNullOrEmpty(url)) {
    @@ -265,18 +268,27 @@ private FormValidation validateOptionalUrl(String url) {
     
             }
     
    +        @RequirePOST
             public FormValidation doCheckDeployitServerUrl(@QueryParameter String deployitServerUrl) {
    +
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
                 if (Strings.isNullOrEmpty(deployitServerUrl)) {
                     return error("Url required.");
                 }
                 return validateOptionalUrl(deployitServerUrl);
             }
     
    +        @RequirePOST
             public FormValidation doCheckDeployitClientProxyUrl(@QueryParameter String deployitClientProxyUrl) {
    +
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
                 return validateOptionalUrl(deployitClientProxyUrl);
             }
     
    +        @RequirePOST
             public FormValidation doCheckConnectionPoolSize(@QueryParameter String connectionPoolSize) {
    +
    +            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
                 if (Strings.isNullOrEmpty(connectionPoolSize)) {
                     return error("Connection pool size is required.");
                 }
    @@ -307,7 +319,10 @@ public ListBoxModel doFillCredentialsIdItems(@AncestorInPath ItemGroup context)
                 return new StandardUsernameListBoxModel().withAll(creds);
             }
     
    +        @RequirePOST
             public FormValidation doCheckCredential(@QueryParameter String credential, @AncestorInPath AbstractProject project) {
    +
    +            project.checkPermission(Jenkins.ADMINISTER);
                 DeployitNotifier deployitNotifier = RepositoryUtils.retrieveDeployitNotifierFromProject(project);
                 String warningMsg = "Changing credentials may unintentionally change your deployables' types - check the definitions afterward.";
                 if (null != deployitNotifier) {
    @@ -321,7 +336,10 @@ public FormValidation doCheckCredential(@QueryParameter String credential, @Ance
                 return ok();
             }
     
    +        @RequirePOST
             public AutoCompletionCandidates doAutoCompleteApplication(@QueryParameter final String value, @AncestorInPath AbstractProject project) {
    +
    +            project.checkPermission(Jenkins.ADMINISTER);
                 String resolvedApplicationName = expandValue(value, project);
                 final AutoCompletionCandidates applicationCadidates = new AutoCompletionCandidates();
     
    @@ -342,7 +360,10 @@ public AutoCompletionCandidates doAutoCompleteApplication(@QueryParameter final
                 return applicationCadidates;
             }
     
    +        @RequirePOST
             public FormValidation doCheckApplication(@QueryParameter String credential, @QueryParameter final String value, @AncestorInPath AbstractProject<?, ?> project) {
    +
    +            project.checkPermission(Jenkins.ADMINISTER);
                 if ("Applications/".equals(value))
                     return ok("Fill in the application ID, eg Applications/PetClinic");
     
    @@ -364,7 +385,10 @@ public FormValidation doCheckApplication(@QueryParameter String credential, @Que
                 return warning("Application does not exist, but will be created upon package import.");
             }
     
    +        @RequirePOST
             public FormValidation doReloadTypes(@QueryParameter String credential, @AncestorInPath AbstractProject project) {
    +
    +            project.checkPermission(Jenkins.ADMINISTER);
                 Credential overridingcredential = RepositoryUtils.retrieveOverridingCredentialFromProject(project);
                 try {
                     DeployitServer deployitServer = RepositoryUtils.getDeployitServer(credential, overridingcredential, project);
    @@ -391,4 +415,4 @@ public String expandValue(final String value, final Job project) {
                 return resolvedValue;
             }
         }
    -}
    +}
    \ No newline at end of file
    
  • src/main/java/com/xebialabs/deployit/ci/JenkinsDeploymentOptions.java+11 6 modified
    @@ -39,6 +39,7 @@
     import org.kohsuke.stapler.AncestorInPath;
     import org.kohsuke.stapler.DataBoundConstructor;
     import org.kohsuke.stapler.QueryParameter;
    +import org.kohsuke.stapler.interceptor.RequirePOST;
     
     import com.xebialabs.deployit.ci.server.DeployitDescriptorRegistry;
     import com.xebialabs.deployit.ci.server.DeployitServer;
    @@ -83,10 +84,12 @@ public String getDisplayName() {
                 return "DeploymentOptions";
             }
     
    +        @RequirePOST
             public ComboBoxModel doFillEnvironmentItems(@QueryParameter(value = "credential") @RelativePath(value = "..") String credential,
    -            @QueryParameter(value = "credential") String credential2,
    -            @AncestorInPath AbstractProject project)
    +                                                    @QueryParameter(value = "credential") String credential2,
    +                                                    @AncestorInPath AbstractProject project)
             {
    +            project.checkPermission(Jenkins.ADMINISTER);
                 String creds = !isNullOrEmpty(credential) ? credential : credential2;
                 Credential overridingCredential = RepositoryUtils.retrieveOverridingCredentialFromProject(project);
                 List<String> environments = new ArrayList<String>();
    @@ -97,11 +100,13 @@ public ComboBoxModel doFillEnvironmentItems(@QueryParameter(value = "credential"
                 return new ComboBoxModel(environments);
             }
     
    +        @RequirePOST
             public FormValidation doCheckEnvironment(@QueryParameter(value = "credential") @RelativePath(value = "..") String credential,
    -            @QueryParameter(value = "credential") String credential2,
    -            @QueryParameter final String value,
    -            @AncestorInPath AbstractProject<?,?> project)
    +                                                 @QueryParameter(value = "credential") String credential2,
    +                                                 @QueryParameter final String value,
    +                                                 @AncestorInPath AbstractProject<?,?> project)
             {
    +            project.checkPermission(Jenkins.ADMINISTER);
                 if (isNullOrEmpty(value) || "Environments/".equals(value))
                     return ok("Fill in the target environment ID, eg Environments/MyEnv");
     
    @@ -134,4 +139,4 @@ public String getVersion() {
         public void setVersion(String version) {
             this.version = version;
         }
    -}
    +}
    \ No newline at end of file
    
  • src/main/resources/com/xebialabs/deployit/ci/Credential/config.jelly+2 2 modified
    @@ -24,10 +24,10 @@
         </f:radioBlock>
         <f:optionalBlock field="secondaryServerInfo" title="Use non-default XL Deploy Server" checked="${instance.showSecondaryServerSettings()}">
             <f:entry title="${%Server Url}" field="secondaryServerUrl">
    -            <f:textbox/>
    +            <f:textbox checkMethod="post" />
             </f:entry>
             <f:entry title="${%Proxy Url}" field="secondaryProxyUrl">
    -            <f:textbox />
    +            <f:textbox checkMethod="post" />
             </f:entry>
         </f:optionalBlock>
     
    
  • src/main/resources/com/xebialabs/deployit/ci/DeployitNotifier/config.jelly+3 3 modified
    @@ -3,7 +3,7 @@
     
         <f:block>
             <f:entry title="${%Global server credential}" field="credential" help="/plugin/deployit-plugin/help-credential.html">
    -            <f:select />
    +            <f:select checkMethod="post" />
             </f:entry>
         </f:block>
     
    @@ -34,7 +34,7 @@
         <f:validateButton title="${%Reload types for credential}" with="credential" method="reloadTypes" />
     
         <f:entry title="${%Application}" field="application" help="/plugin/deployit-plugin/help-application.html">
    -        <f:textbox default="Applications/" />
    +        <f:textbox default="Applications/" checkMethod="post" />
         </f:entry>
     
         <f:entry title="${%Version}" field="version" help="/plugin/deployit-plugin/help-version.html">
    @@ -104,4 +104,4 @@
         </f:block>
     
     </xld:publisherFix>
    -</j:jelly>
    +</j:jelly>
    \ No newline at end of file
    
  • src/main/resources/com/xebialabs/deployit/ci/DeployitNotifier/global.jelly+4 4 modified
    @@ -1,13 +1,13 @@
     <j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
         <f:section title="XL Deploy">
             <f:entry title="${%Default Server Url}" field="deployitServerUrl">
    -            <f:textbox/>
    +            <f:textbox checkMethod="post" />
             </f:entry>
             <f:entry title="${%Default Proxy Url}" field="deployitClientProxyUrl">
    -            <f:textbox/>
    +            <f:textbox checkMethod="post" />
             </f:entry>
             <f:entry title="${%Connection Pool Size}" field="connectionPoolSize">
    -            <f:number/>
    +            <f:number checkMethod="post" />
             </f:entry>
             <f:entry title="Credentials" field="credentials">
                 <f:repeatable field="credentials" minimum="${1}">
    @@ -23,4 +23,4 @@
             </f:entry>
     
         </f:section>
    -</j:jelly>
    +</j:jelly>
    \ No newline at end of file
    
  • src/main/resources/com/xebialabs/deployit/ci/JenkinsDeploymentOptions/config.jelly+2 2 modified
    @@ -2,7 +2,7 @@
         <script type="text/javascript" src="${rootURL}/plugin/deployit-plugin/js/combobox.js" />
     
         <f:entry title="${%Environment}" field="environment" help="/plugin/deployit-plugin/help-environment.html">
    -        <f:combobox field="environment" clazz="setting-input"/>
    +        <f:combobox field="environment" clazz="setting-input" checkMethod="post" />
         </f:entry>
     
         <f:entry title="${%Version}" field="versionKind">
    @@ -22,4 +22,4 @@
             <f:checkbox/>
         </f:entry>
     
    -</j:jelly>
    +</j:jelly>
    \ No newline at end of file
    
  • src/main/resources/com/xebialabs/deployit/ci/workflow/XLDeployDeployStep/config.jelly+2 2 modified
    @@ -7,6 +7,6 @@
             <f:textbox default="Applications/" />
         </f:entry>
         <f:entry title="${%Environment}" field="environmentId" help="/plugin/deployit-plugin/help-environment.html">
    -        <f:textbox default="Environments/"/>
    +        <f:textbox default="Environments/" checkMethod="post" />
         </f:entry>
    -</j:jelly>
    +</j:jelly>
    \ No newline at end of file
    

Vulnerability mechanics

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

References

5

News mentions

0

No linked articles in our index yet.