CVE-2019-10357
Description
A missing permission check in Jenkins Pipeline: Shared Groovy Libraries Plugin 2.14 and earlier allowed users with Overall/Read access to obtain limited information about the content of SCM repositories referenced by global libraries.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Missing permission check in Jenkins Pipeline: Shared Groovy Libraries Plugin 2.14 and earlier allows users with Overall/Read to discover SCM repository content.
CVE-2019-10357 is a medium-severity vulnerability in the Jenkins Pipeline: Shared Groovy Libraries Plugin (workflow-cps-global-lib) versions 2.14 and earlier. The plugin fails to perform a permission check when processing form validation for the revision parameter (e.g., commit, tag, or branch name) specified for a global library. This bypasses the intended access control, allowing any authenticated user with the Overall/Read permission to query the plugin for information about the content of SCM repositories referenced by those global libraries [1][2].
The attack surface is limited to authenticated Jenkins users who have been granted the Overall/Read permission, which is typically the broadest base permission in a Jenkins instance. The vulnerability exists in the form validation endpoint that does not verify whether the user has the necessary permissions (such as Job/Configure or Library/Configure) to actually modify or view library configuration. As a result, an attacker can send crafted requests to this endpoint to probe the SCM repository [2].
Exploitation allows an attacker to obtain limited information about the content of SCM repositories referenced by global libraries. This could include discovering branch names, tags, or commit hashes that exist in the repository, potentially revealing sensitive project metadata or existence of certain code branches. The vulnerability does not allow code execution or direct modification of the repository content [1].
The issue is fixed in Pipeline: Shared Groovy Libraries Plugin version 2.15, which was released on the same day as the advisory. Users are strongly advised to update to the latest version. The Jenkins Security Advisory 2019-07-31 also announced other vulnerabilities in related plugins, but CVE-2019-10357 is specific to the missing permission check [1][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.
| Package | Affected versions | Patched versions |
|---|---|---|
org.jenkins-ci.plugins.workflow:workflow-cps-global-libMaven | < 2.15 | 2.15 |
Affected products
2- Jenkins project/Jenkins Pipeline: Shared Groovy Libraries Pluginv5Range: 2.14 and earlier
Patches
16fce1e241d82[SECURITY-1422] Add permission checks and CSRF protection to doCheckDefaultVersions
7 files changed · +100 −9
src/main/java/org/jenkinsci/plugins/workflow/libs/FolderLibraries.java+11 −7 modified@@ -28,6 +28,7 @@ import com.cloudbees.hudson.plugins.folder.AbstractFolderProperty; import com.cloudbees.hudson.plugins.folder.AbstractFolderPropertyDescriptor; import hudson.Extension; +import hudson.model.Item; import hudson.model.ItemGroup; import hudson.model.Job; import java.util.ArrayList; @@ -69,27 +70,30 @@ public List<LibraryConfiguration> getLibraries() { return false; } - private Collection<LibraryConfiguration> forGroup(@CheckForNull ItemGroup<?> group) { + private Collection<LibraryConfiguration> forGroup(@CheckForNull ItemGroup<?> group, boolean checkPermission) { List<LibraryConfiguration> libraries = new ArrayList<>(); for (ItemGroup<?> g = group; g instanceof AbstractFolder; g = ((AbstractFolder) g).getParent()) { - FolderLibraries prop = ((AbstractFolder<?>) g).getProperties().get(FolderLibraries.class); - if (prop != null) { - libraries.addAll(prop.getLibraries()); + AbstractFolder<?> f = (AbstractFolder<?>) g; + if (!checkPermission || f.hasPermission(Item.CONFIGURE)) { + FolderLibraries prop = f.getProperties().get(FolderLibraries.class); + if (prop != null) { + libraries.addAll(prop.getLibraries()); + } } } return libraries; } @Override public Collection<LibraryConfiguration> forJob(Job<?,?> job, Map<String,String> libraryVersions) { - return forGroup(job.getParent()); + return forGroup(job.getParent(), false); } @Override public Collection<LibraryConfiguration> fromConfiguration(StaplerRequest request) { - return forGroup(request.findAncestorObject(AbstractFolder.class)); + return forGroup(request.findAncestorObject(AbstractFolder.class), true); } @Override public Collection<LibraryConfiguration> suggestedConfigurations(ItemGroup<?> group) { - return forGroup(group); + return forGroup(group, false); } }
src/main/java/org/jenkinsci/plugins/workflow/libs/GlobalLibraries.java+4 −1 modified@@ -89,7 +89,10 @@ public void setLibraries(List<LibraryConfiguration> libraries) { } @Override public Collection<LibraryConfiguration> fromConfiguration(StaplerRequest request) { - return GlobalLibraries.get().getLibraries(); + if (Jenkins.get().hasPermission(Jenkins.RUN_SCRIPTS)) { + return GlobalLibraries.get().getLibraries(); + } + return Collections.emptySet(); } @Override public Collection<LibraryConfiguration> suggestedConfigurations(ItemGroup<?> group) {
src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration.java+2 −0 modified@@ -45,6 +45,7 @@ import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.interceptor.RequirePOST; /** * User configuration for one library. @@ -160,6 +161,7 @@ public FormValidation doCheckName(@QueryParameter String name) { return FormValidation.ok(); } + @RequirePOST public FormValidation doCheckDefaultVersion(@AncestorInPath Item context, @QueryParameter String defaultVersion, @QueryParameter boolean implicit, @QueryParameter boolean allowVersionOverride, @QueryParameter String name) { if (defaultVersion.isEmpty()) { if (implicit) {
src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryResolver.java+2 −0 modified@@ -65,6 +65,8 @@ public abstract class LibraryResolver implements ExtensionPoint { /** * A list of libraries that may have already been configured in this context. + * Implementations should only return libraries that the current user has + * permission to configure in this context. * @param request a web request * @return known libraries, if any (empty by default) */
src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/config.jelly+1 −1 modified@@ -29,7 +29,7 @@ THE SOFTWARE. <f:textbox/> </f:entry> <f:entry field="defaultVersion" title="${%Default version}"> - <f:textbox/> + <f:textbox checkMethod="post"/> </f:entry> <f:entry field="implicit" title="${%Load implicitly}"> <f:checkbox/>
src/test/java/org/jenkinsci/plugins/workflow/libs/FolderLibrariesTest.java+41 −0 modified@@ -25,15 +25,21 @@ package org.jenkinsci.plugins.workflow.libs; import com.cloudbees.hudson.plugins.folder.Folder; +import com.gargoylesoftware.htmlunit.HttpMethod; +import com.gargoylesoftware.htmlunit.WebRequest; import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlTextArea; +import com.gargoylesoftware.htmlunit.util.NameValuePair; import com.google.common.collect.ImmutableMap; +import hudson.model.Item; import hudson.model.Result; import hudson.plugins.git.GitSCM; +import java.net.URL; import java.util.Arrays; import java.util.Collections; import java.util.List; +import jenkins.model.Jenkins; import jenkins.plugins.git.GitSCMSource; import jenkins.plugins.git.GitSampleRepoRule; import jenkins.scm.impl.subversion.SubversionSCMSource; @@ -53,6 +59,7 @@ import org.jvnet.hudson.test.BuildWatcher; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.MockAuthorizationStrategy; public class FolderLibrariesTest { @@ -214,6 +221,40 @@ public class FolderLibrariesTest { } } + @Issue("SECURITY-1422") + @Test public void checkDefaultVersionRestricted() throws Exception { + sampleRepo1.init(); + sampleRepo1.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo1.git("add", "vars"); + sampleRepo1.git("commit", "--message=init"); + r.jenkins.setSecurityRealm(r.createDummySecurityRealm()); + MockAuthorizationStrategy s = new MockAuthorizationStrategy() + .grant(Jenkins.READ).everywhere().toEveryone() + .grant(Item.READ).everywhere().toEveryone() + .grant(Item.CONFIGURE).everywhere().to("admin"); + r.jenkins.setAuthorizationStrategy(s); + LibraryConfiguration foo = new LibraryConfiguration("foo", new SCMSourceRetriever(new GitSCMSource(sampleRepo1.toString()))); + Folder f = r.jenkins.createProject(Folder.class, "f"); + f.getProperties().add(new FolderLibraries(Arrays.asList(foo))); + JenkinsRule.WebClient wc = r.createWebClient(); + wc.setThrowExceptionOnFailingStatusCode(false); + WebRequest req = new WebRequest(new URL(wc.getContextPath() + f.getUrl() + "/descriptorByName/" + + LibraryConfiguration.class.getName() + "/checkDefaultVersion"), HttpMethod.POST); + req.setRequestParameters(Arrays.asList( + new NameValuePair("name", "foo"), + new NameValuePair("defaultVersion", "master"), + new NameValuePair("value", "master"), + new NameValuePair("implicit", "false"), + new NameValuePair("allowVersionOverride", "true"))); + wc.addCrumb(req); + wc.login("user", "user"); + assertThat(wc.getPage(req).getWebResponse().getContentAsString(), + containsString("Cannot validate default version until after saving and reconfiguring")); + wc.login("admin", "admin"); + assertThat(wc.getPage(req).getWebResponse().getContentAsString(), + containsString("Currently maps to revision")); + } + // TODO test replay of `load`ed scripts as well as libraries // TODO test override of global or top folder scope
src/test/java/org/jenkinsci/plugins/workflow/libs/GlobalLibrariesTest.java+39 −0 modified@@ -24,25 +24,32 @@ package org.jenkinsci.plugins.workflow.libs; +import com.gargoylesoftware.htmlunit.HttpMethod; +import com.gargoylesoftware.htmlunit.WebRequest; import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.util.NameValuePair; import hudson.model.Item; import hudson.model.View; +import java.net.URL; import java.util.Arrays; import java.util.Collections; import java.util.List; import jenkins.model.Jenkins; import jenkins.plugins.git.GitSCMSource; +import jenkins.plugins.git.GitSampleRepoRule; import jenkins.scm.impl.subversion.SubversionSCMSource; import static org.hamcrest.Matchers.*; import org.junit.Test; import static org.junit.Assert.*; import org.junit.Rule; +import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.MockAuthorizationStrategy; public class GlobalLibrariesTest { @Rule public JenkinsRule r = new JenkinsRule(); + @Rule public GitSampleRepoRule sampleRepo = new GitSampleRepoRule(); @Test public void configRoundtrip() throws Exception { r.configRoundtrip(); @@ -71,4 +78,36 @@ public class GlobalLibrariesTest { r.assertEqualDataBoundBeans(Arrays.asList(foo, bar), libs); } + @Issue("SECURITY-1422") + @Test public void checkDefaultVersionRestricted() throws Exception { + sampleRepo.init(); + sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}"); + sampleRepo.git("add", "vars"); + sampleRepo.git("commit", "--message=init"); + r.jenkins.setSecurityRealm(r.createDummySecurityRealm()); + MockAuthorizationStrategy s = new MockAuthorizationStrategy() + .grant(Jenkins.READ).everywhere().toEveryone() + .grant(Jenkins.RUN_SCRIPTS).everywhere().to("admin"); + r.jenkins.setAuthorizationStrategy(s); + LibraryConfiguration foo = new LibraryConfiguration("foo", new SCMSourceRetriever(new GitSCMSource(sampleRepo.toString()))); + GlobalLibraries.get().setLibraries(Arrays.asList(foo)); + JenkinsRule.WebClient wc = r.createWebClient(); + wc.setThrowExceptionOnFailingStatusCode(false); + WebRequest req = new WebRequest(new URL(wc.getContextPath() + "/descriptorByName/" + + LibraryConfiguration.class.getName() + "/checkDefaultVersion"), HttpMethod.POST); + req.setRequestParameters(Arrays.asList( + new NameValuePair("name", "foo"), + new NameValuePair("defaultVersion", "master"), + new NameValuePair("value", "master"), + new NameValuePair("implicit", "false"), + new NameValuePair("allowVersionOverride", "true"))); + wc.addCrumb(req); + wc.login("user", "user"); + assertThat(wc.getPage(req).getWebResponse().getContentAsString(), + containsString("Cannot validate default version until after saving and reconfiguring")); + wc.login("admin", "admin"); + assertThat(wc.getPage(req).getWebResponse().getContentAsString(), + containsString("Currently maps to revision")); + } + }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- access.redhat.com/errata/RHSA-2019:2594ghsavendor-advisoryx_refsource_REDHATWEB
- access.redhat.com/errata/RHSA-2019:2651ghsavendor-advisoryx_refsource_REDHATWEB
- access.redhat.com/errata/RHSA-2019:2662ghsavendor-advisoryx_refsource_REDHATWEB
- github.com/advisories/GHSA-9x5v-8352-244gghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-10357ghsaADVISORY
- www.openwall.com/lists/oss-security/2019/07/31/1ghsamailing-listx_refsource_MLISTWEB
- github.com/jenkinsci/workflow-cps-global-lib-plugin/commit/6fce1e241d82641e8648c546bc63c22a5e07e96bghsaWEB
- jenkins.io/security/advisory/2019-07-31/ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.