mongo-escape
Advanced tools
Comparing version 0.0.1 to 2.0.1
162
index.js
/** | ||
* Escapes MongoDB operators from the given "Object" and returns a new Object with | ||
* the operators escaped with "__ESCAPED_DOLLAR_SIGN__". | ||
* Copyright (c) 2014, 2016 Tim Kuijsten | ||
* | ||
* http://www.mongodb.org/display/DOCS/Legal+Key+Names | ||
* Key names in inserted documents are limited as follows: | ||
* Permission to use, copy, modify, and/or distribute this software for any | ||
* purpose with or without fee is hereby granted, provided that the above | ||
* copyright notice and this permission notice appear in all copies. | ||
* | ||
* * The '$' character must not be the first character in the key name. | ||
* * The '.' character must not appear anywhere in the key name. | ||
* | ||
* var escapedObj = mongo_escape({ $push: 'foo' }); | ||
* | ||
* console.log(escapedObj); | ||
* // { __ESCAPED_DOLLAR_SIGN__push: 'foo' } | ||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | ||
/** | ||
* Module dependencies. | ||
*/ | ||
'use strict'; | ||
var type = require('type-component'); | ||
function escaper(input) { | ||
return input.replace(/\$/g, '\uFF04').replace(/\./g, '\uFF0E'); | ||
} | ||
function unescaper(input) { | ||
return input.replace(/\uFF04/g, '$').replace(/\uFF0E/g, '.'); | ||
} | ||
/* | ||
Note: this ought to be foolproof only if [server side JavaScript is disabled], | ||
so make sure `security.javascriptEnabled` is set to `false` in your mongodb | ||
configuration file. This has the effect that the [mapReduce] command and [$where] | ||
operator can not be used since these functions allow the execution of arbitrary | ||
JavaScript code. | ||
*/ | ||
/** | ||
* Used to escape "$" signs from key names. | ||
* Ensure any input is properly escaped. Where needed `$` and `.` are replaced | ||
* with `$` and `.`, respectively. | ||
* | ||
* If input is an object, all keys are escaped. If input is not an object but a | ||
* string it is escaped as well. Otherwise return the original value. If input | ||
* is a function or a symbol an error is raised. | ||
* | ||
* Note: if input is an object, keys are replaced in place. | ||
* | ||
* @param {mixed} input input to escape | ||
* @param {Boolean, default: true} recurse whether or not to recurse | ||
* @return {mixed} properly escaped input | ||
*/ | ||
function escape(input, recurse) { | ||
if (input == null) | ||
return input; | ||
var dollar_token = '__ESCAPED_DOLLAR_SIGN__'; | ||
var dollar_regexp = /\$/g; | ||
switch (typeof input) { | ||
case 'string': | ||
return escaper(input); | ||
case 'number': | ||
case 'boolean': | ||
return input; | ||
} | ||
if (typeof recurse !== 'boolean') | ||
recurse = true; | ||
transform(input, escaper, recurse); | ||
return input; | ||
} | ||
/** | ||
* Used to escape "." symbols from key names. | ||
* Ensure any input is properly unescaped. Where needed `$` and `.` are | ||
* replaced with `$` and `.`, respectively. | ||
* | ||
* If input is an object, all keys are unescaped. If input is not an object but | ||
* a string it is unescaped as well. Otherwise return the original value. If | ||
* input is a function or a symbol an error is raised. | ||
* | ||
* Note: if input is an object, keys are replaced in place. | ||
* | ||
* @param {mixed} input input to unescape | ||
* @param {Boolean, default: true} recurse whether or not to recurse | ||
* @return {mixed} properly unescaped input | ||
*/ | ||
function unescape(input, recurse) { | ||
if (input == null) | ||
return input; | ||
var period_token = '__ESCAPED_PERIOD__'; | ||
var period_regexp = /\./g; | ||
switch (typeof input) { | ||
case 'string': | ||
return unescaper(input); | ||
case 'number': | ||
case 'boolean': | ||
return input; | ||
} | ||
if (typeof recurse !== 'boolean') | ||
recurse = true; | ||
transform(input, unescaper, recurse); | ||
return input; | ||
} | ||
/** | ||
* Escapes an Object for insersion into a MongoDB database. | ||
* source: object-key-transform npm | ||
* | ||
* @param {Object} obj The object that should be escaped. | ||
* @return {Object} The escaped object. | ||
* @api public | ||
* Iterate over all object keys (and optionally recurse) and run a transformation | ||
* on each key. Modify the object in-place. | ||
* | ||
* @param {Object} obj object to transform | ||
* @param {Function} iterator first parameter will be the key to transform, second | ||
* parameter is the value of that key (though this is | ||
* informational only). This function should return the | ||
* new key to be used. | ||
* @param {Boolean, default: false} recurse whether or not to recurse | ||
* @return {undefined} replaces keys in-place | ||
*/ | ||
function transform(obj, iterator, recurse) { | ||
if (typeof obj !== 'object') { throw new TypeError('obj must be an object'); } | ||
if (typeof iterator !== 'function') { throw new TypeError('iterator must be a function'); } | ||
exports = module.exports = function mongo_escape(obj){ | ||
var out = {}, o, k; | ||
if (!obj) return obj; | ||
for (var key in obj) { | ||
o = obj[key]; | ||
k = key.replace(dollar_regexp, dollar_token) | ||
.replace(period_regexp, period_token); | ||
switch (type(o)) { | ||
case 'array': | ||
out[k] = o.map(mongo_escape); | ||
break; | ||
case 'object': | ||
out[k] = mongo_escape(o); | ||
break; | ||
default: | ||
out[k] = o; | ||
break; | ||
recurse = recurse || false; | ||
if (typeof recurse !== 'boolean') { throw new TypeError('recurse must be a boolean'); } | ||
Object.keys(obj).forEach(function(key) { | ||
// recurse if requested and possible | ||
if (recurse && typeof obj[key] === 'object' && obj[key] !== null && Object.keys(obj[key]).length) { | ||
transform(obj[key], iterator, recurse); | ||
} | ||
} | ||
return out; | ||
}; | ||
var transformed = iterator(key, obj[key]); | ||
if (transformed !== key) { | ||
obj[transformed] = obj[key]; | ||
delete obj[key]; | ||
} | ||
}); | ||
} | ||
module.exports.escape = escape; | ||
module.exports.unescape = unescape; |
{ | ||
"name": "mongo-escape", | ||
"description": "Escapes the MongoDB operators from the given Object", | ||
"version": "0.0.1", | ||
"bundledDependencies": [ | ||
"core" | ||
"version": "2.0.1", | ||
"author": { | ||
"name": "Tim Kuijsten", | ||
"email": "tim@netsend.nl", | ||
"url": "https://github.com/timkuijsten" | ||
}, | ||
"license": "ISC", | ||
"description": "Escape variables to prevent NoSQL injection in MongoDB", | ||
"keywords": [ | ||
"mongo", | ||
"key", | ||
"escape", | ||
"sanitize", | ||
"injection" | ||
], | ||
"dependencies": { | ||
"type-component": "*" | ||
"main": "index.js", | ||
"bugs": "https://github.com/timkuijsten/node-mongo-escape/issues", | ||
"scripts": { | ||
"test": "node test" | ||
}, | ||
"component": { | ||
"scripts": ["index.js"] | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/timkuijsten/node-mongo-escape.git" | ||
} | ||
} |
125
test.js
@@ -0,8 +1,125 @@ | ||
/** | ||
* Copyright (c) 2014, 2016 Tim Kuijsten | ||
* | ||
* Permission to use, copy, modify, and/or distribute this software for any | ||
* purpose with or without fee is hereby granted, provided that the above | ||
* copyright notice and this permission notice appear in all copies. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | ||
'use strict'; | ||
var assert = require('assert'); | ||
var mongo_escape = require('./'); | ||
var input = { $push: 'foo' }; | ||
var escaped = mongo_escape(input); | ||
var escaper = require('./index'); | ||
assert.notDeepEqual(input, escaped); | ||
var obj; | ||
/* escape */ | ||
/* don't fall over undefined */ | ||
escaper.escape(obj); | ||
assert.strictEqual(obj, undefined, 'don\'t fall over undefined'); | ||
/* don't fall over null */ | ||
obj = null; | ||
escaper.escape(obj); | ||
assert.strictEqual(obj, null, 'don\'t fall over null'); | ||
/* don't fall over boolean */ | ||
obj = false; | ||
escaper.escape(obj); | ||
assert.strictEqual(obj, false, 'don\'t fall over boolean'); | ||
/* don't fall over numbers */ | ||
obj = 0; | ||
escaper.escape(obj); | ||
assert.strictEqual(obj, 0, 'don\'t fall over numbers'); | ||
/* don't fall over strings */ | ||
obj = ''; | ||
escaper.escape(obj); | ||
assert.strictEqual(obj, '', 'don\'t fall over empty string'); | ||
obj = '$set'; | ||
assert.strictEqual(escaper.escape(obj), '\uFF04set', 'escape string'); | ||
obj = {}; | ||
escaper.escape(obj); | ||
assert.deepEqual(obj, {}, 'escape empty object'); | ||
obj = { $: '$', 'foo.bar': { $: '$' } }; | ||
escaper.escape(obj, false); | ||
assert.deepEqual(obj, { '\uFF04': '$', 'foo\uFF0Ebar': { $: '$' } }, 'should not recurse'); | ||
obj = { $: '$', 'foo.bar': { $: '$' } }; | ||
escaper.escape(obj, false); | ||
escaper.escape(obj, false); | ||
assert.deepEqual(obj, { '\uFF04': '$', 'foo\uFF0Ebar': { $: '$' } }, 'should be idempotent'); | ||
obj = { $: '$', foo: { $: '$', bar: { 'some.foo': 'other' } }, a: 'b' }; | ||
escaper.escape(obj); | ||
assert.deepEqual(obj, { '\uFF04': '$', foo: { '\uFF04': '$', bar: { 'some\uFF0Efoo': 'other' } }, a: 'b' }, 'should recurse by default'); | ||
obj = [ { $: '$', 'foo.bar': { $: '$' } }, { $: [ '$', { 'foo.qux': { $: '$' } } ], 'foo.baz': { $: '$' } } ]; | ||
escaper.escape(obj); | ||
assert.deepEqual(obj, [ { '\uFF04': '$', 'foo\uFF0Ebar': { '\uFF04': '$' } }, { '\uFF04': [ '$', { 'foo\uFF0Equx': { '\uFF04': '$' } } ], 'foo\uFF0Ebaz': { '\uFF04': '$' } } ], 'should recurse on array values'); | ||
/* unescape */ | ||
/* don't fall over undefined */ | ||
obj = undefined | ||
escaper.unescape(obj); | ||
assert.strictEqual(obj, undefined, 'don\'t fall over undefined'); | ||
/* don't fall over null */ | ||
obj = null; | ||
escaper.unescape(obj); | ||
assert.strictEqual(obj, null, 'don\'t fall over null'); | ||
/* don't fall over boolean */ | ||
obj = false; | ||
escaper.unescape(obj); | ||
assert.strictEqual(obj, false, 'don\'t fall over boolean'); | ||
/* don't fall over strings */ | ||
obj = ''; | ||
escaper.unescape(obj); | ||
assert.strictEqual(obj, '', 'don\'t fall over empty string'); | ||
obj = '\uFF04set'; | ||
assert.strictEqual(escaper.unescape(obj), '$set', 'unescape string'); | ||
obj = {}; | ||
escaper.unescape(obj); | ||
assert.deepEqual(obj, {}, 'unescape empty object'); | ||
obj = { 'a': 'b' }; | ||
escaper.unescape(obj, true); | ||
assert.deepEqual(obj, { 'a': 'b' }, 'should return original object'); | ||
obj = { '\uFF04': '$', 'foo\uFF0Ebar': { $: '$' } }; | ||
escaper.unescape(obj, false); | ||
assert.deepEqual(obj, { $: '$', 'foo.bar': { $: '$' } }, 'should not recurse'); | ||
obj = { '\uFF04': '$', 'foo\uFF0Ebar': { $: '$' } }; | ||
escaper.unescape(obj, false); | ||
escaper.unescape(obj, false); | ||
assert.deepEqual(obj, { $: '$', 'foo.bar': { $: '$' } }, 'should be idempotent'); | ||
obj = { '\uFF04': '$', foo: { '\uFF04': '$', bar: { 'some\uFF0Efoo': 'other' } }, a: 'b' }; | ||
escaper.unescape(obj); | ||
assert.deepEqual(obj, { $: '$', foo: { $: '$', bar: { 'some.foo': 'other' } }, a: 'b' }, 'should recurse by default'); | ||
obj = [ { '\uFF04': '$', 'foo\uFF0Ebar': { '\uFF04': '$' } }, { '\uFF04': [ '$', { 'foo\uFF0Equx': { '\uFF04': '$' } } ], 'foo\uFF0Ebaz': { '\uFF04': '$' } } ]; | ||
escaper.unescape(obj); | ||
assert.deepEqual(obj, [ { $: '$', 'foo.bar': { $: '$' } }, { $: [ '$', { 'foo.qux': { $: '$' } } ], 'foo.baz': { $: '$' } } ], 'should recurse on array values'); | ||
console.log('ok'); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
17360
0
8
0
316
2
1
0
101
0
2
- Removedtype-component@*
- Removedtype-component@0.0.1(transitive)