Skip to content

Commit

Permalink
Add parameter validation for all the operations supported by sharp. P…
Browse files Browse the repository at this point in the history
…art of #4.
  • Loading branch information
papandreou committed Mar 14, 2016
1 parent 6c4bd1e commit 04fefde
Show file tree
Hide file tree
Showing 3 changed files with 325 additions and 8 deletions.
295 changes: 294 additions & 1 deletion lib/getFilterInfosAndTargetContentTypeFromQueryString.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,299 @@ Object.keys(isOperationByEngineNameAndName).forEach(function (engineName) {
});
});

function isNumberWithin(num, min, max) {
return typeof num === 'number' && num >= min && num <= max;
}

function isValidOperation(name, args) {
switch (name) {
case 'crop':
return args.length === 1 && /^(?:east|west|center|north(?:|west|east)|south(?:|west|east))/.test(args[0]);
case 'rotate':
return args.length === 0 || (args.length === 1 && (args[0] === 0 || args[0] === 90 || args[0] === 180 || args[0] === 270));
case 'resize':
return args.length === 2 && isNumberWithin(args[0], 1, 16384) && isNumberWithin(args[1], 1, 16384);
case 'extract':
return args.length === 4 && isNumberWithin(args[0], 1, 16384) && isNumberWithin(args[1], 1, 16384) && isNumberWithin(args[2], 1, 16384) && isNumberWithin(args[3], 1, 16384);
case 'interpolateWith':
return args.length === 1 && /^(?:nearest|bilinear|vertexSplitQuadraticBasisSpline|bicubic|locallyBoundedBicubic|nohalo)$/.test(args[0]);
case 'background':
return args.length === 1 && /^#[0-9a-f]{6}$/.test(args[0]);
case 'blur':
return args.length === 0 || (args.length === 1 && isNumberWithin(args[0], 0.3, 1000));
case 'sharpen':
return args.length <= 3 &&
(typeof args[0] === 'undefined' || typeof args[0] === 'number') &&
(typeof args[1] === 'undefined' || typeof args[1] === 'number') &&
(typeof args[2] === 'undefined' || typeof args[2] === 'number');
case 'threshold':
return args.length === 0 || (args.length === 1 && isNumberWithin(args[0], 0, 255));
case 'gamma':
return args.length === 0 || (args.length === 1 && isNumberWithin(args[0], 1, 3));
case 'quality':
return args.length === 1 && isNumberWithin(args[0], 1, 100);
case 'tile':
return args.length <= 2 &&
(typeof args[0] === 'undefined' || isNumberWithin(args[0], 1, 8192)) &&
(typeof args[1] === 'undefined' || isNumberWithin(args[0], 0, 8192));
case 'compressionLevel':
return args.length === 1 && isNumberWithin(args[0], 0, 9);
case 'png':
case 'jpeg':
case 'gif':
case 'webp':
case 'withoutEnlargement':
case 'progressive':
case 'metadata':
case 'ignoreAspectRatio':
case 'embed':
case 'max':
case 'min':
case 'negate':
case 'flatten':
case 'flip':
case 'flop':
case 'grayscale':
case 'greyscale':
case 'normalize':
case 'withMetadata':
case 'withoutChromaSubsampling':
case 'withoutAdaptiveFiltering':
case 'trellisQuantization':
case 'trellisQuantisation':
case 'overshootDeringing':
case 'optimizeScans':
case 'optimiseScans':
return args.length === 0;
// Not supported: overlayWith

// Engines:
case 'sharp':
case 'gm':
return args.length === 0;

// FIXME: Add validation code for all the below.
// https://github.com/papandreou/express-processimage/issues/4
// Other engines:
case 'pngcrush':
case 'pngquant':
case 'jpegtran':
case 'optipng':
case 'svgfilter':
case 'inkscape':
return true;

// Graphicsmagick specific operations:
// FIXME: Add validation code for all the below.
// https://github.com/papandreou/express-processimage/issues/4
case 'setFormat':
case 'identify':
case 'selectFrame':
case 'subCommand':
case 'adjoin':
case 'affine':
case 'alpha':
case 'append':
case 'authenticate':
case 'average':
case 'backdrop':
case 'blackThreshold':
case 'bluePrimary':
case 'border':
case 'borderColor':
case 'box':
case 'channel':
case 'chop':
case 'clip':
case 'coalesce':
case 'colorize':
case 'colorMap':
case 'compose':
case 'compress':
case 'convolve':
case 'createDirectories':
case 'deconstruct':
case 'define':
case 'delay':
case 'displace':
case 'display':
case 'dispose':
case 'dissolve':
case 'encoding':
case 'endian':
case 'file':
case 'flatten':
case 'foreground':
case 'frame':
case 'fuzz':
case 'gaussian':
case 'geometry':
case 'greenPrimary':
case 'highlightColor':
case 'highlightStyle':
case 'iconGeometry':
case 'intent':
case 'lat':
case 'level':
case 'list':
case 'log':
case 'loop':
case 'map':
case 'mask':
case 'matte':
case 'matteColor':
case 'maximumError':
case 'mode':
case 'monitor':
case 'mosaic':
case 'motionBlur':
case 'name':
case 'noop':
case 'normalize':
case 'opaque':
case 'operator':
case 'orderedDither':
case 'outputDirectory':
case 'page':
case 'pause':
case 'pen':
case 'ping':
case 'pointSize':
case 'preview':
case 'process':
case 'profile':
case 'progress':
case 'randomThreshold':
case 'recolor':
case 'redPrimary':
case 'remote':
case 'render':
case 'repage':
case 'sample':
case 'samplingFactor':
case 'scene':
case 'scenes':
case 'screen':
case 'set':
case 'segment':
case 'shade':
case 'shadow':
case 'sharedMemory':
case 'shave':
case 'shear':
case 'silent':
case 'rawSize':
case 'snaps':
case 'stegano':
case 'stereo':
case 'textFont':
case 'texture':
case 'threshold':
case 'thumbnail':
case 'tile':
case 'title':
case 'transform':
case 'transparent':
case 'treeDepth':
case 'update':
case 'units':
case 'unsharp':
case 'usePixmap':
case 'view':
case 'virtualPixel':
case 'visual':
case 'watermark':
case 'wave':
case 'whitePoint':
case 'whiteThreshold':
case 'window':
case 'windowGroup':
case 'strip':
case 'interlace':
case 'setFormat':
case 'resizeExact':
case 'scale':
case 'filter':
case 'density':
case 'noProfile':
case 'resample':
case 'rotate':
case 'magnify':
case 'minify':
case 'quality':
case 'charcoal':
case 'modulate':
case 'antialias':
case 'bitdepth':
case 'colors':
case 'colorspace':
case 'comment':
case 'contrast':
case 'cycle':
case 'despeckle':
case 'dither':
case 'monochrome':
case 'edge':
case 'emboss':
case 'enhance':
case 'equalize':
case 'gamma':
case 'implode':
case 'label':
case 'limit':
case 'median':
case 'negative':
case 'noise':
case 'paint':
case 'raise':
case 'lower':
case 'region':
case 'roll':
case 'sharpen':
case 'solarize':
case 'spread':
case 'swirl':
case 'type':
case 'trim':
case 'extent':
case 'gravity':
case 'background':
case 'fill':
case 'stroke':
case 'strokeWidth':
case 'font':
case 'fontSize':
case 'draw':
case 'drawPoint':
case 'drawLine':
case 'drawRectangle':
case 'drawArc':
case 'drawEllipse':
case 'drawCircle':
case 'drawPolyline':
case 'drawPolygon':
case 'drawBezier':
case 'drawText':
case 'setDraw':
case 'thumb':
case 'thumbExact':
case 'morph':
case 'sepia':
case 'autoOrient':
case 'in':
case 'out':
case 'preprocessor':
case 'addSrcFormatter':
case 'inputIs':
case 'compare':
case 'composite':
case 'montage':
return true;
default:
return false;
}
}

