Bug 1915045 Ensure decode tasks are scheduled on BufferingState::Enter() r=media...
[gecko.git] / js / xpconnect / tests / unit / test_xpcwn_tamperproof.js
blob62d57533fad2eb1b40a42571251c69d08c284587
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 */
14   someInteger: 42
17 function check_throws(f) {
18   try {
19     f();
20   } catch (exc) {
21     return;
22   }
23   throw new TypeError("Expected exception, no exception thrown");
27  * Test that XPCWrappedNative objects do not permit expando properties to be created.
28  *
29  * This function is called twice. The first time, realObj is an nsITimer XPCWN
30  * and accessObj === realObj.
31  *
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.
35  */
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.
51   let originalMethod;
52   if (method) {
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);
57   }
59   // A constant is the same thing.
60   let originalConstantValue;
61   if (constant) {
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);
67   }
69   // Assignment to a readonly accessor property with no setter doesn't work either.
70   let originalAttributeDesc;
71   if (attribute) {
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);
83   }
85   // Reflect.set doesn't work either.
86   if (method) {
87     Assert.ok(!Reflect.set({}, method, "bad", accessObj));
88     Assert.equal(realObj[method], originalMethod);
89   }
90   if (attribute) {
91     Assert.ok(!Reflect.set({}, attribute, "bad", accessObj));
92     Assert.equal(originalAttributeDesc.get, Object.getOwnPropertyDescriptor(realObj, attribute).get);
93   }
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});
104     });
105     check_throws(function () {
106       Object.defineProperty(accessObj, name, {writable: true});
107     });
108     check_throws(function () {
109       Object.defineProperty(accessObj, name, {get: function () { return "lies"; }});
110     });
111     check_throws(function () {
112       Object.defineProperty(accessObj, name, {value: "bad"});
113     });
114     let desc = Object.getOwnPropertyDescriptor(realObj, name);
115     if (originalDesc === undefined) {
116       Assert.equal(undefined, desc);
117     } else {
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);
124     }
125   }
127   // Existing properties can't be deleted.
128   if (method) {
129     Assert.equal(false, delete accessObj[method]);
130     check_throws(function () { "use strict"; delete accessObj[method]; });
131     Assert.equal(realObj[method], originalMethod);
132   }
133   if (constant) {
134     Assert.equal(false, delete accessObj[constant]);
135     check_throws(function () { "use strict"; delete accessObj[constant]; });
136     Assert.equal(realObj[constant], originalConstantValue);
137   }
138   if (attribute) {
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);
143   }
146 function test_twice(obj, options) {
147   test_tamperproof(obj, obj, options);
149   let handler = {
150     getPrototypeOf(t) {
151       return new Proxy(Object.getPrototypeOf(t), handler);
152     }
153   };
154   test_tamperproof(obj, new Proxy(obj, handler), options);
157 function run_test() {
158   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
159   test_twice(timer, {
160     method: "init",
161     constant: "TYPE_ONE_SHOT",
162     attribute: "callback"
163   });
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",
171     attribute: "length"
172   });
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"
179   });