Vulnerability: Command Injection in git-tags-remote All versions of `git-tags-remote ` are vulnerable to Command Injection. The package fails to sanitize the repository input and passes it directly to an `exec` call on the `get` function . This may allow attackers to execute arbitrary code in the system if the `repo` value passed to the function is user-controlled.  

The following proof-of-concept creates a file in `/tmp`:  
```
const gitTagsRemote = require('git-tags-remote');

gitTagsRemote.get('https://github.com/sh0ji/git-tags-remote.git; echo "Injection Success" > /tmp/command-injection.test')
.then(tags => console.log(tags));
```
Vulnerability ID: GHSA-gm9x-q798-hmr4
CWE: CWE-78
Severity score: 7.2
Project: https://github.com/sh0ji/git-tags-remote
Published: 2020-07-29
File: src/index.ts
Commit message: fix: use spawn for more secure input

resolves #58
Commit author: Evan Yamanishi

Vulnerable version (before fix):
 import { valid, compare } from 'semver'; import { exec } from 'child_process';  const lsRemoteTags = (repo: string): Promise<string> => new Promise( 	(resolve, reject) => { 		exec(`git ls-remote --tags ${repo}`, (_, stdout, stderr) => { 			if (stderr) reject(new Error(stderr)); 			resolve(stdout.toString().trim()); 		}); 	}, );  const parseTags = (tags: string): Map<string, string> => { 	const tagMap = new Map(); 	tags.split('\n') 		.forEach((str) => { 			const ref = str.split(/\t/);

Fixed version (after fix):
 import { valid, compare } from 'semver'; import { spawn } from 'child_process';  const lsRemoteTags = (repoPath: string): Promise<string> => new Promise((resolve, reject) => { 	let stderr = ''; 	let stdout = '';  	const child = spawn('git', ['ls-remote', '--tags', repoPath]);  	child.stdout.on('data', (data) => { 		stdout += data; 	});  	child.stderr.on('data', (data) => { 		stderr += data; 	});  	child.on('error', reject);  	child.on('close', (exitCode) => { 		if (exitCode !== 0 || stderr.length) reject(new Error(stderr)); 		resolve(stdout.toString().trim()); 	}); });  const parseTags = (tags: string): Map<string, string> => { 	const tagMap = new Map<string, string>(); 	tags.split('\n') 		.forEach((str) => { 			const ref = str.split(/\t/);

Patch:
patch: @@ -1,17 +1,30 @@
 import { valid, compare } from 'semver';
-import { exec } from 'child_process';
+import { spawn } from 'child_process';
 
-const lsRemoteTags = (repo: string): Promise<string> => new Promise(
-	(resolve, reject) => {
-		exec(`git ls-remote --tags ${repo}`, (_, stdout, stderr) => {
-			if (stderr) reject(new Error(stderr));
-			resolve(stdout.toString().trim());
-		});
-	},
-);
+const lsRemoteTags = (repoPath: string): Promise<string> => new Promise((resolve, reject) => {
+	let stderr = '';
+	let stdout = '';
+
+	const child = spawn('git', ['ls-remote', '--tags', repoPath]);
+
+	child.stdout.on('data', (data) => {
+		stdout += data;
+	});
+
+	child.stderr.on('data', (data) => {
+		stderr += data;
+	});
+
+	child.on('error', reject);
+
+	child.on('close', (exitCode) => {
+		if (exitCode !== 0 || stderr.length) reject(new Error(stderr));
+		resolve(stdout.toString().trim());
+	});
+});
 
 const parseTags = (tags: string): Map<string, string> => {
-	const tagMap = new Map();
+	const tagMap = new Map<string, string>();
 	tags.split('\n')
 		.forEach((str) => {
 			const ref = str.split(/\t/);
path: src/index.ts