CVE-2018-25083: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') (CWE-78)
value: The pullit package before 1.4.0 for Node.js allows OS Command Injection because eval is used on an attacker-supplied Git branch name.
CWE description: The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
CVSS v3 base score: 9.8
Published: 2023-03-27T03:15Z
Repository: https://github.com/jkup/pullit
Language: JSON
Files changed: package.json, src/index.js
Commit date: 2018-02-18 18:44:07 -0800
Commit message: fix security vuln

Diff: diff --git a/package.json b/package.json
index 012cd55..2ba75b1 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
   "name": "pullit",
-  "version": "1.3.0",
+  "version": "1.4.0",
   "description": "Display and pull branches from GitHub pull requests",
   "main": "src/index.js",
   "preferGlobal": true,
   "scripts": {
     "start": "node src/index.js",
@@ -10,17 +10,11 @@
   },
   "repository": {
     "type": "git",
     "url": "git+https://github.com/jkup/pullit.git"
   },
-  "keywords": [
-    "github",
-    "pull",
-    "request",
-    "pr",
-    "cli"
-  ],
+  "keywords": ["github", "pull", "request", "pr", "cli"],
   "author": "Jon Kuperman",
   "license": "MIT",
   "bin": {
     "pullit": "./bin/pullit.js"
   },
@@ -36,17 +30,11 @@
     "github": "^11.0.0",
     "parse-github-repo-url": "^1.4.1",
     "terminal-menu": "^2.1.1"
   },
   "jest": {
-    "collectCoverageFrom": [
-      "src/**/*.js"
-    ],
+    "collectCoverageFrom": ["src/**/*.js"],
     "testEnvironment": "node",
-    "modulePathIgnorePatterns": [
-      "__tests__/fixtures/"
-    ],
-    "testPathIgnorePatterns": [
-      "__tests__/fixtures/"
-    ]
+    "modulePathIgnorePatterns": ["__tests__/fixtures/"],
+    "testPathIgnorePatterns": ["__tests__/fixtures/"]
   }
