🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
Book a DemoInstallSign in
Socket

express-mongo-sanitize

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

express-mongo-sanitize - npm Package Compare versions

Comparing version

to
1.2.0

6

CHANGELOG.md

@@ -5,2 +5,7 @@ # Change Log

## [1.2.0] - 2016-01-13
### Added
- A new option `replaceWith` which can be used to replace offending characters in a key. This is an alternative to removing the data from the payload.
## [1.1.0] - 2016-01-13

@@ -14,2 +19,3 @@ ### Added

[1.2.0]: https://github.com/fiznool/express-mongo-sanitize/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/fiznool/express-mongo-sanitize/compare/v1.0.0...v1.1.0

53

index.js
'use strict';
var sanitize = function(val) {
if(Array.isArray(val)) {
val.forEach(sanitize);
var TEST_REGEX = /^\$|\./,
REPLACE_REGEX = /^\$|\./g;
} else if(val instanceof Object) {
Object.keys(val).forEach(function(key) {
if (/^\$|\./.test(key)) {
delete val[key];
} else {
sanitize(val[key]);
}
});
var sanitize = function(val, options) {
options = options || {};
var replaceWith = null;
if(!(TEST_REGEX.test(options.replaceWith))) {
replaceWith = options.replaceWith;
}
return val;
var act = function(val) {
if(Array.isArray(val)) {
val.forEach(act);
} else if(val instanceof Object) {
Object.keys(val).forEach(function(key) {
var v = val[key];
var noRecurse = false;
if(TEST_REGEX.test(key)) {
delete val[key];
if(replaceWith) {
val[key.replace(REPLACE_REGEX, replaceWith)] = v;
} else {
noRecurse = true;
}
}
if(!noRecurse) {
act(v);
}
});
}
return val;
};
return act(val);
};
var middleware = function(options) {
options = options || {};
return function(req, res, next) {
['body', 'params', 'query'].forEach(function(k) {
if(req[k]) {
req[k] = sanitize(req[k]);
req[k] = sanitize(req[k], options);
}

@@ -28,0 +51,0 @@ });

{
"name": "express-mongo-sanitize",
"version": "1.1.0",
"version": "1.2.0",
"description": "Sanitize your express payload to prevent MongoDB operator injection.",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -24,4 +24,11 @@ # Express Mongoose Sanitize

app.use(bodyParser.json());
// To remove data, use:
app.use(mongoSanitize());
// Or, to replace prohibited characters with _, use:
app.use(mongoSanitize({
replaceWith: '_'
}))
```

@@ -31,9 +38,16 @@

This module removes any keys in objects that begin with a `$` sign from `req.body`, `req.query` or `req.params`.
This module searches for any keys in objects that begin with a `$` sign or contain a `.`, from `req.body`, `req.query` or `req.params`. It can then either:
- completely remove these keys and associated data from the object, or
- replace the prohibited characters with another allowed character.
The behaviour is governed by the passed option, `replaceWith`. Set this option to have the sanitizer replace the prohibited characters with the character passed in.
See the spec file for more examples.
## Why?
Object keys starting with a `$` are _reserved_ for use by MongoDB as operators. Without this sanitization, malicious users could send an object containing a `$` operator, which could change the context of a database operation. Most notorious is the `$where` operator, which can execute arbitrary JavaScript on the database.
Object keys starting with a `$` or containing a `.` are _reserved_ for use by MongoDB as operators. Without this sanitization, malicious users could send an object containing a `$` operator, or including a `.`, which could change the context of a database operation. Most notorious is the `$where` operator, which can execute arbitrary JavaScript on the database.
The best way to prevent this is to sanitize the received data, and remove any offending keys.
The best way to prevent this is to sanitize the received data, and remove any offending keys, or replace the characters with a 'safe' one.

@@ -40,0 +54,0 @@ ## Credits

@@ -9,121 +9,361 @@ 'use strict';

describe('Express Mongo Sanitize', function() {
var app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(sanitize());
describe('Remove Data', function() {
var app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(sanitize());
app.post('/body', function(req, res){
res.status(200).json({
body: req.body
app.post('/body', function(req, res){
res.status(200).json({
body: req.body
});
});
});
app.get('/query', function(req, res){
res.status(200).json({
query: req.query
app.get('/query', function(req, res){
res.status(200).json({
query: req.query
});
});
});
describe('Top-level object', function() {
it('should sanitize the query string', function(done) {
request(app)
.get('/query?q=search&$where=malicious&dotted.data=some_data')
.set('Accept', 'application/json')
.expect(200, {
query: {
q: 'search'
}
}, done);
});
describe('Top-level object', function() {
it('should sanitize the query string', function(done) {
request(app)
.get('/query?q=search&$where=malicious&dotted.data=some_data')
.set('Accept', 'application/json')
.expect(200, {
query: {
q: 'search'
}
}, done);
});
it('should sanitize a JSON body', function(done) {
request(app)
.post('/body')
.send({
q: 'search',
is: true,
and: 1,
even: null,
stop: undefined,
$where: 'malicious',
'dotted.data': 'some_data'
})
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect(200, {
body: {
it('should sanitize a JSON body', function(done) {
request(app)
.post('/body')
.send({
q: 'search',
is: true,
and: 1,
even: null
}
}, done);
even: null,
stop: undefined,
$where: 'malicious',
'dotted.data': 'some_data'
})
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect(200, {
body: {
q: 'search',
is: true,
and: 1,
even: null
}
}, done);
});
it('should sanitize a form url-encoded body', function(done) {
request(app)
.post('/body')
.send('q=search&$where=malicious&dotted.data=some_data')
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('Accept', 'application/json')
.expect(200, {
body: {
q: 'search'
}
}, done);
});
});
it('should sanitize a form url-encoded body', function(done) {
request(app)
.post('/body')
.send('q=search&$where=malicious&dotted.data=some_data')
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('Accept', 'application/json')
.expect(200, {
body: {
q: 'search'
}
}, done);
describe('Nested Object', function() {
it('should sanitize a nested object in the query string', function(done) {
request(app)
.get('/query?username[$gt]=foo&username[dotted.data]=some_data')
.set('Accept', 'application/json')
.expect(200, {
query: {
username: {}
}
}, done);
});
it('should sanitize a nested object in a JSON body', function(done) {
request(app)
.post('/body')
.send({
username: {
$gt: 'foo',
'dotted.data': 'some_data'
}
})
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect(200, {
body: {
username: {}
}
}, done);
});
it('should sanitize a nested object in a form url-encoded body', function(done) {
request(app)
.post('/body')
.send('username[$gt]=foo&username[dotted.data]=some_data')
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('Accept', 'application/json')
.expect(200, {
body: {
username: {}
}
}, done);
});
});
describe('Nested Object inside an Array', function() {
it('should sanitize a nested object in the query string', function(done) {
request(app)
.get('/query?username[0][$gt]=foo&username[0][dotted.data]=some_data')
.set('Accept', 'application/json')
.expect(200, {
query: {
username: [{}]
}
}, done);
});
it('should sanitize a nested object in a JSON body', function(done) {
request(app)
.post('/body')
.send({
username: [{
$gt: 'foo',
'dotted.data': 'some_data'
}]
})
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect(200, {
body: {
username: [{}]
}
}, done);
});
it('should sanitize a nested object in a form url-encoded body', function(done) {
request(app)
.post('/body')
.send('username[0][$gt]=foo&username[0][dotted.data]=some_data')
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('Accept', 'application/json')
.expect(200, {
body: {
username: [{}]
}
}, done);
});
});
});
describe('Nested Object', function() {
it('should sanitize a nested object in the query string', function(done) {
request(app)
.get('/query?username[$gt]=foo&username[dotted.data]=some_data')
.set('Accept', 'application/json')
.expect(200, {
query: {
username: {}
}
}, done);
describe('Preserve Data', function() {
var app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(sanitize({
replaceWith: '_'
}));
app.post('/body', function(req, res){
res.status(200).json({
body: req.body
});
});
it('should sanitize a nested object in a JSON body', function(done) {
request(app)
.post('/body')
.send({
username: {
$gt: 'foo',
app.get('/query', function(req, res){
res.status(200).json({
query: req.query
});
});
describe('Top-level object', function() {
it('should sanitize the query string', function(done) {
request(app)
.get('/query?q=search&$where=malicious&dotted.data=some_data')
.set('Accept', 'application/json')
.expect(200, {
query: {
q: 'search',
_where: 'malicious',
dotted_data: 'some_data'
}
}, done);
});
it('should sanitize a JSON body', function(done) {
request(app)
.post('/body')
.send({
q: 'search',
is: true,
and: 1,
even: null,
stop: undefined,
$where: 'malicious',
'dotted.data': 'some_data'
}
})
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect(200, {
body: {
username: {}
}
}, done);
})
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect(200, {
body: {
q: 'search',
is: true,
and: 1,
even: null,
_where: 'malicious',
dotted_data: 'some_data'
}
}, done);
});
it('should sanitize a form url-encoded body', function(done) {
request(app)
.post('/body')
.send('q=search&$where=malicious&dotted.data=some_data')
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('Accept', 'application/json')
.expect(200, {
body: {
q: 'search',
_where: 'malicious',
dotted_data: 'some_data'
}
}, done);
});
});
it('should sanitize a nested object in a form url-encoded body', function(done) {
request(app)
.post('/body')
.send('username[$gt]=foo&username[dotted.data]=some_data')
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('Accept', 'application/json')
.expect(200, {
body: {
username: {}
}
}, done);
describe('Nested Object', function() {
it('should sanitize a nested object in the query string', function(done) {
request(app)
.get('/query?username[$gt]=foo&username[dotted.data]=some_data')
.set('Accept', 'application/json')
.expect(200, {
query: {
username: {
_gt: 'foo',
dotted_data: 'some_data'
}
}
}, done);
});
it('should sanitize a nested object in a JSON body', function(done) {
request(app)
.post('/body')
.send({
username: {
$gt: 'foo',
'dotted.data': 'some_data'
}
})
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect(200, {
body: {
username: {
_gt: 'foo',
dotted_data: 'some_data'
}
}
}, done);
});
it('should sanitize a nested object in a form url-encoded body', function(done) {
request(app)
.post('/body')
.send('username[$gt]=foo&username[dotted.data]=some_data')
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('Accept', 'application/json')
.expect(200, {
body: {
username: {
_gt: 'foo',
dotted_data: 'some_data'
}
}
}, done);
});
});
describe('Nested Object inside an Array', function() {
it('should sanitize a nested object in the query string', function(done) {
request(app)
.get('/query?username[0][$gt]=foo&username[0][dotted.data]=some_data')
.set('Accept', 'application/json')
.expect(200, {
query: {
username: [{
_gt: 'foo',
dotted_data: 'some_data'
}]
}
}, done);
});
it('should sanitize a nested object in a JSON body', function(done) {
request(app)
.post('/body')
.send({
username: [{
$gt: 'foo',
'dotted.data': 'some_data'
}]
})
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect(200, {
body: {
username: [{
_gt: 'foo',
dotted_data: 'some_data'
}]
}
}, done);
});
it('should sanitize a nested object in a form url-encoded body', function(done) {
request(app)
.post('/body')
.send('username[0][$gt]=foo&username[0][dotted.data]=some_data')
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('Accept', 'application/json')
.expect(200, {
body: {
username: [{
_gt: 'foo',
dotted_data: 'some_data'
}]
}
}, done);
});
});
});
describe('Nested Object inside an Array', function() {
it('should sanitize a nested object in the query string', function(done) {
request(app)
.get('/query?username[0][$gt]=foo&username[0][dotted.data]=some_data')
describe('Preserve Data: prohibited characters', function() {
it('should not allow data to be replaced with a `$`', function(done) {
var app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(sanitize({
replaceWith: '$'
}));
app.get('/query', function(req, res){
res.status(200).json({
query: req.query
});
});
request(app)
.get('/query?q=search&$where=malicious&dotted.data=some_data')
.set('Accept', 'application/json')
.expect(200, {
query: {
username: [{}]
q: 'search'
}

@@ -133,29 +373,20 @@ }, done);

it('should sanitize a nested object in a JSON body', function(done) {
request(app)
.post('/body')
.send({
username: [{
$gt: 'foo',
'dotted.data': 'some_data'
}]
})
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect(200, {
body: {
username: [{}]
}
}, done);
});
it('should not allow data to be replaced with a `.`', function(done) {
var app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(sanitize({
replaceWith: '.'
}));
it('should sanitize a nested object in a form url-encoded body', function(done) {
request(app)
.post('/body')
.send('username[0][$gt]=foo&username[0][dotted.data]=some_data')
.set('Content-Type', 'application/x-www-form-urlencoded')
app.get('/query', function(req, res){
res.status(200).json({
query: req.query
});
});
request(app)
.get('/query?q=search&$where=malicious&dotted.data=some_data')
.set('Accept', 'application/json')
.expect(200, {
body: {
username: [{}]
query: {
q: 'search'
}

@@ -162,0 +393,0 @@ }, done);