1 // Test that it's not possible to create expando properties on XPCWNs.
2 // See <https://bugzilla.mozilla.org/show_bug.cgi?id=1143810#c5>.
4 function TestInterfaceAll() {}
5 TestInterfaceAll.prototype = {
6 QueryInterface: ChromeUtils.generateQI(["nsIXPCTestInterfaceA",
7 "nsIXPCTestInterfaceB",
8 "nsIXPCTestInterfaceC"]),
10 /* nsIXPCTestInterfaceA / nsIXPCTestInterfaceB */
11 name: "TestInterfaceAllDefaultName",
13 /* nsIXPCTestInterfaceC */
17 function check_throws(f) {
23 throw new TypeError("Expected exception, no exception thrown");
27 * Test that XPCWrappedNative objects do not permit expando properties to be created.
29 * This function is called twice. The first time, realObj is an nsITimer XPCWN
30 * and accessObj === realObj.
32 * The second time, accessObj is a scripted proxy with realObj as its target.
33 * So the second time we are testing that scripted proxies don't magically
34 * bypass whatever mechanism enforces the expando policy on XPCWNs.
36 function test_tamperproof(realObj, accessObj, {method, constant, attribute}) {
37 // Assignment can't create an expando property.
38 check_throws(function () { accessObj.expando = 14; });
39 Assert.equal(false, "expando" in realObj);
41 // Strict assignment throws.
42 check_throws(function () { "use strict"; accessObj.expando = 14; });
43 Assert.equal(false, "expando" in realObj);
45 // Assignment to an inherited method name doesn't work either.
46 check_throws(function () { accessObj.hasOwnProperty = () => "lies"; });
47 check_throws(function () { "use strict"; accessObj.hasOwnProperty = () => "lies"; });
48 Assert.ok(!realObj.hasOwnProperty("hasOwnProperty"));
50 // Assignment to a method name doesn't work either.
53 originalMethod = accessObj[method];
54 accessObj[method] = "nope"; // non-writable data property, no exception in non-strict code
55 check_throws(function () { "use strict"; accessObj[method] = "nope"; });
56 Assert.ok(realObj[method] === originalMethod);
59 // A constant is the same thing.
60 let originalConstantValue;
62 originalConstantValue = accessObj[constant];
63 accessObj[constant] = "nope";
64 Assert.equal(realObj[constant], originalConstantValue);
65 check_throws(function () { "use strict"; accessObj[constant] = "nope"; });
66 Assert.equal(realObj[constant], originalConstantValue);
69 // Assignment to a readonly accessor property with no setter doesn't work either.
70 let originalAttributeDesc;
72 originalAttributeDesc = Object.getOwnPropertyDescriptor(realObj, attribute);
73 Assert.ok("set" in originalAttributeDesc);
74 Assert.ok(originalAttributeDesc.set === undefined);
76 accessObj[attribute] = "nope"; // accessor property with no setter: no exception in non-strict code
77 check_throws(function () { "use strict"; accessObj[attribute] = "nope"; });
79 let desc = Object.getOwnPropertyDescriptor(realObj, attribute);
80 Assert.ok("set" in desc);
81 Assert.equal(originalAttributeDesc.get, desc.get);
82 Assert.equal(undefined, desc.set);
85 // Reflect.set doesn't work either.
87 Assert.ok(!Reflect.set({}, method, "bad", accessObj));
88 Assert.equal(realObj[method], originalMethod);
91 Assert.ok(!Reflect.set({}, attribute, "bad", accessObj));
92 Assert.equal(originalAttributeDesc.get, Object.getOwnPropertyDescriptor(realObj, attribute).get);
95 // Object.defineProperty can't do anything either.
96 let names = ["expando"];
97 if (method) names.push(method);
98 if (constant) names.push(constant);
99 if (attribute) names.push(attribute);
100 for (let name of names) {
101 let originalDesc = Object.getOwnPropertyDescriptor(realObj, name);
102 check_throws(function () {
103 Object.defineProperty(accessObj, name, {configurable: true});
105 check_throws(function () {
106 Object.defineProperty(accessObj, name, {writable: true});
108 check_throws(function () {
109 Object.defineProperty(accessObj, name, {get: function () { return "lies"; }});
111 check_throws(function () {
112 Object.defineProperty(accessObj, name, {value: "bad"});
114 let desc = Object.getOwnPropertyDescriptor(realObj, name);
115 if (originalDesc === undefined) {
116 Assert.equal(undefined, desc);
118 Assert.equal(originalDesc.configurable, desc.configurable);
119 Assert.equal(originalDesc.enumerable, desc.enumerable);
120 Assert.equal(originalDesc.writable, desc.writable);
121 Assert.equal(originalDesc.value, desc.value);
122 Assert.equal(originalDesc.get, desc.get);
123 Assert.equal(originalDesc.set, desc.set);
127 // Existing properties can't be deleted.
129 Assert.equal(false, delete accessObj[method]);
130 check_throws(function () { "use strict"; delete accessObj[method]; });
131 Assert.equal(realObj[method], originalMethod);
134 Assert.equal(false, delete accessObj[constant]);
135 check_throws(function () { "use strict"; delete accessObj[constant]; });
136 Assert.equal(realObj[constant], originalConstantValue);
139 Assert.equal(false, delete accessObj[attribute]);
140 check_throws(function () { "use strict"; delete accessObj[attribute]; });
141 desc = Object.getOwnPropertyDescriptor(realObj, attribute);
142 Assert.equal(originalAttributeDesc.get, desc.get);
146 function test_twice(obj, options) {
147 test_tamperproof(obj, obj, options);
151 return new Proxy(Object.getPrototypeOf(t), handler);
154 test_tamperproof(obj, new Proxy(obj, handler), options);
157 function run_test() {
158 let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
161 constant: "TYPE_ONE_SHOT",
162 attribute: "callback"
165 let cmdline = Cu.createCommandLine([], null, Ci.nsICommandLine.STATE_INITIAL_LAUNCH);
166 test_twice(cmdline, {});
168 test_twice(Object.getPrototypeOf(cmdline), {
169 method: "getArgument",
170 constant: "STATE_INITIAL_LAUNCH",
174 // Test a tearoff object.
175 let b = xpcWrap(new TestInterfaceAll(), Ci.nsIXPCTestInterfaceB);
176 let tearoff = b.nsIXPCTestInterfaceA;
177 test_twice(tearoff, {
178 method: "QueryInterface"