Package: release.debian.org Severity: normal Tags: bullseye User: release.debian....@packages.debian.org Usertags: pu
[ Reason ] node-object-path is vulnerable to prototye pollution (CVE-2021-23434 and CVE-2021-3805 [ Impact ] Medium vulnerability [ Tests ] Test passed with these patches, including new checks [ Risks ] Low risk, package is not really different than the one pushed to unstable (only doc differs). [ Checklist ] [X] *all* changes are documented in the d/changelog [X] I reviewed all changes and I approve them [X] attach debdiff against the package in (old)stable [X] the issue is verified as fixed in unstable [ Changes ] Better checks [ Other info ] Note that we could upload a 0.11.8-1~deb11u1: there is no differences except a documentation update. If you agree, I prefer this way. Cheers, Yadd
diff --git a/debian/changelog b/debian/changelog index f1e6929..ce9339e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +node-object-path (0.11.5-3+deb11u1) bullseye; urgency=medium + + * Team upload + * Fix prototype pollution (Closes: CVE-2021-23434) + * Fix prototype pollution (Closes: CVE-2021-3805) + + -- Yadd <y...@debian.org> Fri, 17 Sep 2021 18:38:10 +0200 + node-object-path (0.11.5-3) unstable; urgency=medium * Team upload diff --git a/debian/gbp.conf b/debian/gbp.conf index b713356..e11bcb5 100644 --- a/debian/gbp.conf +++ b/debian/gbp.conf @@ -1,5 +1,6 @@ [DEFAULT] pristine-tar = True +debian-branch = bullseye [import-orig] filter = [ '.gitignore', '.travis.yml', '.git*' ] diff --git a/debian/patches/CVE-2021-23434.patch b/debian/patches/CVE-2021-23434.patch new file mode 100644 index 0000000..8d08d2e --- /dev/null +++ b/debian/patches/CVE-2021-23434.patch @@ -0,0 +1,67 @@ +Description: Fix prototype pollution when path components are not strings +Author: Mario Casciaro <mariocasci...@gmail.com +Origin: upstream, https://github.com/mariocasciaro/object-path/commit/7bdf4abef +Bug: https://snyk.io/vuln/SNYK-JS-OBJECTPATH-1569453 +Forwarded: not-needed +Reviewed-By: Yadd <y...@debian.org> +Last-Update: 2021-09-17 + +--- a/index.js ++++ b/index.js +@@ -111,6 +111,9 @@ + return set(obj, path.split('.').map(getKey), value, doNotReplace); + } + var currentPath = path[0]; ++ if (typeof currentPath !== 'string' && typeof currentPath !== 'number') { ++ currentPath = String(currentPath) ++ } + var currentValue = getShallowProperty(obj, currentPath); + if (options.includeInheritedProps && (currentPath === '__proto__' || + (currentPath === 'constructor' && typeof currentValue === 'function'))) { +--- a/test.js ++++ b/test.js +@@ -241,12 +241,18 @@ + objectPath.set({}, '__proto__.injected', 'this is bad') + expect(Object.prototype.injected).to.be.undefined + ++ objectPath.set({}, [['__proto__'], 'injected'], 'this is bad') ++ expect(Object.prototype.injected).to.be.undefined ++ + function Clazz() {} + Clazz.prototype.test = 'original' + + objectPath.set(new Clazz(), '__proto__.test', 'this is bad') + expect(Clazz.prototype.test).to.be.equal('original') + ++ objectPath.set(new Clazz(), [['__proto__'], 'test'], 'this is bad') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ + objectPath.set(new Clazz(), 'constructor.prototype.test', 'this is bad') + expect(Clazz.prototype.test).to.be.equal('original') + }) +@@ -256,6 +262,11 @@ + .to.throw('For security reasons') + expect(Object.prototype.injected).to.be.undefined + ++ expect(function() { ++ objectPath.withInheritedProps.set({}, [['__proto__'], 'injected'], 'this is bad') ++ expect(Object.prototype.injected).to.be.undefined ++ }).to.throw('For security reasons') ++ + function Clazz() {} + Clazz.prototype.test = 'original' + +@@ -267,8 +278,11 @@ + .to.throw('For security reasons') + expect(Clazz.prototype.test).to.be.equal('original') + +- const obj = {} +- expect(function() {objectPath.withInheritedProps.set(obj, 'constructor.prototype.injected', 'this is OK')}) ++ expect(function() {objectPath.withInheritedProps.set({}, 'constructor.prototype.injected', 'this is OK')}) ++ .to.throw('For security reasons') ++ expect(Object.prototype.injected).to.be.undefined ++ ++ expect(function() {objectPath.withInheritedProps.set({}, [['constructor'], 'prototype', 'injected'], 'this is bad')}) + .to.throw('For security reasons') + expect(Object.prototype.injected).to.be.undefined + }) diff --git a/debian/patches/CVE-2021-3805.patch b/debian/patches/CVE-2021-3805.patch new file mode 100644 index 0000000..daa56ff --- /dev/null +++ b/debian/patches/CVE-2021-3805.patch @@ -0,0 +1,837 @@ +Description: Fix prototype pollution vulnerability +Author: Mario Casciaro <mariocasci...@gmail.com> +Origin: upstream, https://github.com/mariocasciaro/object-path/commit/4f0903fd7 +Bug: https://huntr.dev/bounties/571e3baf-7c46-46e3-9003-ba7e4e623053 +Forwarded: not-needed +Reviewed-By: Yadd <y...@debian.org> +Last-Update: 2021-09-17 + +--- a/README.md ++++ b/README.md +@@ -172,6 +172,8 @@ + objectPath.set(obj, 'notOwn.prop', 'b'); + ``` + ++**NOTE**: For security reasons `object-path` will throw an exception when trying to access an object's magic properties (e.g. `__proto__`, `constructor`) when in "inherited props" mode. ++ + ### Immutability + + If you are looking for an *immutable* alternative of this library, you can take a look at: [object-path-immutable](https://github.com/mariocasciaro/object-path-immutable) +--- a/index.js ++++ b/index.js +@@ -1,87 +1,88 @@ +-(function (root, factory){ +- 'use strict'; ++(function (root, factory) { ++ 'use strict' + + /*istanbul ignore next:cant test*/ + if (typeof module === 'object' && typeof module.exports === 'object') { +- module.exports = factory(); ++ module.exports = factory() + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. +- define([], factory); ++ define([], factory) + } else { + // Browser globals +- root.objectPath = factory(); ++ root.objectPath = factory() + } +-})(this, function(){ +- 'use strict'; ++})(this, function () { ++ 'use strict' + +- var toStr = Object.prototype.toString; +- function hasOwnProperty(obj, prop) { +- if(obj == null) { ++ var toStr = Object.prototype.toString ++ ++ function hasOwnProperty (obj, prop) { ++ if (obj == null) { + return false + } + //to handle objects with null prototypes (too edge case?) + return Object.prototype.hasOwnProperty.call(obj, prop) + } + +- function isEmpty(value){ ++ function isEmpty (value) { + if (!value) { +- return true; ++ return true + } + if (isArray(value) && value.length === 0) { +- return true; ++ return true + } else if (typeof value !== 'string') { +- for (var i in value) { +- if (hasOwnProperty(value, i)) { +- return false; +- } ++ for (var i in value) { ++ if (hasOwnProperty(value, i)) { ++ return false + } +- return true; ++ } ++ return true + } +- return false; ++ return false + } + +- function toString(type){ +- return toStr.call(type); ++ function toString (type) { ++ return toStr.call(type) + } + +- function isObject(obj){ +- return typeof obj === 'object' && toString(obj) === "[object Object]"; ++ function isObject (obj) { ++ return typeof obj === 'object' && toString(obj) === '[object Object]' + } + +- var isArray = Array.isArray || function(obj){ ++ var isArray = Array.isArray || function (obj) { + /*istanbul ignore next:cant test*/ +- return toStr.call(obj) === '[object Array]'; ++ return toStr.call(obj) === '[object Array]' + } + +- function isBoolean(obj){ +- return typeof obj === 'boolean' || toString(obj) === '[object Boolean]'; ++ function isBoolean (obj) { ++ return typeof obj === 'boolean' || toString(obj) === '[object Boolean]' + } + +- function getKey(key){ +- var intKey = parseInt(key); ++ function getKey (key) { ++ var intKey = parseInt(key) + if (intKey.toString() === key) { +- return intKey; ++ return intKey + } +- return key; ++ return key + } + +- function factory(options) { ++ function factory (options) { + options = options || {} + +- var objectPath = function(obj) { +- return Object.keys(objectPath).reduce(function(proxy, prop) { +- if(prop === 'create') { +- return proxy; ++ var objectPath = function (obj) { ++ return Object.keys(objectPath).reduce(function (proxy, prop) { ++ if (prop === 'create') { ++ return proxy + } + + /*istanbul ignore else*/ + if (typeof objectPath[prop] === 'function') { +- proxy[prop] = objectPath[prop].bind(objectPath, obj); ++ proxy[prop] = objectPath[prop].bind(objectPath, obj) + } + +- return proxy; +- }, {}); +- }; ++ return proxy ++ }, {}) ++ } + + var hasShallowProperty + if (options.includeInheritedProps) { +@@ -94,213 +95,226 @@ + } + } + +- function getShallowProperty(obj, prop) { ++ function getShallowProperty (obj, prop) { + if (hasShallowProperty(obj, prop)) { +- return obj[prop]; ++ return obj[prop] ++ } ++ } ++ ++ var getShallowPropertySafely ++ if (options.includeInheritedProps) { ++ getShallowPropertySafely = function (obj, currentPath) { ++ if (typeof currentPath !== 'string' && typeof currentPath !== 'number') { ++ currentPath = String(currentPath) ++ } ++ var currentValue = getShallowProperty(obj, currentPath) ++ if (currentPath === '__proto__' || currentPath === 'prototype' || ++ (currentPath === 'constructor' && typeof currentValue === 'function')) { ++ throw new Error('For security reasons, object\'s magic properties cannot be set') ++ } ++ return currentValue ++ } ++ } else { ++ getShallowPropertySafely = function (obj, currentPath) { ++ return getShallowProperty(obj, currentPath) + } + } + +- function set(obj, path, value, doNotReplace){ ++ function set (obj, path, value, doNotReplace) { + if (typeof path === 'number') { +- path = [path]; ++ path = [path] + } + if (!path || path.length === 0) { +- return obj; ++ return obj + } + if (typeof path === 'string') { +- return set(obj, path.split('.').map(getKey), value, doNotReplace); +- } +- var currentPath = path[0]; +- if (typeof currentPath !== 'string' && typeof currentPath !== 'number') { +- currentPath = String(currentPath) +- } +- var currentValue = getShallowProperty(obj, currentPath); +- if (options.includeInheritedProps && (currentPath === '__proto__' || +- (currentPath === 'constructor' && typeof currentValue === 'function'))) { +- throw new Error('For security reasons, object\'s magic properties cannot be set') ++ return set(obj, path.split('.').map(getKey), value, doNotReplace) + } ++ var currentPath = path[0] ++ var currentValue = getShallowPropertySafely(obj, currentPath) + if (path.length === 1) { + if (currentValue === void 0 || !doNotReplace) { +- obj[currentPath] = value; ++ obj[currentPath] = value + } +- return currentValue; ++ return currentValue + } + + if (currentValue === void 0) { + //check if we assume an array +- if(typeof path[1] === 'number') { +- obj[currentPath] = []; ++ if (typeof path[1] === 'number') { ++ obj[currentPath] = [] + } else { +- obj[currentPath] = {}; ++ obj[currentPath] = {} + } + } + +- return set(obj[currentPath], path.slice(1), value, doNotReplace); ++ return set(obj[currentPath], path.slice(1), value, doNotReplace) + } + + objectPath.has = function (obj, path) { + if (typeof path === 'number') { +- path = [path]; ++ path = [path] + } else if (typeof path === 'string') { +- path = path.split('.'); ++ path = path.split('.') + } + + if (!path || path.length === 0) { +- return !!obj; ++ return !!obj + } + + for (var i = 0; i < path.length; i++) { +- var j = getKey(path[i]); ++ var j = getKey(path[i]) + +- if((typeof j === 'number' && isArray(obj) && j < obj.length) || ++ if ((typeof j === 'number' && isArray(obj) && j < obj.length) || + (options.includeInheritedProps ? (j in Object(obj)) : hasOwnProperty(obj, j))) { +- obj = obj[j]; ++ obj = obj[j] + } else { +- return false; ++ return false + } + } + +- return true; +- }; ++ return true ++ } + +- objectPath.ensureExists = function (obj, path, value){ +- return set(obj, path, value, true); +- }; ++ objectPath.ensureExists = function (obj, path, value) { ++ return set(obj, path, value, true) ++ } + +- objectPath.set = function (obj, path, value, doNotReplace){ +- return set(obj, path, value, doNotReplace); +- }; ++ objectPath.set = function (obj, path, value, doNotReplace) { ++ return set(obj, path, value, doNotReplace) ++ } + +- objectPath.insert = function (obj, path, value, at){ +- var arr = objectPath.get(obj, path); +- at = ~~at; ++ objectPath.insert = function (obj, path, value, at) { ++ var arr = objectPath.get(obj, path) ++ at = ~~at + if (!isArray(arr)) { +- arr = []; +- objectPath.set(obj, path, arr); ++ arr = [] ++ objectPath.set(obj, path, arr) + } +- arr.splice(at, 0, value); +- }; ++ arr.splice(at, 0, value) ++ } + +- objectPath.empty = function(obj, path) { ++ objectPath.empty = function (obj, path) { + if (isEmpty(path)) { +- return void 0; ++ return void 0 + } + if (obj == null) { +- return void 0; ++ return void 0 + } + +- var value, i; ++ var value, i + if (!(value = objectPath.get(obj, path))) { +- return void 0; ++ return void 0 + } + + if (typeof value === 'string') { +- return objectPath.set(obj, path, ''); ++ return objectPath.set(obj, path, '') + } else if (isBoolean(value)) { +- return objectPath.set(obj, path, false); ++ return objectPath.set(obj, path, false) + } else if (typeof value === 'number') { +- return objectPath.set(obj, path, 0); ++ return objectPath.set(obj, path, 0) + } else if (isArray(value)) { +- value.length = 0; ++ value.length = 0 + } else if (isObject(value)) { + for (i in value) { + if (hasShallowProperty(value, i)) { +- delete value[i]; ++ delete value[i] + } + } + } else { +- return objectPath.set(obj, path, null); ++ return objectPath.set(obj, path, null) + } +- }; ++ } + +- objectPath.push = function (obj, path /*, values */){ +- var arr = objectPath.get(obj, path); ++ objectPath.push = function (obj, path /*, values */) { ++ var arr = objectPath.get(obj, path) + if (!isArray(arr)) { +- arr = []; +- objectPath.set(obj, path, arr); ++ arr = [] ++ objectPath.set(obj, path, arr) + } + +- arr.push.apply(arr, Array.prototype.slice.call(arguments, 2)); +- }; ++ arr.push.apply(arr, Array.prototype.slice.call(arguments, 2)) ++ } + + objectPath.coalesce = function (obj, paths, defaultValue) { +- var value; ++ var value + + for (var i = 0, len = paths.length; i < len; i++) { + if ((value = objectPath.get(obj, paths[i])) !== void 0) { +- return value; ++ return value + } + } + +- return defaultValue; +- }; ++ return defaultValue ++ } + +- objectPath.get = function (obj, path, defaultValue){ ++ objectPath.get = function (obj, path, defaultValue) { + if (typeof path === 'number') { +- path = [path]; ++ path = [path] + } + if (!path || path.length === 0) { +- return obj; ++ return obj + } + if (obj == null) { +- return defaultValue; ++ return defaultValue + } + if (typeof path === 'string') { +- return objectPath.get(obj, path.split('.'), defaultValue); ++ return objectPath.get(obj, path.split('.'), defaultValue) + } + +- var currentPath = getKey(path[0]); +- var nextObj = getShallowProperty(obj, currentPath) ++ var currentPath = getKey(path[0]) ++ var nextObj = getShallowPropertySafely(obj, currentPath) + if (nextObj === void 0) { +- return defaultValue; ++ return defaultValue + } + + if (path.length === 1) { +- return nextObj; ++ return nextObj + } + +- return objectPath.get(obj[currentPath], path.slice(1), defaultValue); +- }; ++ return objectPath.get(obj[currentPath], path.slice(1), defaultValue) ++ } + +- objectPath.del = function del(obj, path) { ++ objectPath.del = function del (obj, path) { + if (typeof path === 'number') { +- path = [path]; ++ path = [path] + } + + if (obj == null) { +- return obj; ++ return obj + } + + if (isEmpty(path)) { +- return obj; ++ return obj + } +- if(typeof path === 'string') { +- return objectPath.del(obj, path.split('.')); ++ if (typeof path === 'string') { ++ return objectPath.del(obj, path.split('.')) + } + +- var currentPath = getKey(path[0]); ++ var currentPath = getKey(path[0]) ++ getShallowPropertySafely(obj, currentPath) + if (!hasShallowProperty(obj, currentPath)) { +- return obj; ++ return obj + } + +- if(path.length === 1) { ++ if (path.length === 1) { + if (isArray(obj)) { +- obj.splice(currentPath, 1); ++ obj.splice(currentPath, 1) + } else { +- delete obj[currentPath]; ++ delete obj[currentPath] + } + } else { +- return objectPath.del(obj[currentPath], path.slice(1)); ++ return objectPath.del(obj[currentPath], path.slice(1)) + } + +- return obj; ++ return obj + } + +- return objectPath; ++ return objectPath + } + +- var mod = factory(); +- mod.create = factory; ++ var mod = factory() ++ mod.create = factory + mod.withInheritedProps = factory({includeInheritedProps: true}) +- return mod; +-}); ++ return mod ++}) +--- a/test.js ++++ b/test.js +@@ -133,6 +133,43 @@ + expect(objectPath.get(extended, 'enabled')).to.be.equal(true) + expect(objectPath.get(extended, 'one')).to.be.equal(undefined) + }) ++ ++ it('[security] should not get magic properties in default mode', function () { ++ expect(objectPath.get({}, '__proto__')).to.be.undefined ++ expect(objectPath.get({}, [['__proto__']])).to.be.undefined ++ ++ function Clazz() {} ++ Clazz.prototype.test = [] ++ ++ expect(objectPath.get(new Clazz(), '__proto__')).to.be.undefined ++ expect(objectPath.get(new Clazz(), [['__proto__']])).to.be.undefined ++ expect(objectPath.get(new Clazz(), ['constructor', 'prototype'])).to.be.undefined ++ }) ++ ++ it('[security] should not get magic properties in inheritedProps mode', function () { ++ expect(function() { ++ objectPath.withInheritedProps.get({}, '__proto__') ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.get({}, [['__proto__']]) ++ }).to.throw('For security reasons') ++ ++ function Clazz() {} ++ Clazz.prototype.test = 'original' ++ ++ expect(function() { ++ objectPath.withInheritedProps.get(new Clazz(), '__proto__') ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.get(new Clazz(), [['__proto__']]) ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.get(new Clazz(), ['constructor', 'prototype']) ++ }).to.throw('For security reasons') ++ }) + }) + + +@@ -244,9 +281,15 @@ + objectPath.set({}, [['__proto__'], 'injected'], 'this is bad') + expect(Object.prototype.injected).to.be.undefined + ++ objectPath.set({}, ['__proto__'], {}) ++ expect(Object.prototype.toString).to.be.a('function') ++ + function Clazz() {} + Clazz.prototype.test = 'original' + ++ objectPath.set({}, ['__proto__'], {test: 'this is bad'}) ++ expect(Clazz.prototype.test).to.be.equal('original') ++ + objectPath.set(new Clazz(), '__proto__.test', 'this is bad') + expect(Clazz.prototype.test).to.be.equal('original') + +@@ -258,9 +301,10 @@ + }) + + it('[security] should throw an exception if trying to set magic properties in inheritedProps mode', function () { +- expect(function() {objectPath.withInheritedProps.set({}, '__proto__.injected', 'this is bad')}) +- .to.throw('For security reasons') +- expect(Object.prototype.injected).to.be.undefined ++ expect(function() { ++ objectPath.withInheritedProps.set({}, '__proto__.injected', 'this is bad') ++ expect(Object.prototype.injected).to.be.undefined ++ }).to.throw('For security reasons') + + expect(function() { + objectPath.withInheritedProps.set({}, [['__proto__'], 'injected'], 'this is bad') +@@ -270,21 +314,25 @@ + function Clazz() {} + Clazz.prototype.test = 'original' + +- expect(function() {objectPath.withInheritedProps.set(new Clazz(), '__proto__.test', 'this is bad')}) +- .to.throw('For security reasons') +- expect(Clazz.prototype.test).to.be.equal('original') ++ expect(function() { ++ objectPath.withInheritedProps.set(new Clazz(), '__proto__.test', 'this is bad') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }).to.throw('For security reasons') + +- expect(function() {objectPath.withInheritedProps.set(new Clazz(), 'constructor.prototype.test', 'this is bad')}) +- .to.throw('For security reasons') +- expect(Clazz.prototype.test).to.be.equal('original') ++ expect(function() { ++ objectPath.withInheritedProps.set(new Clazz(), 'constructor.prototype.test', 'this is bad') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }).to.throw('For security reasons') + +- expect(function() {objectPath.withInheritedProps.set({}, 'constructor.prototype.injected', 'this is OK')}) +- .to.throw('For security reasons') +- expect(Object.prototype.injected).to.be.undefined ++ expect(function() { ++ objectPath.withInheritedProps.set({}, 'constructor.prototype.injected', 'this is OK') ++ expect(Object.prototype.injected).to.be.undefined ++ }).to.throw('For security reasons') + +- expect(function() {objectPath.withInheritedProps.set({}, [['constructor'], 'prototype', 'injected'], 'this is bad')}) +- .to.throw('For security reasons') +- expect(Object.prototype.injected).to.be.undefined ++ expect(function() { ++ objectPath.withInheritedProps.set({}, [['constructor'], 'prototype', 'injected'], 'this is bad') ++ expect(Object.prototype.injected).to.be.undefined ++ }).to.throw('For security reasons') + }) + }) + +@@ -328,6 +376,39 @@ + expect(obj).to.have.nested.property('b.e.0.0', 'l') + }) + ++ it('[security] should not push within prototype properties in default mode', function () { ++ function Clazz() {} ++ Clazz.prototype.test = [] ++ ++ objectPath.push(new Clazz(), '__proto__.test', 'pushed') ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ ++ objectPath.push(new Clazz(), [['__proto__'], 'test'], 'pushed') ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ ++ objectPath.push(new Clazz(), 'constructor.prototype.test', 'pushed') ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ }) ++ ++ it('[security] should not push within prototype properties in inheritedProps mode', function () { ++ function Clazz() {} ++ Clazz.prototype.test = [] ++ ++ expect(function() { ++ objectPath.withInheritedProps.push(new Clazz(), '__proto__.test', 'pushed') ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.push(new Clazz(), [['__proto__'], 'test'], 'pushed') ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.push(new Clazz(), 'constructor.prototype.test', 'pushed') ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ }).to.throw('For security reasons') ++ }) + }) + + +@@ -361,6 +442,67 @@ + expect(any[1]).to.be.an('object') + expect(any[1][1]).to.be.an('object') + }) ++ ++ it('[security] should not set magic properties in default mode', function () { ++ objectPath.ensureExists({}, '__proto__.injected', 'this is bad') ++ expect(Object.prototype.injected).to.be.undefined ++ ++ objectPath.ensureExists({}, [['__proto__'], 'injected'], 'this is bad') ++ expect(Object.prototype.injected).to.be.undefined ++ ++ objectPath.ensureExists({}, ['__proto__'], {}) ++ expect(Object.prototype.toString).to.be.a('function') ++ ++ function Clazz() {} ++ Clazz.prototype.test = 'original' ++ ++ objectPath.ensureExists({}, ['__proto__'], {test: 'this is bad'}) ++ expect(Clazz.prototype.test).to.be.equal('original') ++ ++ objectPath.ensureExists(new Clazz(), '__proto__.test', 'this is bad') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ ++ objectPath.ensureExists(new Clazz(), [['__proto__'], 'test'], 'this is bad') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ ++ objectPath.ensureExists(new Clazz(), 'constructor.prototype.test', 'this is bad') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }) ++ ++ it('[security] should throw an exception if trying to set magic properties in inheritedProps mode', function () { ++ expect(function() {objectPath.withInheritedProps.ensureExists({}, '__proto__.injected', 'this is bad')}) ++ .to.throw('For security reasons') ++ expect(Object.prototype.injected).to.be.undefined ++ ++ expect(function() { ++ objectPath.withInheritedProps.ensureExists({}, [['__proto__'], 'injected'], 'this is bad') ++ expect(Object.prototype.injected).to.be.undefined ++ }).to.throw('For security reasons') ++ ++ function Clazz() {} ++ Clazz.prototype.test = 'original' ++ ++ expect(function() { ++ objectPath.withInheritedProps.ensureExists(new Clazz(), '__proto__.test', 'this is bad') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }).to.throw('For security reasons') ++ ++ ++ expect(function() { ++ objectPath.withInheritedProps.ensureExists(new Clazz(), 'constructor.prototype.test', 'this is bad') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.ensureExists({}, 'constructor.prototype.injected', 'this is OK') ++ expect(Object.prototype.injected).to.be.undefined ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.ensureExists({}, [['constructor'], 'prototype', 'injected'], 'this is bad') ++ expect(Object.prototype.injected).to.be.undefined ++ }).to.throw('For security reasons') ++ }) + }) + + describe('coalesce', function () { +@@ -512,6 +654,40 @@ + expect(obj.instance.arr).to.be.an('array') + expect(obj['function']).to.equal(null) + }) ++ ++ it('[security] should not empty prototype properties in default mode', function () { ++ function Clazz() {} ++ Clazz.prototype.test = 'original' ++ ++ objectPath.empty(new Clazz(), '__proto__') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ ++ objectPath.empty(new Clazz(), [['__proto__']]) ++ expect(Clazz.prototype.test).to.be.equal('original') ++ ++ objectPath.empty(new Clazz(), 'constructor.prototype') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }) ++ ++ it('[security] should throw an exception if trying to delete prototype properties in inheritedProps mode', function () { ++ function Clazz() {} ++ Clazz.prototype.test = 'original' ++ ++ expect(function() { ++ objectPath.withInheritedProps.empty(new Clazz(), '__proto__') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.empty(new Clazz(), 'constructor.prototype') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.empty({}, [['constructor'], 'prototype']) ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }).to.throw('For security reasons') ++ }) + }) + + describe('del', function () { +@@ -588,6 +764,56 @@ + expect(obj.b.d).to.have.length(0) + expect(obj.b.d).to.be.deep.equal([]) + }) ++ ++ it('[security] should not delete prototype properties in default mode', function () { ++ objectPath.del({}, '__proto__.valueOf') ++ expect(Object.prototype.valueOf).to.be.a('function') ++ ++ objectPath.del({}, [['__proto__'], 'valueOf']) ++ expect(Object.prototype.valueOf).to.be.a('function') ++ ++ function Clazz() {} ++ Clazz.prototype.test = 'original' ++ ++ objectPath.del(new Clazz(), '__proto__.test') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ ++ objectPath.del(new Clazz(), [['__proto__'], 'test']) ++ expect(Clazz.prototype.test).to.be.equal('original') ++ ++ objectPath.del(new Clazz(), 'constructor.prototype.test') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }) ++ ++ it('[security] should throw an exception if trying to delete prototype properties in inheritedProps mode', function () { ++ expect(function() { ++ objectPath.withInheritedProps.del({}, '__proto__.valueOf') ++ expect(Object.prototype.valueOf).to.be.a('function') ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.del({}, [['__proto__'], 'valueOf']) ++ expect(Object.prototype.valueOf).to.be.a('function') ++ }).to.throw('For security reasons') ++ ++ function Clazz() {} ++ Clazz.prototype.test = 'original' ++ ++ expect(function() { ++ objectPath.withInheritedProps.del(new Clazz(), '__proto__.test') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.del(new Clazz(), 'constructor.prototype.test', 'this is bad') ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.del({}, [['constructor'], 'prototype', 'test']) ++ expect(Clazz.prototype.test).to.be.equal('original') ++ }).to.throw('For security reasons') ++ }) + }) + + describe('insert', function () { +@@ -630,6 +856,45 @@ + 'asdf' + ]) + }) ++ ++ it('[security] should not insert within prototype properties in default mode', function () { ++ function Clazz() {} ++ Clazz.prototype.test = [] ++ ++ objectPath.insert(new Clazz(), '__proto__.test', 'inserted', 0) ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ ++ objectPath.insert(new Clazz(), [['__proto__'], 'test'], 'inserted', 0) ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ ++ objectPath.insert(new Clazz(), 'constructor.prototype.test', 'inserted', 0) ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ }) ++ ++ it('[security] should not insert within prototype properties in inheritedProps mode', function () { ++ function Clazz() {} ++ Clazz.prototype.test = [] ++ ++ expect(function() { ++ objectPath.withInheritedProps.insert(new Clazz(), '__proto__.test', 'inserted', 0) ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.insert(new Clazz(), [['__proto__'], 'test'], 'inserted', 0) ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.insert(new Clazz(), 'constructor.prototype.test', 'inserted', 0) ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ }).to.throw('For security reasons') ++ ++ expect(function() { ++ objectPath.withInheritedProps.insert(new Clazz().constructor, 'prototype.test', 'inserted', 0) ++ expect(Clazz.prototype.test).to.be.deep.equal([]) ++ }).to.throw('For security reasons') ++ }) + }) + + describe('has', function () { diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..7b1f359 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,2 @@ +CVE-2021-23434.patch +CVE-2021-3805.patch diff --git a/debian/salsa-ci.yml b/debian/salsa-ci.yml index 33c3a64..6fd902a 100644 --- a/debian/salsa-ci.yml +++ b/debian/salsa-ci.yml @@ -1,4 +1,7 @@ --- +variables: + RELEASE: 'bullseye' + include: - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml