Task 26377610: [DynamicProto] Investigate possible security issue with prototype pollution (#81)
This commit is contained in:
Родитель
09cf322bd0
Коммит
69866bb9c4
|
@ -24,6 +24,10 @@ jobs:
|
|||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Update rush shrinkwrap dependencies (for different node versions)
|
||||
run: node common/scripts/install-run-rush.js update --full
|
||||
- run: npm install rollup -g
|
||||
- run: npm install grunt-cli
|
||||
- run: npm install
|
||||
- run: node common/scripts/install-run-rush.js check && node common/scripts/install-run-rush.js update
|
||||
- run: npm run build --verbose
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
11
gruntfile.js
11
gruntfile.js
|
@ -89,6 +89,15 @@ module.exports = function (grunt) {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
connect: {
|
||||
server: {
|
||||
options: {
|
||||
port: 9005,
|
||||
base: '.',
|
||||
debug: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -100,6 +109,7 @@ module.exports = function (grunt) {
|
|||
grunt.loadNpmTasks("@nevware21/grunt-eslint-ts");
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-qunit');
|
||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||
grunt.loadNpmTasks('grunt-run');
|
||||
grunt.registerTask("default", ["ts:rollup", "ts:rolluptest", "ts:dynamicproto", "ts:dynamicprototest", "qunit:rollup", "qunit:dynamicproto"]);
|
||||
grunt.registerTask("dynamicproto", ["eslint-ts:dynamicproto-fix", "ts:dynamicproto"]);
|
||||
|
@ -107,4 +117,5 @@ module.exports = function (grunt) {
|
|||
grunt.registerTask("rollup", ["ts:rollup", "ts:rolluptest", "qunit:rollup"]);
|
||||
grunt.registerTask("lint", ["eslint-ts:dynamicproto"]);
|
||||
grunt.registerTask("lint-fix", ["eslint-ts:dynamicproto-fix"]);
|
||||
grunt.registerTask("serve", ["connect:server:keepalive"]);
|
||||
};
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
"license": "MIT",
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@nevware21/ts-utils": ">= 0.9.4 < 2.x"
|
||||
"@nevware21/ts-utils": ">= 0.10.4 < 2.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/applicationinsights-rollup-es3" : "^1.0.1",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { getGlobal, objHasOwnProperty, throwTypeError } from "@nevware21/ts-utils";
|
||||
import { getGlobal, objCreate, objHasOwnProperty, throwTypeError } from "@nevware21/ts-utils";
|
||||
|
||||
interface DynamicGlobalSettings {
|
||||
/**
|
||||
|
@ -152,8 +152,7 @@ function _isObjectOrArrayPrototype(target:any) {
|
|||
* Helper used to check whether the target is an Object prototype, Array prototype or Function prototype
|
||||
* @ignore
|
||||
*/
|
||||
function _isObjectArrayOrFunctionPrototype(target:any)
|
||||
{
|
||||
function _isObjectArrayOrFunctionPrototype(target:any) {
|
||||
return _isObjectOrArrayPrototype(target) || target === Function[Prototype];
|
||||
}
|
||||
|
||||
|
@ -219,7 +218,7 @@ function _forEachProp(target: any, func: (name: string) => void) {
|
|||
* @ignore
|
||||
*/
|
||||
function _isDynamicCandidate(target:any, funcName:string, skipOwn:boolean) {
|
||||
return (funcName !== Constructor && typeof target[funcName] === strFunction && (skipOwn || objHasOwnProperty(target, funcName)));
|
||||
return (funcName !== Constructor && typeof target[funcName] === strFunction && (skipOwn || objHasOwnProperty(target, funcName)) && funcName !== str__Proto && funcName !== Prototype);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -239,7 +238,7 @@ function _throwTypeError(message:string) {
|
|||
*/
|
||||
function _getInstanceFuncs(thisTarget:any): any {
|
||||
// Get the base proto
|
||||
var instFuncs = {};
|
||||
var instFuncs = objCreate(null);
|
||||
|
||||
// Save any existing instance functions
|
||||
_forEachProp(thisTarget, (name) => {
|
||||
|
@ -292,7 +291,7 @@ function _getBaseFuncs(classProto:any, thisTarget:any, instFuncs:any, useBaseIns
|
|||
}
|
||||
|
||||
// Start creating a new baseFuncs by creating proxies for the instance functions (as they may get replaced)
|
||||
var baseFuncs = {};
|
||||
var baseFuncs = objCreate(null);
|
||||
_forEachProp(instFuncs, (name) => {
|
||||
// Create an instance callback for passing the base function to the caller
|
||||
baseFuncs[name] = _instFuncProxy(thisTarget, instFuncs, name);
|
||||
|
@ -333,8 +332,8 @@ function _getInstFunc(target: any, funcName: string, proto: any, currentDynProto
|
|||
// it will walk the proto chain and return any parent proto classname.
|
||||
if (target && objHasOwnProperty(proto, DynClassName)) {
|
||||
|
||||
let instFuncTable = target[DynInstFuncTable] || {};
|
||||
instFunc = (instFuncTable[proto[DynClassName]] || {})[funcName];
|
||||
let instFuncTable = target[DynInstFuncTable] || objCreate(null);
|
||||
instFunc = (instFuncTable[proto[DynClassName]] || objCreate(null))[funcName];
|
||||
|
||||
if (!instFunc) {
|
||||
// Avoid stack overflow from recursive calling the same function
|
||||
|
@ -426,27 +425,31 @@ function _populatePrototype(proto:any, className:string, target:any, baseInstFun
|
|||
}
|
||||
|
||||
if (!_isObjectOrArrayPrototype(proto)) {
|
||||
let instFuncTable = target[DynInstFuncTable] = target[DynInstFuncTable] || {};
|
||||
let instFuncs = instFuncTable[className] = (instFuncTable[className] || {}); // fetch and assign if as it may not exist yet
|
||||
let instFuncTable = target[DynInstFuncTable] = target[DynInstFuncTable] || objCreate(null);
|
||||
if (!_isObjectOrArrayPrototype(instFuncTable)) {
|
||||
let instFuncs = instFuncTable[className] = (instFuncTable[className] || objCreate(null)); // fetch and assign if as it may not exist yet
|
||||
|
||||
// Set whether we are allow to lookup instances, if someone has set to false then do not re-enable
|
||||
if (instFuncTable[DynAllowInstChkTag] !== false) {
|
||||
instFuncTable[DynAllowInstChkTag] = !!setInstanceFunc;
|
||||
}
|
||||
|
||||
_forEachProp(target, (name) => {
|
||||
// Only add overridden functions
|
||||
if (_isDynamicCandidate(target, name, false) && target[name] !== baseInstFuncs[name] ) {
|
||||
// Save the instance Function to the lookup table and remove it from the instance as it's not a dynamic proto function
|
||||
instFuncs[name] = target[name];
|
||||
delete target[name];
|
||||
|
||||
// Add a dynamic proto if one doesn't exist or if a prototype function exists and it's not a dynamic one
|
||||
if (!objHasOwnProperty(proto, name) || (proto[name] && !proto[name][DynProxyTag])) {
|
||||
proto[name] = _createDynamicPrototype(proto, name);
|
||||
}
|
||||
// Set whether we are allow to lookup instances, if someone has set to false then do not re-enable
|
||||
if (instFuncTable[DynAllowInstChkTag] !== false) {
|
||||
instFuncTable[DynAllowInstChkTag] = !!setInstanceFunc;
|
||||
}
|
||||
});
|
||||
|
||||
if (!_isObjectOrArrayPrototype(instFuncs)) {
|
||||
_forEachProp(target, (name) => {
|
||||
// Only add overridden functions
|
||||
if (_isDynamicCandidate(target, name, false) && target[name] !== baseInstFuncs[name] ) {
|
||||
// Save the instance Function to the lookup table and remove it from the instance as it's not a dynamic proto function
|
||||
instFuncs[name] = target[name];
|
||||
delete target[name];
|
||||
|
||||
// Add a dynamic proto if one doesn't exist or if a prototype function exists and it's not a dynamic one
|
||||
if (!objHasOwnProperty(proto, name) || (proto[name] && !proto[name][DynProxyTag])) {
|
||||
proto[name] = _createDynamicPrototype(proto, name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,667 @@
|
|||
/// <reference path="./TestFramework/Common.ts" />
|
||||
|
||||
import { objGetPrototypeOf } from "@nevware21/ts-utils";
|
||||
import dynamicProto from "../src/DynamicProto";
|
||||
|
||||
class HackClass {
|
||||
public hello: string;
|
||||
|
||||
constructor() {
|
||||
this.hello = "world";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BadInstClass {
|
||||
public _dynInstFuncs: any = {};
|
||||
|
||||
constructor() {
|
||||
this._dynInstFuncs = Object.prototype;
|
||||
}
|
||||
}
|
||||
|
||||
class BadProxyInstClass {
|
||||
public _dynInstFuncs: any = {};
|
||||
|
||||
constructor() {
|
||||
this._dynInstFuncs = new Proxy(this, {
|
||||
get: (target, prop) => {
|
||||
if (typeof prop === "string" && prop.startsWith("_dynCls")) {
|
||||
return Object.prototype;
|
||||
}
|
||||
|
||||
return target[prop];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class SecurityCheckTests extends TestClass {
|
||||
|
||||
public testInitialize() {
|
||||
}
|
||||
|
||||
public registerTests() {
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype directly",
|
||||
test: () => {
|
||||
let a: any = {};
|
||||
|
||||
try {
|
||||
dynamicProto(Object, a, (_self, base) => {
|
||||
_self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
|
||||
_self.__proto__ = {
|
||||
testHack: true
|
||||
};
|
||||
|
||||
_self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
});
|
||||
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
} catch (e) {
|
||||
QUnit.assert.ok(true, "Expected an exception to be thrown");
|
||||
}
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype directly",
|
||||
test: () => {
|
||||
let a: any = {};
|
||||
|
||||
try {
|
||||
dynamicProto(Object.prototype, a, (_self, base) => {
|
||||
_self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
|
||||
_self.__proto__ = {
|
||||
testHack: true
|
||||
};
|
||||
|
||||
_self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
});
|
||||
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
} catch (e) {
|
||||
QUnit.assert.ok(true, "Expected an exception to be thrown");
|
||||
}
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype directly",
|
||||
test: () => {
|
||||
let a: any = {};
|
||||
|
||||
try {
|
||||
dynamicProto(Object, a, (_self, base) => {
|
||||
_self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
|
||||
_self.__proto__ = {
|
||||
testHack: true
|
||||
};
|
||||
|
||||
_self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
});
|
||||
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
} catch (e) {
|
||||
QUnit.assert.ok(true, "Expected an exception to be thrown");
|
||||
}
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype directly with a HackClass instance and __proto__ property",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let self = <any>_self;
|
||||
self.__proto__ = {
|
||||
testHack: true
|
||||
};
|
||||
|
||||
self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
|
||||
self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype directly with a HackClass instance and __proto__ function",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let self = <any>_self;
|
||||
self.__proto__ = () => {
|
||||
testHack: true
|
||||
};
|
||||
|
||||
self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
|
||||
self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype directly using defineProperty with a HackClass instance and __proto__ property",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let self = <any>_self;
|
||||
|
||||
Object.defineProperty(self, "__proto__", {
|
||||
value: {
|
||||
testHack: true
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
|
||||
self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype directly using defineProperty with a HackClass instance and __proto__ function",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let self = <any>_self;
|
||||
|
||||
Object.defineProperty(self, "__proto__", {
|
||||
value: () => {
|
||||
testHack: true
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
|
||||
self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype using HackClass instance with a __proto__ function",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let self = <any>_self;
|
||||
|
||||
self.__proto__ = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
};
|
||||
|
||||
self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
|
||||
self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype directly with HackClass and an object instance",
|
||||
test: () => {
|
||||
let a = {};
|
||||
|
||||
try {
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let self = <any>_self;
|
||||
self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
|
||||
self.__proto__ = {
|
||||
testHack: true
|
||||
};
|
||||
|
||||
self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
});
|
||||
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
} catch (e) {
|
||||
QUnit.assert.ok(true, "Expected an exception to be thrown");
|
||||
QUnit.assert.ok(e.message.indexOf("not in hierarchy") > -1, "Expected an exception to be thrown");
|
||||
}
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype indirectly using defineProperty with HackClass and an object instance",
|
||||
test: () => {
|
||||
let a = {};
|
||||
|
||||
try {
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let self = <any>_self;
|
||||
self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
|
||||
Object.defineProperty(self, "__proto__", {
|
||||
value: {
|
||||
testHack: true
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
});
|
||||
} catch (e) {
|
||||
QUnit.assert.ok(true, "Expected an exception to be thrown");
|
||||
QUnit.assert.ok(e.message.indexOf("not in hierarchy") > -1, "Expected an exception to be thrown");
|
||||
}
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype directly with evil __proto__ with HackClass and an object instance",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let self = <any>_self;
|
||||
self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
|
||||
self["__proto__['hacked']"] = {
|
||||
testHack: true
|
||||
};
|
||||
|
||||
self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype indirectly using defineProperty with evil __proto__ with HackClass and an object instance",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let self = <any>_self;
|
||||
self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
|
||||
Object.defineProperty(self, "__proto__['hacked']", {
|
||||
value: {
|
||||
testHack: true
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype directly with a HackClass instance",
|
||||
test: () => {
|
||||
let a = new HackClass()
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let self = <any>_self;
|
||||
self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
|
||||
self.__proto__ = {
|
||||
testHack: true
|
||||
};
|
||||
|
||||
self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype indirectly using defineProperty with a HackClass instance",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let self = <any>_self;
|
||||
self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
|
||||
Object.defineProperty(self, "__proto__", {
|
||||
value: {
|
||||
testHack: true
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype indirectly with a null prototype instance",
|
||||
test: () => {
|
||||
let a: any = {};
|
||||
let theInstance = Object.create(a);
|
||||
|
||||
try {
|
||||
dynamicProto(theInstance, a, (_self, base) => {
|
||||
_self.__proto__ = {
|
||||
testHack: true
|
||||
};
|
||||
|
||||
_self.prototype = {
|
||||
testHack2: true
|
||||
};
|
||||
|
||||
_self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
} catch (e) {
|
||||
QUnit.assert.ok(true, "Expected an exception to be thrown");
|
||||
}
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack2"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype indirectly with an a prototype instance",
|
||||
test: () => {
|
||||
let a: any = {};
|
||||
let theInstance = Object.create(a);
|
||||
try {
|
||||
dynamicProto(Object.getPrototypeOf(theInstance), a, (_self, base) => {
|
||||
_self._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
} catch (e) {
|
||||
QUnit.assert.ok(true, "Expected an exception to be thrown");
|
||||
}
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype indirectly by using a proxy to return the Object.prototype as the instance functions",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
_self["_dynInstFuncs"] = new Proxy(_self["_dynInstFuncs"] || {}, {
|
||||
get: (target, prop) => {
|
||||
if (typeof prop === "string" && prop.startsWith("_dynCls")) {
|
||||
return Object.prototype;
|
||||
}
|
||||
|
||||
return target[prop];
|
||||
}
|
||||
});
|
||||
|
||||
(_self as any)._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype indirectly by using a proxy to return the Object.prototype as the instance functions",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let className = _self["_dynClass"];
|
||||
let classProto = _self["_dynInstFuncs"] = (_self["_dynInstFuncs"] || {});
|
||||
|
||||
// Change the return class prototype to be Object.prototype
|
||||
classProto["_dynCls" + className] = Object.prototype;
|
||||
|
||||
(_self as any)._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype indirectly by using a HackClass and updating the base class prototype",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
try {
|
||||
objGetPrototypeOf(base).testHack = true;
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
} catch (e) {
|
||||
QUnit.assert.ok(true, "Expected an exception to be thrown");
|
||||
}
|
||||
|
||||
(_self as any)._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("testHack"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Array.prototype indirectly by using a proxy to return the Array.prototype as the instance functions",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
_self["_dynInstFuncs"] = new Proxy(_self["_dynInstFuncs"] || {}, {
|
||||
get: (target, prop) => {
|
||||
if (typeof prop === "string" && prop.startsWith("_dynCls")) {
|
||||
return Array.prototype;
|
||||
}
|
||||
|
||||
return target[prop];
|
||||
}
|
||||
});
|
||||
|
||||
(_self as any)._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Array.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Array.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Array.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Array.prototype indirectly by using a proxy to return the Object.prototype as the instance functions",
|
||||
test: () => {
|
||||
let a = new HackClass();
|
||||
|
||||
dynamicProto(HackClass, a, (_self, base) => {
|
||||
let className = _self["_dynClass"];
|
||||
let classProto = _self["_dynInstFuncs"] = (_self["_dynInstFuncs"] || {});
|
||||
|
||||
// Change the return class prototype to be Object.prototype
|
||||
classProto["_dynCls" + className] = Array.prototype;
|
||||
|
||||
(_self as any)._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Array.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Array.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Array.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype with a BadInstClass instance",
|
||||
test: () => {
|
||||
let a = new BadInstClass();
|
||||
|
||||
dynamicProto(BadInstClass, a, (_self, base) => {
|
||||
|
||||
(_self as any)._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_dynInstFuncs"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
this.testCase({
|
||||
name: "Try to update Object.prototype with a BadProxyInstClass instance",
|
||||
test: () => {
|
||||
let a = new BadProxyInstClass();
|
||||
|
||||
dynamicProto(BadProxyInstClass, a, (_self, base) => {
|
||||
|
||||
(_self as any)._testFunction = () => {
|
||||
QUnit.assert.fail("Should not be able to update Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_testFunction"), "Should not have polluted Object.prototype");
|
||||
QUnit.assert.ok(!Object.prototype.hasOwnProperty("_dynInstFuncs"), "Should not have polluted Object.prototype");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,12 @@ import { DynamicProtoDefaultTests } from '../DynamicProto.Tests';
|
|||
import { DynamicProtoMultipleCallTests } from '../DynamicProtoMultipleCall.Tests';
|
||||
import { DynamicProtoNoInstTests } from '../DynamicProtoNoInst.Tests';
|
||||
import { DynamicProtoMultipleNoInstTests } from '../DynamicProtoMultipleNoInst.Tests';
|
||||
import { SecurityCheckTests } from '../SecurityCheck.Tests';
|
||||
|
||||
export function runTests() {
|
||||
new DynamicProtoDefaultTests("Default").registerTests();
|
||||
new DynamicProtoMultipleCallTests("Multiple").registerTests();
|
||||
new DynamicProtoNoInstTests("SetInst").registerTests();
|
||||
new DynamicProtoMultipleNoInstTests("Multiple SetInst").registerTests();
|
||||
new SecurityCheckTests("Security Checks").registerTests();
|
||||
}
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
"fullClean": "git clean -xdf && npm install && rush update --recheck --purge --full",
|
||||
"fullCleanBuild": "npm run fullClean && npm run build",
|
||||
"npm_pack": "copyfiles README.md LICENSE lib && cd lib && npm pack",
|
||||
"npm_publish": "cd lib && node ../tools/release-tools/npm_publish.js ."
|
||||
"npm_publish": "cd lib && node ../tools/release-tools/npm_publish.js .",
|
||||
"serve": "grunt serve"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -41,7 +42,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/microsoft/DynamicProto-JS#readme",
|
||||
"dependencies": {
|
||||
"@nevware21/ts-utils": ">= 0.9.4 < 2.x"
|
||||
"@nevware21/ts-utils": ">= 0.10.4 < 2.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/rush": "5.82.1",
|
||||
|
@ -57,6 +58,7 @@
|
|||
"eslint-plugin-security": "^1.4.0",
|
||||
"grunt": "^1.5.3",
|
||||
"grunt-cli": "^1.4.3",
|
||||
"grunt-contrib-connect": "^3.0.0",
|
||||
"grunt-contrib-qunit": "^4.0.0",
|
||||
"grunt-contrib-uglify": "^5.0.1",
|
||||
"grunt-run": "^0.8.1",
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/microsoft/DynamicProto-JS#readme",
|
||||
"dependencies": {
|
||||
"@nevware21/ts-utils": ">= 0.9.4 < 2.x"
|
||||
"@nevware21/ts-utils": ">= 0.10.4 < 2.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "^1.5.3",
|
||||
|
|
Загрузка…
Ссылка в новой задаче