-}
\ No newline at end of file
+}
diff --git a/src/index.js b/src/index.js
index 3a34831..03b3055 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,21 +1,19 @@
-const GitHubApi = require('github');
-const Menu = require('terminal-menu');
-const {
-  execSync
-} = require('child_process');
-const parse = require('parse-github-repo-url');
+const GitHubApi = require("github");
+const Menu = require("terminal-menu");
+const { execFileSync } = require("child_process");
+const parse = require("parse-github-repo-url");
 
 class Pullit {
   constructor() {
     this.init();
     this.github = new GitHubApi({});
   }
 
   init() {
-    const url = execSync(`git config --get remote.origin.url`, {
-      encoding: 'utf8'
+    const url = execFileSync("git", ["config", "--get", "remote.origin.url"], {
+      encoding: "utf8"
     }).trim();
 
     return this.parsedGithubUrl(url);
   }
 
@@ -32,16 +30,15 @@ class Pullit {
         repo: this.repo,
         number: id
       })
       .then(res => {
         const branch = res.data.head.ref;
-        execSync(
-          `git fetch origin pull/${id}/head:${branch} && git checkout ${branch}`
-        );
+        execFileSync("git", ["fetch", "origin", `pull/${id}/head:${branch}`]);
+        execFileSync("git", ["checkout", branch]);
       })
       .catch(err => {
-        console.log('Error: Could not find the specified pull request.');
+        console.log("Error: Could not find the specified pull request.");
       });
   }
 
   fetchRequests() {
     return this.github.pullRequests.getAll({
@@ -49,39 +46,45 @@ class Pullit {
       repo: this.repo
     });
   }
 
   display() {
-    this.fetchRequests().then(results => {
-      const menu = Menu({
-       width: process.stdout.columns - 4,
-        x: 0,
-        y: 2
-      });
-      menu.reset();
-      menu.write('Currently open pull requests:\n');
-      menu.write('-------------------------\n');
+    this.fetchRequests()
+      .then(results => {
+        const menu = Menu({
+          width: process.stdout.columns - 4,
+          x: 0,
+          y: 2
+        });
+        menu.reset();
+        menu.write("Currently open pull requests:\n");
+        menu.write("-------------------------\n");
 
-      results.data.forEach(element => {
-        menu.add(`${element.number} - ${element.title} - ${element.head.user.login}`);
-      });
+        results.data.forEach(element => {
+          menu.add(
+            `${element.number} - ${element.title} - ${element.head.user.login}`
+          );
+        });
 
-      menu.add(`Exit`);
+        menu.add(`Exit`);
 
-      menu.on('select', label => {
-        menu.close();
-        this.fetch(label.split(' ')[0]);
-      });
-      process.stdin.pipe(menu.createStream()).pipe(process.stdout);
+        menu.on("select", label => {
+          menu.close();
+          this.fetch(label.split(" ")[0]);
+        });
+        process.stdin.pipe(menu.createStream()).pipe(process.stdout);
 
-      process.stdin.setRawMode(true);
-      menu.on('close', () => {
-        process.stdin.setRawMode(false);
-        process.stdin.end();
+        process.stdin.setRawMode(true);
+        menu.on("close", () => {
+          process.stdin.setRawMode(false);
+          process.stdin.end();
+        });
+      })
+      .catch(err => {
+        console.log(
+          "Error: could not display pull requests. Please make sure this is a valid repository."
+        );
       });
-    }).catch(err => {
-      console.log('Error: could not display pull requests. Please make sure this is a valid repository.')
-    });
   }
 }
 
 module.exports = Pullit;

Vulnerable code:   "version": "1.3.0",
  "keywords": [
    "github",
    "pull",
    "request",
    "pr",
    "cli"
  ],
    "collectCoverageFrom": [
      "src/**/*.js"
    ],
    "modulePathIgnorePatterns": [
      "__tests__/fixtures/"
    ],
    "testPathIgnorePatterns": [
      "__tests__/fixtures/"
    ]
}
const GitHubApi = require('github');
const Menu = require('terminal-menu');
const {
  execSync
} = require('child_process');
const parse = require('parse-github-repo-url');
    const url = execSync(`git config --get remote.origin.url`, {
      encoding: 'utf8'
        execSync(
          `git fetch origin pull/${id}/head:${branch} && git checkout ${branch}`
        );
        console.log('Error: Could not find the specified pull request.');
    this.fetchRequests().then(results => {
      const menu = Menu({
       width: process.stdout.columns - 4,
        x: 0,
        y: 2
      });
      menu.reset();
      menu.write('Currently open pull requests:\n');
      menu.write('-------------------------\n');
      results.data.forEach(element => {
        menu.add(`${element.number} - ${element.title} - ${element.head.user.login}`);
      });
      menu.add(`Exit`);
      menu.on('select', label => {
        menu.close();
        this.fetch(label.split(' ')[0]);
      });
      process.stdin.pipe(menu.createStream()).pipe(process.stdout);
      process.stdin.setRawMode(true);
      menu.on('close', () => {
        process.stdin.setRawMode(false);
        process.stdin.end();
    }).catch(err => {
      console.log('Error: could not display pull requests. Please make sure this is a valid repository.')
    });
Fixed code:   "version": "1.4.0",
  "keywords": ["github", "pull", "request", "pr", "cli"],
    "collectCoverageFrom": ["src/**/*.js"],
    "modulePathIgnorePatterns": ["__tests__/fixtures/"],
    "testPathIgnorePatterns": ["__tests__/fixtures/"]
}
const GitHubApi = require("github");
const Menu = require("terminal-menu");
const { execFileSync } = require("child_process");
const parse = require("parse-github-repo-url");
    const url = execFileSync("git", ["config", "--get", "remote.origin.url"], {
      encoding: "utf8"
        execFileSync("git", ["fetch", "origin", `pull/${id}/head:${branch}`]);
        execFileSync("git", ["checkout", branch]);
        console.log("Error: Could not find the specified pull request.");
    this.fetchRequests()
      .then(results => {
        const menu = Menu({
          width: process.stdout.columns - 4,
          x: 0,
          y: 2
        });
        menu.reset();
        menu.write("Currently open pull requests:\n");
        menu.write("-------------------------\n");
        results.data.forEach(element => {
          menu.add(
            `${element.number} - ${element.title} - ${element.head.user.login}`
          );
        });
        menu.add(`Exit`);
        menu.on("select", label => {
          menu.close();
          this.fetch(label.split(" ")[0]);
        });
        process.stdin.pipe(menu.createStream()).pipe(process.stdout);
        process.stdin.setRawMode(true);
        menu.on("close", () => {
          process.stdin.setRawMode(false);
          process.stdin.end();
        });
      })
      .catch(err => {
        console.log(
          "Error: could not display pull requests. Please make sure this is a valid repository."
        );