VYPR
Critical severityNVD Advisory· Published Jun 1, 2018· Updated Sep 16, 2024

CVE-2018-3757

CVE-2018-3757

Description

CVE-2018-3757 is a command injection in pdf-image v2.0.0 due to unescaped shell command construction.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2018-3757 is a command injection in pdf-image v2.0.0 due to unescaped shell command construction.

Vulnerability

CVE-2018-3757 is a command injection vulnerability in pdf-image version 2.0.0. The library constructs shell commands by concatenating user-controlled input (the pdfFilePath argument) without proper escaping. Specifically, the constructConvertCommandForPage method uses util.format to build a string that is passed directly to child_process.exec, allowing an attacker who controls the PDF file path to inject arbitrary shell commands [1][3].

Exploitation

An attacker with the ability to supply a malicious PDF file path (e.g., through an application that passes user-supplied filenames to pdf-image) can inject shell metacharacters into the command string. For example, a path containing backticks or semicolons would be executed by the shell. No authentication or special privileges are required beyond the ability to provide the file path to the library [1][3].

Impact

Successful exploitation allows the attacker to execute arbitrary shell commands in the context of the application process. This leads to full compromise of the affected system, including potential data exfiltration, file manipulation, or lateral movement. The vulnerability has a CVSS v3 severity of 9.8 (Critical) [2].

Mitigation

