CVE-2022-24066 (published 2022-04-01T20:15Z)

The package simple-git before 3.5.0 are vulnerable to Command Injection due to an incomplete fix of [CVE-2022-24433](https://security.snyk.io/vuln/SNYK-JS-SIMPLEGIT-2421199) which only patches against the git fetch attack vector. A similar use of the --upload-pack feature of git is also supported for git clone, which the prior fix didn't cover.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-88
cwe_name: Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')
description: The product constructs a string for a command to be executed by a separate component
in another control sphere, but it does not properly delimit the
intended arguments, options, or switches within that command string.
extended_description: 
            When creating commands using interpolation into a string, developers may assume that only the arguments/options that they specify will be processed.  This assumption may be even stronger when the programmer has encoded the command in a way that prevents separate commands from being provided maliciously, e.g. in the case of shell metacharacters.  When constructing the command, the developer may use whitespace or other delimiters that are required to separate arguments when the command. However, if an attacker can provide an untrusted input that contains argument-separating delimiters, then the resulting command will have more arguments than intended by the developer.  The attacker may then be able to change the behavior of the command.  Depending on the functionality supported by the extraneous arguments, this may have security-relevant consequences.
         
         

Fix information:
repo_url: https://github.com/steveukx/git-js
commit_details: repo_url: https://github.com/steveukx/git-js
author: Steve King
author_date: 2022-03-29 07:21:01+01:00
msg: Prevent use of `--upload-pack` as a command in `git.clone` to avoid potential accidental command execution.
repository: repo_url: https://github.com/steveukx/git-js
repo_name: steveukx/git-js
description: A light weight interface for running git commands in any node.js application.
repo_language: TypeScript
owner: steveukx
file_changes: filename: wicked-files-vanish.md
change_type: ModificationType.ADD
diff: @@ -0,0 +1,5 @@
+---
+"simple-git": minor
+---
+
+Resolves potential command injection vulnerability by preventing use of `--upload-pack` in `git.clone`

programming_language: Markdown
filename: clone.ts
change_type: ModificationType.MODIFY
diff: @@ -1,10 +1,9 @@
-import { straightThroughStringTask } from './task';
+import { configurationErrorTask, EmptyTask, straightThroughStringTask } from './task';
 import { OptionFlags, Options, StringTask } from '../types';
-import { append } from '../utils';
+import { append, filterString } from '../utils';
 
 export type CloneOptions = Options &
-   OptionFlags<
-      '--bare' |
+   OptionFlags<'--bare' |
       '--dissociate' |
       '--mirror' |
       '--no-checkout' |
@@ -15,25 +14,30 @@ export type CloneOptions = Options &
       '--remote-submodules' |
       '--single-branch' |
       '--shallow-submodules' |
-      '--verbose'
-      > &
+      '--verbose'> &
    OptionFlags<'--depth' | '-j' | '--jobs', number> &
    OptionFlags<'--branch' | '--origin' | '--recurse-submodules' | '--separate-git-dir' | '--shallow-exclude' | '--shallow-since' | '--template', string>
 
-export function cloneTask(repo: string | undefined, directory: string | undefined, customArgs: string[]): StringTask<string> {
+function disallowedCommand(command: string) {
+   return /^--upload-pack(=|$)/.test(command);
+}
+
+export function cloneTask(repo: string | undefined, directory: string | undefined, customArgs: string[]): StringTask<string> | EmptyTask {
    const commands = ['clone', ...customArgs];
-   if (typeof repo === 'string') {
-      commands.push(repo);
-   }
-   if (typeof directory === 'string') {
-      commands.push(directory);
+
+   filterString(repo) && commands.push(repo);
+   filterString(directory) && commands.push(directory);
+
+   const banned = commands.find(disallowedCommand);
+   if (banned) {
+      return configurationErrorTask(`git.fetch: potential exploit argument blocked.`);
    }
 
    return straightThroughStringTask(commands);
 }
 
-export function cloneMirrorTask(repo: string | undefined, directory: string | undefined, customArgs: string[]): StringTask<string> {
-   append(customArgs,'--mirror');
+export function cloneMirrorTask(repo: string | undefined, directory: string | undefined, customArgs: string[]) {
+   append(customArgs, '--mirror');
 
    return cloneTask(repo, directory, customArgs);
 }

programming_language: TypeScript
filename: clone.spec.ts
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,6 @@
+import { promiseError } from '@kwsites/promise-result';
 import { SimpleGit, TaskOptions } from 'typings';
-import { assertExecutedCommands, closeWithSuccess, newSimpleGit } from './__fixtures__';
+import { assertExecutedCommands, assertGitError, closeWithSuccess, newSimpleGit } from './__fixtures__';
 
 describe('clone', () => {
    let git: SimpleGit;
@@ -15,7 +16,7 @@ describe('clone', () => {
 
    beforeEach(() => git = newSimpleGit());
 
-   it.each(cloneTests)('callbacks - %s %s', async (api, name, cloneArgs, executedCommands)=> {
+   it.each(cloneTests)('callbacks - %s %s', async (api, name, cloneArgs, executedCommands) => {
       const callback = jest.fn();
       const queue = (git[api] as any)(...cloneArgs, callback);
       await closeWithSuccess(name);
@@ -32,5 +33,30 @@ describe('clone', () => {
       expect(await queue).toBe(name);
       assertExecutedCommands(...executedCommands);
    });
+
+   describe('failures', () => {
+
+      it('disallows upload-pack as remote/branch', async () => {
+         const error = await promiseError(git.clone('origin', '--upload-pack=touch ./foo'));
+
+         assertGitError(error, 'potential exploit argument blocked');
+      });
+
+      it('disallows upload-pack as varargs', async () => {
+         const error = await promiseError(git.clone('origin', 'main', {
+            '--upload-pack': 'touch ./foo'
+         }));
+
+         assertGitError(error, 'potential exploit argument blocked');
+      });
+
+      it('disallows upload-pack as varargs', async () => {
+         const error = await promiseError(git.clone('origin', 'main', [
+            '--upload-pack', 'touch ./foo'
+         ]));
+
+         assertGitError(error, 'potential exploit argument blocked');
+      });
+   });
 });
 

programming_language: TypeScript