Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- Manifest: js/xpconnect/tests/unit/xpcshell.toml
// Test that it's not possible to create expando properties on XPCWNs.
function TestInterfaceAll() {}
TestInterfaceAll.prototype = {
QueryInterface: ChromeUtils.generateQI(["nsIXPCTestInterfaceA",
"nsIXPCTestInterfaceB",
"nsIXPCTestInterfaceC"]),
/* nsIXPCTestInterfaceA / nsIXPCTestInterfaceB */
name: "TestInterfaceAllDefaultName",
/* nsIXPCTestInterfaceC */
someInteger: 42
};
function check_throws(f) {
try {
f();
} catch (exc) {
return;
}
throw new TypeError("Expected exception, no exception thrown");
}
/*
* Test that XPCWrappedNative objects do not permit expando properties to be created.
*
* This function is called twice. The first time, realObj is an nsITimer XPCWN
* and accessObj === realObj.
*
* The second time, accessObj is a scripted proxy with realObj as its target.
* So the second time we are testing that scripted proxies don't magically
* bypass whatever mechanism enforces the expando policy on XPCWNs.
*/
function test_tamperproof(realObj, accessObj, {method, constant, attribute}) {
// Assignment can't create an expando property.
check_throws(function () { accessObj.expando = 14; });
Assert.equal(false, "expando" in realObj);
// Strict assignment throws.
check_throws(function () { "use strict"; accessObj.expando = 14; });
Assert.equal(false, "expando" in realObj);
// Assignment to an inherited method name doesn't work either.
check_throws(function () { accessObj.hasOwnProperty = () => "lies"; });
check_throws(function () { "use strict"; accessObj.hasOwnProperty = () => "lies"; });
Assert.ok(!realObj.hasOwnProperty("hasOwnProperty"));
// Assignment to a method name doesn't work either.
let originalMethod;
if (method) {
originalMethod = accessObj[method];
accessObj[method] = "nope"; // non-writable data property, no exception in non-strict code
check_throws(function () { "use strict"; accessObj[method] = "nope"; });
Assert.ok(realObj[method] === originalMethod);
}
// A constant is the same thing.
let originalConstantValue;
if (constant) {
originalConstantValue = accessObj[constant];
accessObj[constant] = "nope";
Assert.equal(realObj[constant], originalConstantValue);
check_throws(function () { "use strict"; accessObj[constant] = "nope"; });
Assert.equal(realObj[constant], originalConstantValue);
}
// Assignment to a readonly accessor property with no setter doesn't work either.
let originalAttributeDesc;
if (attribute) {
originalAttributeDesc = Object.getOwnPropertyDescriptor(realObj, attribute);
Assert.ok("set" in originalAttributeDesc);
Assert.ok(originalAttributeDesc.set === undefined);
accessObj[attribute] = "nope"; // accessor property with no setter: no exception in non-strict code
check_throws(function () { "use strict"; accessObj[attribute] = "nope"; });
let desc = Object.getOwnPropertyDescriptor(realObj, attribute);
Assert.ok("set" in desc);
Assert.equal(originalAttributeDesc.get, desc.get);
Assert.equal(undefined, desc.set);
}
// Reflect.set doesn't work either.
if (method) {
Assert.ok(!Reflect.set({}, method, "bad", accessObj));
Assert.equal(realObj[method], originalMethod);
}
if (attribute) {
Assert.ok(!Reflect.set({}, attribute, "bad", accessObj));
Assert.equal(originalAttributeDesc.get, Object.getOwnPropertyDescriptor(realObj, attribute).get);
}
// Object.defineProperty can't do anything either.
let names = ["expando"];
if (method) names.push(method);
if (constant) names.push(constant);
if (attribute) names.push(attribute);
for (let name of names) {
let originalDesc = Object.getOwnPropertyDescriptor(realObj, name);
check_throws(function () {
Object.defineProperty(accessObj, name, {configurable: true});
});
check_throws(function () {
Object.defineProperty(accessObj, name, {writable: true});
});
check_throws(function () {
Object.defineProperty(accessObj, name, {get: function () { return "lies"; }});
});
check_throws(function () {
Object.defineProperty(accessObj, name, {value: "bad"});
});
let desc = Object.getOwnPropertyDescriptor(realObj, name);
if (originalDesc === undefined) {
Assert.equal(undefined, desc);
} else {
Assert.equal(originalDesc.configurable, desc.configurable);
Assert.equal(originalDesc.enumerable, desc.enumerable);
Assert.equal(originalDesc.writable, desc.writable);
Assert.equal(originalDesc.value, desc.value);
Assert.equal(originalDesc.get, desc.get);
Assert.equal(originalDesc.set, desc.set);
}
}
// Existing properties can't be deleted.
if (method) {
Assert.equal(false, delete accessObj[method]);
check_throws(function () { "use strict"; delete accessObj[method]; });
Assert.equal(realObj[method], originalMethod);
}
if (constant) {
Assert.equal(false, delete accessObj[constant]);
check_throws(function () { "use strict"; delete accessObj[constant]; });
Assert.equal(realObj[constant], originalConstantValue);
}
if (attribute) {
Assert.equal(false, delete accessObj[attribute]);
check_throws(function () { "use strict"; delete accessObj[attribute]; });
desc = Object.getOwnPropertyDescriptor(realObj, attribute);
Assert.equal(originalAttributeDesc.get, desc.get);
}
}
function test_twice(obj, options) {
test_tamperproof(obj, obj, options);
let handler = {
getPrototypeOf(t) {
return new Proxy(Object.getPrototypeOf(t), handler);
}
};
test_tamperproof(obj, new Proxy(obj, handler), options);
}
function run_test() {
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
test_twice(timer, {
method: "init",
constant: "TYPE_ONE_SHOT",
attribute: "callback"
});
let cmdline = Cu.createCommandLine([], null, Ci.nsICommandLine.STATE_INITIAL_LAUNCH);
test_twice(cmdline, {});
test_twice(Object.getPrototypeOf(cmdline), {
method: "getArgument",
constant: "STATE_INITIAL_LAUNCH",
attribute: "length"
});
// Test a tearoff object.
let b = xpcWrap(new TestInterfaceAll(), Ci.nsIXPCTestInterfaceB);
let tearoff = b.nsIXPCTestInterfaceA;
test_twice(tearoff, {
method: "QueryInterface"
});
}