The vulnerability is fixed in commit 54679496a89738443917608c2bbe2f6e5dd20e83, which replaces child_process.exec with child-process-promise.spawn, avoiding shell interpretation. Users should update to a version of pdf-image that includes this fix (post-2.0.0). No other workarounds are available [1][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.

PackageAffected versionsPatched versions
pdf-imagenpm
< 2.0.02.0.0

Affected products

1

Patches

1
54679496a897

resolve #340208 - Command injection in 'pdf-image', Severity:Medium (6.1) - fix #38 - solution for v2

https://github.com/roest01/node-pdf-imageTobias RösMay 13, 2018via ghsa
3 files changed · +91 65
  • index.js+59 57 modified
    @@ -5,7 +5,7 @@ var Promise = require("es6-promise").Promise;
     var path = require("path");
     var fs   = require("fs");
     var util = require("util");
    -var exec = require("child_process").exec;
    +var spawn = require("child-process-promise").spawn;
     
     function PDFImage(pdfFilePath, options) {
       if (!options) options = {};
    @@ -23,10 +23,10 @@ function PDFImage(pdfFilePath, options) {
     
     PDFImage.prototype = {
       constructGetInfoCommand: function () {
    -    return util.format(
    -      "pdfinfo \"%s\"",
    -      this.pdfFilePath
    -    );
    +    return {
    +      cmd: "pdfinfo",
    +      args: [this.pdfFilePath]
    +    };
       },
       parseGetInfoCommandOutput: function (output) {
         var info = {};
    @@ -40,20 +40,12 @@ PDFImage.prototype = {
       getInfo: function () {
         var self = this;
         var getInfoCommand = this.constructGetInfoCommand();
    -    var promise = new Promise(function (resolve, reject) {
    -      exec(getInfoCommand, function (err, stdout, stderr) {
    -        if (err) {
    -          return reject({
    -            message: "Failed to get PDF'S information",
    -            error: err,
    -            stdout: stdout,
    -            stderr: stderr
    -          });
    -        }
    -        return resolve(self.parseGetInfoCommandOutput(stdout));
    -      });
    +    return new Promise(function (resolve, reject) {
    +      spawn(getInfoCommand.cmd, getInfoCommand.args, { capture: [ 'stdout', 'stderr' ]})
    +        .then(function (cmdResult) {
    +          resolve(self.parseGetInfoCommandOutput(cmdResult.stdout.toString()));
    +        }).catch(reject);
         });
    -    return promise;
       },
       numberOfPages: function () {
         return this.getInfo().then(function (info) {
    @@ -84,46 +76,53 @@ PDFImage.prototype = {
       constructConvertCommandForPage: function (pageNumber) {
         var pdfFilePath = this.pdfFilePath;
         var outputImagePath = this.getOutputImagePathForPage(pageNumber);
    -    var convertOptionsString = this.constructConvertOptions();
    -    return util.format(
    -      "%s %s\"%s[%d]\" \"%s\"",
    -      this.useGM ? "gm convert" : "convert",
    -      convertOptionsString ? convertOptionsString + " " : "",
    -      pdfFilePath, pageNumber, outputImagePath
    -    );
    +    var convertOptions = this.constructConvertOptions();
    +    var args = [];
    +    if (convertOptions) args = convertOptions.slice();
    +    args.push(pdfFilePath+"["+pageNumber+"]");
    +    args.push(outputImagePath);
    +
    +    return {
    +      cmd: this.useGM ? "gm convert" : "convert",
    +      args: args
    +    };
       },
       constructCombineCommandForFile: function (imagePaths) {
    -    return util.format(
    -      "%s -append %s \"%s\"",
    -      this.useGM ? "gm convert" : "convert",
    -      imagePaths.join(' '),
    -      this.getOutputImagePathForFile()
    -    );
    +    var args = imagePaths.slice();
    +    args.push(this.getOutputImagePathForFile());
    +    args.unshift("-append");
    +    return {
    +      cmd: this.useGM ? "gm convert" : "convert",
    +      args: args
    +    };
       },
       constructConvertOptions: function () {
    -    return Object.keys(this.convertOptions).sort().map(function (optionName) {
    +    var convertOptions = [];
    +    Object.keys(this.convertOptions).sort().map(function (optionName) {
           if (this.convertOptions[optionName] !== null) {
    -        return optionName + " " + this.convertOptions[optionName];
    +        convertOptions.push(optionName);
    +        convertOptions.push(this.convertOptions[optionName]);
           } else {
    -        return optionName;
    +        convertOptions.push(optionName);
           }
    -    }, this).join(" ");
    +    }, this);
    +    return convertOptions;
       },
       combineImages: function(imagePaths) {
         var pdfImage = this;
         var combineCommand = pdfImage.constructCombineCommandForFile(imagePaths);
         return new Promise(function (resolve, reject) {
    -      exec(combineCommand, function (err, stdout, stderr) {
    -        if (err) {
    -          return reject({
    +      spawn(combineCommand.cmd, combineCommand.args, { capture: [ 'stdout', 'stderr' ]})
    +        .then(function () {
    +          spawn("rm", imagePaths); //cleanUp
    +          resolve(pdfImage.getOutputImagePathForFile());
    +        }).catch(function(error){
    +          reject({
                 message: "Failed to combine images",
    -            error: err,
    -            stdout: stdout,
    -            stderr: stderr
    +            error: error.message,
    +            stdout: error.stdout,
    +            stderr: error.stderr
               });
    -        }
    -        exec("rm "+imagePaths.join(' ')); //cleanUp
    -        return resolve(pdfImage.getOutputImagePathForFile());
           });
         });
       },
    @@ -167,16 +166,18 @@ PDFImage.prototype = {
     
         var promise = new Promise(function (resolve, reject) {
           function convertPageToImage() {
    -        exec(convertCommand, function (err, stdout, stderr) {
    -          if (err) {
    -            return reject({
    +        return new Promise(function (resolve, reject) {
    +          spawn(convertCommand.cmd, convertCommand.args, { capture: [ 'stdout', 'stderr' ]})
    +            .then(function () {
    +              resolve(outputImagePath);
    +            }).catch(function(error){
    +            reject({
                   message: "Failed to convert page to image",
    -              error: err,
    -              stdout: stdout,
    -              stderr: stderr
    +              error: error.message,
    +              stdout: error.stdout,
    +              stderr: error.stderr
                 });
    -          }
    -          return resolve(outputImagePath);
    +          });
             });
           }
     
    @@ -194,7 +195,9 @@ PDFImage.prototype = {
     
             if (imageNotExists) {
               // (1)
    -          convertPageToImage();
    +          convertPageToImage().then(function(result){
    +            resolve(result);
    +          }).catch(reject);
               return;
             }
     
    @@ -209,11 +212,10 @@ PDFImage.prototype = {
     
               if (imageFileStat.mtime < pdfFileStat.mtime) {
                 // (2)
    -            convertPageToImage();
    -            return;
    +            convertPageToImage().then(function(result){
    +              resolve(result);
    +            }).catch(reject);
               }
    -
    -          return resolve(outputImagePath);
             });
           });
         });
    
  • package.json+2 1 modified
    @@ -11,7 +11,8 @@
       },
       "homepage": "https://github.com/mooz/node-pdf-image#readme",
       "dependencies": {
    -    "es6-promise": "~4.2.4"
    +    "es6-promise": "~4.2.4",
    +    "child-process-promise": "^2.2.1"
       },
       "devDependencies": {
         "chai": "~4.1.2",
    
  • tests/test-main.js+30 7 modified
    @@ -45,19 +45,22 @@ describe("PDFImage", function () {
       });
     
       it("should return correct convert command", function () {
    -    expect(pdfImage.constructConvertCommandForPage(1))
    -      .equal('convert "/tmp/test.pdf[1]" "/tmp/test-1.png"');
    +    var convertCommand = pdfImage.constructConvertCommandForPage(1);
    +    expect(convertCommand.cmd).equal("convert");
    +    expect(convertCommand.args.length).equal(2);
       });
     
       it("should return correct convert command to combine images", function () {
    -    expect(pdfImage.constructCombineCommandForFile(['/tmp/test-0.png', '/tmp/test-1.png']))
    -      .equal('convert -append /tmp/test-0.png /tmp/test-1.png "/tmp/test.png"');
    +    var cmdConfig = pdfImage.constructCombineCommandForFile(['/tmp/test-0.png', '/tmp/test-1.png']);
    +    expect(cmdConfig.cmd).equal('convert');
    +    expect(cmdConfig.args.length).equal(4);
       });
     
       it("should use gm when you ask it to", function () {
         pdfImage = new PDFImage(pdfPath, {graphicsMagick: true});
    -    expect(pdfImage.constructConvertCommandForPage(1))
    -      .equal('gm convert "/tmp/test.pdf[1]" "/tmp/test-1.png"');
    +    var cmdConfig = pdfImage.constructConvertCommandForPage(1);
    +    expect(cmdConfig.cmd).equal('gm convert');
    +    expect(cmdConfig.args.length).equal(2);
       });
     
       // TODO: Do page updating test
    @@ -148,7 +151,27 @@ describe("PDFImage", function () {
           "-density": 300,
           "-trim": null
         });
    -    expect(pdfImage.constructConvertOptions()).equal("-density 300 -trim");
    +    expect(pdfImage.constructConvertOptions()[0]).equal("-density 300");
    +    expect(pdfImage.constructConvertOptions()[1]).equal("-trim");
    +  });
    +
    +  it("should convert all PDF's pages with convertOptions", function () {
    +    return new Promise(function(resolve, reject){
    +      pdfImage.setConvertOptions({
    +        "-quality": 100,
    +        "-trim": null
    +      });
    +
    +      pdfImage.convertFile().then(function (images) {
    +        images.forEach(function(image){
    +          expect(fs.existsSync(image)).to.be.true;
    +        });
    +        generatedFiles = images;
    +        resolve();
    +      }).catch(function (error) {
    +        reject(error.message + " " + error.stderr);
    +      });
    +    })
       });
     
       afterEach(function(done){
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.