module.exports = function getFilterInfosAndTargetContentTypeFromQueryString(queryString, options) {
options = options || {};
var filters = options.filters || {},
Expand Down Expand Up @@ -299,7 +592,7 @@ module.exports = function getFilterInfosAndTargetContentTypeFromQueryString(quer
}
}) : [];

if (typeof options.allowOperation === 'function' && !options.allowOperation(operationName, operationArgs)) {
if (!isValidOperation(operationName, operationArgs) || (typeof options.allowOperation === 'function' && !options.allowOperation(operationName, operationArgs))) {
leftOverQueryStringFragments.push(keyValuePair);
} else {
var filterInfo;
Expand Down
38 changes: 31 additions & 7 deletions test/processImage.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,14 +311,14 @@ describe('express-processimage', function () {
});

it('should allow an operation for which allowOperation returns true', function () {
return expect('GET /turtle.jpg?resize=87', 'to yield response', {
return expect('GET /turtle.jpg?resize=87,100', 'to yield response', {
headers: {
'Content-Type': 'image/jpeg'
},
body: expect.it('to have metadata satisfying', { size: { width: 87 } })
}).then(function () {
expect(config.allowOperation, 'to have calls satisfying', function () {
config.allowOperation('resize', [87]);
config.allowOperation('resize', [ 87, 100 ]);
});
});
});
Expand Down Expand Up @@ -357,7 +357,7 @@ describe('express-processimage', function () {
});

it('should allow retrieving the image metadata for the result of an operation', function () {
return expect('GET /turtle.jpg?png&greyscale&resize=10&metadata', 'to yield response', {
return expect('GET /turtle.jpg?png&greyscale&resize=10,9&metadata', 'to yield response', {
body: {
width: 10,
height: 9,
Expand Down Expand Up @@ -459,7 +459,7 @@ describe('express-processimage', function () {

it('should use sharp when a gif is converted to png', function () {
config.debug = true;
return expect('GET /animated.gif?resize=40&png', 'to yield response', {
return expect('GET /animated.gif?resize=40,100&png', 'to yield response', {
headers: {
'X-Express-Processimage': 'sharp'
},
Expand All @@ -485,9 +485,14 @@ describe('express-processimage', function () {
});
});

it('should return an error when an invalid syntax is used for a parameter value', function () {
return expect('GET /turtle.jpg?resize=100%22', 'to yield response', {
errorPassedToNext: { statusCode: 400 }
it('should ignore invalid operations', function () {
return expect('GET /turtle.jpg?resize=10%22', 'to yield response', {
body: expect.it('to have metadata satisfying', {
size: {
width: 481,
height: 424
}
})
});
});
});
Expand Down Expand Up @@ -663,4 +668,23 @@ describe('express-processimage', function () {
});
});
});

describe('with invalid parameters', function () {
[
'resize=foo,100', 'resize=', 'resize=100,200,300', 'resize=0,0', 'resize=-1,-1', 'resize=32000,32000',
'crop=foo', 'crop=', 'crop=north,south',
'extract=', 'extract=1,2,3,4,5', 'extract=32000,32000,32000,32000',
'rotate=95', 'rotate=90,270',
'png=hey',
'interpolateWith=something'
].forEach(function (invalidOperation) {
it('disallows an operation of ' + invalidOperation, function () {
return expect('GET /testImage.png?' + invalidOperation, 'to yield response', {
body: expect.it('to have metadata satisfying', {
size: { width: 12, height: 5 }
})
});
});
});
});
});
Binary file added testdata/testImage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 04fefde

Please sign in to comment.