aheckmann / gm

GraphicsMagick for node
http://aheckmann.github.com/gm/
6.96k stars 617 forks source link

Production service (nodeJS/express) crash processing a user uploaded image/file #849

Closed stavert closed 1 year ago

stavert commented 1 year ago

Hello,

About 2 hours ago, 11:02:43 PM Eastern we experienced this production service crash as follows. It appears that a user, which we're working to contact, tried to upload an image file and it killed our service to where the forever process couldn't even get it restarted.

2022-11-30 23:02:43.768 [production_api] [ERROR] 572423 services/helpers.js 121 Error: Command failed: 
    at ChildProcess.onExit (/var/www/jubi.api/node_modules/gm/lib/command.js:301:17)
    at ChildProcess.emit (node:events:527:28)
    at ChildProcess.emit (node:domain:475:12)
    at maybeClose (node:internal/child_process:1092:16)
    at Socket.<anonymous> (node:internal/child_process:451:11)
    at Socket.emit (node:events:527:28)
    at Socket.emit (node:domain:475:12)
    at Pipe.<anonymous> (node:net:709:12) {
  code: null,
  signal: 'SIGKILL'
}

As you can see there's no indication of an issue with the command which was trying to run. I can upload a corrupted/fake jpg and it will clearly report:

2022-12-01 00:46:07.981 [production_api] [ERROR] 572404 services/helpers.js 121 Error: Command failed: gm identify: No decode delegate for this image format (/tmp/upload_f9f5d5a340bab35e82479d31bcf97ea6).
gm identify: Request did not return an image.

    at ChildProcess.onExit (/var/www/jubi.api/node_modules/gm/lib/command.js:301:17)
    at ChildProcess.emit (node:events:527:28)
    at ChildProcess.emit (node:domain:475:12)
    at maybeClose (node:internal/child_process:1092:16)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:302:5) {
  code: 1,
  signal: null
}

No SIGKILL in this case and very clear reason why because the file was not really a jpg file.

The code in question does the following:

gm(file.path)
          .noProfile()
          .autoOrient()
          .write(tmpFile, function (err) {
            if (err) return handleReject(err, callback) // eslint-disable-line

            // Get the file's size
            gm(tmpFile)
              .size(function (err, size) {
                if (err) return handleReject(err, callback) // eslint-disable-line

                const tmpFileDef = {
                  phyFile: tmpFile,
                  key: util.format('%s/%s.jpg', (isSystem ? 'si' : 'ui'), key),
                  rmtFile: util.format('%s/%s.jpg', config.aws.paths.images, key),
                  size: [size.width, size.height]
                }
                tmpFiles.push(tmpFileDef)

                uploadToS3(bucket, tmpFile, tmpFileDef.rmtFile)
                  .then(function (resp) {
                    tmpFileDef.url = resp.Location
                    callback(null)
                  })
                  .catch(function (err) {
                    handleReject(err, reject) // eslint-disable-line
                  })
              })
          })

The code is basically handling user uploaded thumbnail orientations.

Our versions are:

Any known issues or advice is greatly appreciated.

stavert commented 1 year ago

I just got hold of the file used for the upload and it's an animated GIF about 27MB in size!! I'm not an image processing expert. Is this way beyond the capability of GM?

I can recreate the problem every time with the provided image. I've tried to compress it for upload here but am not able to get it down below 10MB.

stavert commented 1 year ago

So just tested the gm command itself. Obviously this is not an issue with this gm package.

rali@jubiplatform-dev:~$ gm identify -verbose animatedimagetest.gif Killed