Add limitInputPixels option to reject input #115

This commit is contained in:
Lovell Fuller 2015-01-20 14:18:05 +00:00
parent c93f79daa7
commit 8421e3aa5f
4 changed files with 108 additions and 4 deletions

View File

@ -254,6 +254,12 @@ A Promises/A+ promise is returned when `callback` is not provided.
An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`. This will reduce memory usage and can improve performance on some systems.
#### limitInputPixels(pixels)
Do not process input images where the number of pixels (width * height) exceeds this limit.
`pixels` is the integral Number of pixels, with a value between 1 and the default 268402689 (0x3FFF * 0x3FFF).
### Image transformation options
#### resize(width, [height])

View File

@ -11,6 +11,12 @@ var BluebirdPromise = require('bluebird');
var sharp = require('./build/Release/sharp');
var libvipsVersion = sharp.libvipsVersion();
var maximum = {
width: 0x3FFF,
height: 0x3FFF,
pixels: Math.pow(0x3FFF, 2)
};
var Sharp = function(input) {
if (!(this instanceof Sharp)) {
return new Sharp(input);
@ -21,6 +27,7 @@ var Sharp = function(input) {
bufferIn: null,
streamIn: false,
sequentialRead: false,
limitInputPixels: maximum.pixels,
// ICC profiles
iccProfilePath: path.join(__dirname, 'icc') + path.sep,
// resize options
@ -366,24 +373,37 @@ Sharp.prototype.resize = function(width, height) {
if (!width) {
this.options.width = -1;
} else {
if (typeof width === 'number' && !Number.isNaN(width) && width % 1 === 0 && width > 0 && width <= 0x3FFF) {
if (typeof width === 'number' && !Number.isNaN(width) && width % 1 === 0 && width > 0 && width <= maximum.width) {
this.options.width = width;
} else {
throw new Error('Invalid width ' + width);
throw new Error('Invalid width (1 to ' + maximum.width + ') ' + width);
}
}
if (!height) {
this.options.height = -1;
} else {
if (typeof height === 'number' && !Number.isNaN(height) && height % 1 === 0 && height > 0 && height <= 0x3FFF) {
if (typeof height === 'number' && !Number.isNaN(height) && height % 1 === 0 && height > 0 && height <= maximum.height) {
this.options.height = height;
} else {
throw new Error('Invalid height ' + height);
throw new Error('Invalid height (1 to ' + maximum.height + ') ' + height);
}
}
return this;
};
/*
Limit the total number of pixels for input images
Assumes the image dimensions contained in the file header can be trusted
*/
Sharp.prototype.limitInputPixels = function(limit) {
if (typeof limit === 'number' && !Number.isNaN(limit) && limit % 1 === 0 && limit > 0 && limit <= maximum.pixels) {
this.options.limitInputPixels = limit;
} else {
throw new Error('Invalid pixel limit (1 to ' + maximum.pixels + ') ' + limit);
}
return this;
};
/*
Write output image data to a file
*/

View File

@ -54,6 +54,7 @@ struct ResizeBaton {
void* bufferIn;
size_t bufferInLength;
std::string iccProfilePath;
int limitInputPixels;
std::string output;
std::string outputFormat;
void* bufferOut;
@ -94,6 +95,7 @@ struct ResizeBaton {
ResizeBaton():
bufferInLength(0),
limitInputPixels(0),
outputFormat(""),
bufferOutLength(0),
topOffsetPre(-1),
@ -178,6 +180,12 @@ class ResizeWorker : public NanAsyncWorker {
}
vips_object_local(hook, image);
// Limit input images to a given number of pixels, where pixels = width * height
if (image->Xsize * image->Ysize > baton->limitInputPixels) {
(baton->err).append("Input image exceeds pixel limit");
return Error(baton, hook);
}
// Calculate angle of rotation
Angle rotation;
bool flip;
@ -923,6 +931,8 @@ NAN_METHOD(resize) {
}
// ICC profile to use when input CMYK image has no embedded profile
baton->iccProfilePath = *String::Utf8Value(options->Get(NanNew<String>("iccProfilePath"))->ToString());
// Limit input images to a given number of pixels, where pixels = width * height
baton->limitInputPixels = options->Get(NanNew<String>("limitInputPixels"))->Int32Value();
// Extract image options
baton->topOffsetPre = options->Get(NanNew<String>("topOffsetPre"))->Int32Value();
baton->leftOffsetPre = options->Get(NanNew<String>("leftOffsetPre"))->Int32Value();

View File

@ -524,4 +524,72 @@ describe('Input/output', function() {
});
}
describe('Limit pixel count of input image', function() {
it('Invalid fails - negative', function(done) {
var isValid = false;
try {
sharp().limitInputPixels(-1);
isValid = true;
} catch (e) {}
assert(!isValid);
done();
});
it('Invalid fails - float', function(done) {
var isValid = false;
try {
sharp().limitInputPixels(12.3);
isValid = true;
} catch (e) {}
assert(!isValid);
done();
});
it('Invalid fails - huge', function(done) {
var isValid = false;
try {
sharp().limitInputPixels(Math.pow(0x3FFF, 2) + 1);
isValid = true;
} catch (e) {}
assert(!isValid);
done();
});
it('Invalid fails - string', function(done) {
var isValid = false;
try {
sharp().limitInputPixels('fail');
isValid = true;
} catch (e) {}
assert(!isValid);
done();
});
it('Same size as input works', function(done) {
sharp(fixtures.inputJpg).metadata(function(err, metadata) {
if (err) throw err;
sharp(fixtures.inputJpg)
.limitInputPixels(metadata.width * metadata.height)
.toBuffer(function(err) {
assert.strictEqual(true, !err);
done();
});
});
});
it('Smaller than input fails', function(done) {
sharp(fixtures.inputJpg).metadata(function(err, metadata) {
if (err) throw err;
sharp(fixtures.inputJpg)
.limitInputPixels(metadata.width * metadata.height - 1)
.toBuffer(function(err) {
assert.strictEqual(true, !!err);
done();
});
});
});
});
});