CVE-2019-10392
Description
Jenkins Git Client Plugin allowed OS command injection via unsanitized URL argument to git ls-remote, enabling attackers with Job/Configure permission to execute arbitrary commands.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Jenkins Git Client Plugin allowed OS command injection via unsanitized URL argument to git ls-remote, enabling attackers with Job/Configure permission to execute arbitrary commands.
Vulnerability
Description CVE-2019-10392 is an OS command injection vulnerability in the Jenkins Git Client Plugin versions 2.8.4 and earlier, and 3.0.0-rc [1]. The plugin did not properly restrict user-supplied values passed as the URL argument to the git ls-remote command, allowing attackers to inject arbitrary OS commands [2].
Exploitation
An attacker with Job/Configure permission can exploit this by providing a malicious repository URL containing shell metacharacters [3]. The plugin does not validate the URL format, so a crafted string like ; malicious_command can be appended to the URL, leading to command execution [2]. The attack occurs during job configuration when the plugin validates the repository URL via git ls-remote [3].
Impact
Successful exploitation allows the attacker to execute arbitrary system commands on the Jenkins controller, running with the same privileges as the Jenkins process [3]. This can lead to full compromise of the Jenkins server and any data accessible to it [1].
Mitigation
The vulnerability is fixed in Git Client Plugin version 2.8.5 [4]. Users of version 3.0.0-rc should downgrade to 2.8.5 until a stable 3.x fix is available [3]. The fix introduces URL validation and uses the -- separator in git commands to prevent option injection [2].
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:git-clientMaven | < 2.8.5 | 2.8.5 |
Affected products
2- Range: 2.8.4 and earlier, 3.0.0-rc
Patches
1899123fa2eb9[SECURITY-1534] Prevent remote execution by repo URL
1 file changed · +73 −7
src/main/java/org/jenkinsci/plugins/gitclient/CliGitAPIImpl.java+73 −7 modified@@ -369,6 +369,67 @@ public List<IndexEntry> getSubmodules( String treeIsh ) throws GitException, Int return submodules; } + /** + * Constant which disables safety check of remote URL. + * + * <code>CHECK_REMOTE_URL=Boolean.valueOf(System.getProperty(CliGitAPIImpl.class.getName() + ".checkRemoteURL", "true"))</code>. + * + * Refuse unsafe git URL's, including URL's that start with '-' + * and URL's that contain space characters. + * + * Use '-Dorg.jenkinsci.plugins.gitclient.CliGitAPIImpl.checkRemoteURL=false' + * to prevent check of remote URL. + */ + private static final boolean CHECK_REMOTE_URL = Boolean.valueOf(System.getProperty(CliGitAPIImpl.class.getName() + ".checkRemoteURL", "true")); + + /** + * SECURITY-1534 found that arguments + * added to a git URL from the user interface can allow a user + * with job creation permissions to execute an arbitrary program + * on the git server if the git server is configured to allow + * custom pack programs. Reject a URL if it includes invalid + * content. + */ + private void addCheckedRemoteUrl(@NonNull ArgumentListBuilder args, @NonNull String url) { + String trimmedUrl = url.trim(); + /* Don't check for invalid args if URL starts with known good cases. + * Known good cases include: + * '/' - Unix local file + * 'C:' - Windows local file + * 'file:', 'git:', 'http:', 'https:', 'ssh:', and 'git@' - known protocols + */ + if (CHECK_REMOTE_URL + && !trimmedUrl.startsWith("/") + && !trimmedUrl.startsWith("\\\\") + && !trimmedUrl.startsWith("file:") + && !trimmedUrl.startsWith("git:") + && !trimmedUrl.startsWith("git@") + && !trimmedUrl.startsWith("http:") + && !trimmedUrl.startsWith("https:") + && !trimmedUrl.startsWith("ssh:") + && !trimmedUrl.matches("^[A-Za-z]:.+")) { + /* Not one of the known good cases, check if this could be a bad case + * Bad cases include: + * '-' - starts with 'dash' as possible argument + * '`' - includes backquote in the string (command execution) + * ' ' - includes a space (not guaranteed a threat, but threat risk increases) + */ + if (trimmedUrl.startsWith("-") + || trimmedUrl.contains("`") + || trimmedUrl.contains("--upload-pack=") + || trimmedUrl.matches(".*\\s+.*")) { + throw new GitException("Invalid remote URL: " + url); + } + } + // Mark the end of options for git versions that support it. + // Tells command line git that later arguments are operands, not options. + // See POSIX 1-2017 guideline 10. + if (isAtLeastVersion(2, 8, 0, 0)) { + args.add("--"); // SECURITY-1534 - end of options, causes tests to fail with git 2.7.4 and older + } + args.add(trimmedUrl); + } + /** * fetch_. * @@ -497,7 +558,7 @@ public void fetch(String remoteName, RefSpec... refspec) throws GitException, In String url = getRemoteUrl(remoteName); if (url == null) throw new GitException("remote." + remoteName + ".url not defined"); - args.add(url); + addCheckedRemoteUrl(args, url); if (refspec != null && refspec.length > 0) for (RefSpec rs: refspec) if (rs != null) @@ -2770,7 +2831,10 @@ public Set<String> getRemoteTagNames(String tagPattern) throws GitException { try { ArgumentListBuilder args = new ArgumentListBuilder(); args.add("ls-remote", "--tags"); - args.add(getRemoteUrl("origin")); + String remoteUrl = getRemoteUrl("origin"); + if (remoteUrl != null) { + addCheckedRemoteUrl(args, remoteUrl); + } if (tagPattern != null) args.add(tagPattern); String result = launchCommandIn(args, workspace); @@ -2872,7 +2936,7 @@ public Set<String> getRefNames(String refPrefix) throws GitException, Interrupte public Map<String, ObjectId> getHeadRev(String url) throws GitException, InterruptedException { ArgumentListBuilder args = new ArgumentListBuilder("ls-remote"); args.add("-h"); - args.add(url); + addCheckedRemoteUrl(args, url); StandardCredentials cred = credentials.get(url); if (cred == null) cred = defaultCredentials; @@ -2902,7 +2966,8 @@ public ObjectId getHeadRev(String url, String branchSpec) throws GitException, I StandardCredentials cred = credentials.get(url); if (cred == null) cred = defaultCredentials; - args.add(url); + addCheckedRemoteUrl(args, url); + if (branchName.startsWith("refs/tags/")) { args.add(branchName+"^{}"); // JENKINS-23299 - tag SHA1 needs to be converted to commit SHA1 } else { @@ -2922,7 +2987,7 @@ public Map<String, ObjectId> getRemoteReferences(String url, String pattern, boo if (tagsOnly) { args.add("-t"); } - args.add(url); + addCheckedRemoteUrl(args, url); if (pattern != null) { args.add(pattern); } @@ -2964,7 +3029,7 @@ public Map<String, String> getRemoteSymbolicReferences(String url, String patter // https://github.com/git/git/blob/afd6726309/Documentation/RelNotes/2.8.0.txt#L72-L73 ArgumentListBuilder args = new ArgumentListBuilder("ls-remote"); args.add("--symref"); - args.add(url); + addCheckedRemoteUrl(args, url); if (pattern != null) { args.add(pattern); } @@ -3013,7 +3078,8 @@ public void push(RemoteConfig repository, String refspec) throws GitException, I StandardCredentials cred = credentials.get(url); if (cred == null) cred = defaultCredentials; - args.add("push", url); + args.add("push"); + addCheckedRemoteUrl(args, url); if (refspec != null) args.add(refspec);
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-hw6x-2qwv-rxr7ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-10392ghsaADVISORY
- www.openwall.com/lists/oss-security/2019/09/12/2ghsamailing-listx_refsource_MLISTWEB
- github.com/jenkinsci/git-client-plugin/commit/899123fa2eb9dd2c37137aae630c47c6be6b4b02ghsaWEB
- jenkins.io/security/advisory/2019-09-12/ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.