Skip to content

Commit b9368b6

Browse files
authored
fix(marshal): reject getters in pass-by-ref, even if it returns a function (#2438)
The prohibit-accessors check was improved by using `!('get' in descs[key])`. This should catch properties with a setter but not a getter, which will have a descriptor with `get: undefined`. Although this happens to be caught elsewhere, (because such a property reads as a non-function, triggering the "cannot serialize objects with non-methods" clause. closes #2436
1 parent 97c1bc3 commit b9368b6

File tree

2 files changed

+39
-17
lines changed

2 files changed

+39
-17
lines changed

packages/marshal/src/marshal.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,14 @@ function assertCanBeRemotable(val) {
345345
assert(!Array.isArray(val), X`Arrays cannot be pass-by-remote`);
346346
assert(val !== null, X`null cannot be pass-by-remote`);
347347

348-
const keys = ownKeys(val);
348+
const descs = getOwnPropertyDescriptors(val);
349+
const keys = ownKeys(descs); // enumerable-and-not, string-or-Symbol
349350
keys.forEach(key => {
351+
assert(
352+
// @ts-ignore
353+
!('get' in descs[key]),
354+
X`cannot serialize objects with getters like ${q(String(key))} in ${val}`,
355+
);
350356
assert.typeof(
351357
val[key],
352358
'function',

packages/marshal/test/test-marshal.js

+32-16
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,11 @@ test('records', t => {
275275
// const emptyData = { body: JSON.stringify({}), slots: [] };
276276

277277
// For objects with Symbol-named properties
278-
const sym1 = Symbol.for('sym1');
279-
const sym2 = Symbol.for('sym2');
280-
const sym3 = Symbol.for('sym3');
281-
const sym4 = Symbol.for('sym4');
278+
const symEnumData = Symbol.for('symEnumData');
279+
const symEnumFunc = Symbol.for('symEnumFunc');
280+
const symNonenumData = Symbol.for('symNonenumData');
281+
const symNonenumFunc = Symbol.for('symNonenumFunc');
282+
const symNonenumGetFunc = Symbol.for('symNonenumGetFunc');
282283

283284
function build(...opts) {
284285
const props = {};
@@ -287,21 +288,27 @@ test('records', t => {
287288
if (opt === 'enumStringData') {
288289
props.key1 = { enumerable: true, value: 'data' };
289290
} else if (opt === 'enumStringFunc') {
290-
props.key2 = { enumerable: true, value: () => 0 };
291-
} else if (opt === 'enumStringGet') {
292-
props.key3 = { enumerable: true, get: () => 0 };
291+
props.enumStringFunc = { enumerable: true, value: () => 0 };
292+
} else if (opt === 'enumStringGetData') {
293+
props.enumStringGetData = { enumerable: true, get: () => 0 };
294+
} else if (opt === 'enumStringGetFunc') {
295+
props.enumStringGetFunc = { enumerable: true, get: () => () => 0 };
296+
} else if (opt === 'enumStringSet') {
297+
props.enumStringSet = { enumerable: true, set: () => undefined };
293298
} else if (opt === 'enumSymbolData') {
294-
props[sym1] = { enumerable: true, value: 2 };
299+
props[symEnumData] = { enumerable: true, value: 2 };
295300
} else if (opt === 'enumSymbolFunc') {
296-
props[sym2] = { enumerable: true, value: () => 0 };
301+
props[symEnumFunc] = { enumerable: true, value: () => 0 };
297302
} else if (opt === 'nonenumStringData') {
298-
props.key4 = { enumerable: false, value: 3 };
303+
props.nonEnumStringData = { enumerable: false, value: 3 };
299304
} else if (opt === 'nonenumStringFunc') {
300-
props.key5 = { enumerable: false, value: () => 0 };
305+
props.nonEnumStringFunc = { enumerable: false, value: () => 0 };
301306
} else if (opt === 'nonenumSymbolData') {
302-
props[sym3] = { enumerable: false, value: 4 };
307+
props[symNonenumData] = { enumerable: false, value: 4 };
303308
} else if (opt === 'nonenumSymbolFunc') {
304-
props[sym4] = { enumerable: false, value: () => 0 };
309+
props[symNonenumFunc] = { enumerable: false, value: () => 0 };
310+
} else if (opt === 'nonenumSymbolGetFunc') {
311+
props[symNonenumGetFunc] = { enumerable: false, get: () => () => 0 };
305312
} else if (opt === 'data') {
306313
mark = 'data';
307314
} else if (opt === 'far') {
@@ -389,9 +396,18 @@ test('records', t => {
389396
shouldThrow(['far', 'enumStringData', 'enumStringFunc'], CSO);
390397

391398
// anything with getters is rejected
392-
shouldThrow(['enumStringGet'], NOACC);
393-
shouldThrow(['enumStringGet', 'enumStringData'], NOACC);
394-
shouldThrow(['enumStringGet', 'enumStringFunc'], CSO);
399+
shouldThrow(['enumStringGetData'], NOACC);
400+
shouldThrow(['enumStringGetData', 'enumStringData'], NOACC);
401+
shouldThrow(['enumStringGetData', 'enumStringFunc'], CSO);
402+
shouldThrow(['enumStringGetFunc'], NOACC);
403+
shouldThrow(['enumStringGetFunc', 'enumStringData'], NOACC);
404+
shouldThrow(['enumStringGetFunc', 'enumStringFunc'], CSO);
405+
shouldThrow(['enumStringSet'], NOACC);
406+
shouldThrow(['enumStringSet', 'enumStringData'], NOACC);
407+
shouldThrow(['enumStringSet', 'enumStringFunc'], CSO);
408+
shouldThrow(['nonenumSymbolGetFunc'], CSO);
409+
shouldThrow(['nonenumSymbolGetFunc', 'enumStringData'], CSO);
410+
shouldThrow(['nonenumSymbolGetFunc', 'enumStringFunc'], CSO);
395411

396412
// anything with symbols can only be a remotable
397413
shouldThrow(['enumSymbolData'], NOMETH);

0 commit comments

Comments
 (0)