Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@endo/marshal

Package Overview
Dependencies
Maintainers
4
Versions
45
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@endo/marshal - npm Package Compare versions

Comparing version 0.7.5 to 0.7.6

21

CHANGELOG.md

@@ -6,2 +6,23 @@ # Change Log

### [0.7.6](https://github.com/endojs/endo/compare/@endo/marshal@0.7.5...@endo/marshal@0.7.6) (2022-10-19)
### Bug Fixes
* **marshal:** Return a special error message from passStyleOf(typedArray) ([dbd498e](https://github.com/endojs/endo/commit/dbd498e30a5c3b0d2713d863bc7479ceef39cd79)), closes [#1326](https://github.com/endojs/endo/issues/1326)
* parse positive bigints correctly despite XS parsing them wrong ([#1325](https://github.com/endojs/endo/issues/1325)) ([ab31d51](https://github.com/endojs/endo/commit/ab31d51fc2df0355b45da8bbeff892c45d81c98a)), closes [#1309](https://github.com/endojs/endo/issues/1309)
* **marshal:** Add CapData encode/decode consistency assertions ([0b75021](https://github.com/endojs/endo/commit/0b75021acea174ce386c156f21de72a2150ad2dc))
* **marshal:** Consistently quote "[@qclass](https://github.com/qclass)" in error messages ([4ab6743](https://github.com/endojs/endo/commit/4ab67436e9eb3c5ad9dc39d9e050529272e8dad6))
* fix tiny typo ([#1321](https://github.com/endojs/endo/issues/1321)) ([7f3c371](https://github.com/endojs/endo/commit/7f3c371073d49b1b911d582cb8c63cbc9160d213))
* **marshal:** Detect unexpected nonenumerable properties on tagged records and remotables ([da0f11f](https://github.com/endojs/endo/commit/da0f11f73ae1f2b9f7ec8c39e56b2c244e81b45d)), closes [#1316](https://github.com/endojs/endo/issues/1316)
* unserialize makes copyRecords without problematic assign semantics ([#1304](https://github.com/endojs/endo/issues/1304)) ([5f0caf9](https://github.com/endojs/endo/commit/5f0caf95c40c55d8815818e66f63960788676cb0))
### Performance Improvements
* **marshal:** Extend new assert style to `check` calls ([11d9f97](https://github.com/endojs/endo/commit/11d9f976944b4faaea06037252cfd54e3dd91c37))
* **marshal:** Skip unnecessary assert.details calls ([053ca12](https://github.com/endojs/endo/commit/053ca12110c706439a5f3611592a0b49cb829ac6))
### [0.7.5](https://github.com/endojs/endo/compare/@endo/marshal@0.7.4...@endo/marshal@0.7.5) (2022-09-27)

@@ -8,0 +29,0 @@

2

NEWS.md
User-visible changes in `@endo/marshal`:
# v0.7.5 (2022-09-26)
# v0.7.5 (2022-09-27)

@@ -5,0 +5,0 @@ - Adds "smallcaps" encoding

{
"name": "@endo/marshal",
"version": "0.7.5",
"version": "0.7.6",
"description": "marshal",

@@ -40,10 +40,10 @@ "type": "module",

"dependencies": {
"@endo/eventual-send": "^0.16.5",
"@endo/nat": "^4.1.20",
"@endo/promise-kit": "^0.2.49"
"@endo/eventual-send": "^0.16.6",
"@endo/nat": "^4.1.21",
"@endo/promise-kit": "^0.2.50"
},
"devDependencies": {
"@endo/init": "^0.5.49",
"@endo/lockdown": "^0.1.21",
"@endo/ses-ava": "^0.2.33",
"@endo/init": "^0.5.50",
"@endo/lockdown": "^0.1.22",
"@endo/ses-ava": "^0.2.34",
"ava": "^3.12.1",

@@ -79,3 +79,3 @@ "c8": "^7.7.3"

},
"gitHead": "2d3f1a5c472aaef102e8919cbf8d0c53238d155f"
"gitHead": "8da6dc1002417c0f18cd43b351f8f62d7010260c"
}

@@ -37,2 +37,3 @@ // @ts-check

is,
entries,
fromEntries,

@@ -57,2 +58,13 @@ freeze,

/**
* @param {Encoding} encoded
* @param {string} qclass
* @returns {boolean}
*/
const qclassMatches = (encoded, qclass) =>
isObject(encoded) &&
!isArray(encoded) &&
hasQClass(encoded) &&
encoded[QCLASS] === qclass;
/**
* @typedef {object} EncodeToCapDataOptions

@@ -207,10 +219,37 @@ * @property {(

case 'remotable': {
return encodeRemotableToCapData(passable, encodeToCapDataRecur);
const encoded = encodeRemotableToCapData(
passable,
encodeToCapDataRecur,
);
if (qclassMatches(encoded, 'slot')) {
return encoded;
}
assert.fail(
X`internal: Remotable encoding must be an object with ${q(
QCLASS,
)} ${q('slot')}: ${encoded}`,
);
}
case 'promise': {
const encoded = encodePromiseToCapData(passable, encodeToCapDataRecur);
if (qclassMatches(encoded, 'slot')) {
return encoded;
}
assert.fail(
X`internal: Promise encoding must be an object with ${q(QCLASS)} ${q(
'slot',
)}: ${encoded}`,
);
}
case 'error': {
return encodeErrorToCapData(passable, encodeToCapDataRecur);
const encoded = encodeErrorToCapData(passable, encodeToCapDataRecur);
if (qclassMatches(encoded, 'error')) {
return encoded;
}
assert.fail(
X`internal: Error encoding must be an object with ${q(QCLASS)} ${q(
'error',
)}: ${encoded}`,
);
}
case 'promise': {
return encodePromiseToCapData(passable, encodeToCapDataRecur);
}
default: {

@@ -225,3 +264,3 @@ assert.fail(

const encodeToCapData = passable => {
if (ErrorHelper.canBeValid(passable, x => x)) {
if (ErrorHelper.canBeValid(passable)) {
// We pull out this special case to accommodate errors that are not

@@ -306,5 +345,5 @@ // valid Passables. For example, because they're not frozen.

}
// Assertions of the above to narrow the type.
assert(isObject(jsonEncoded));
if (hasQClass(jsonEncoded)) {
if (isArray(jsonEncoded)) {
return jsonEncoded.map(encodedVal => decodeFromCapData(encodedVal));
} else if (hasQClass(jsonEncoded)) {
const qclass = jsonEncoded[QCLASS];

@@ -314,5 +353,4 @@ assert.typeof(

'string',
X`invalid qclass typeof ${q(typeof qclass)}`,
X`invalid ${q(QCLASS)} typeof ${q(typeof qclass)}`,
);
assert(!isArray(jsonEncoded));
switch (qclass) {

@@ -367,3 +405,2 @@ // Encoding of primitives not handled by JSON

}
case 'tagged': {

@@ -378,7 +415,2 @@ // Using @ts-ignore rather than @ts-expect-error below because

}
case 'error': {
return decodeErrorFromCapData(jsonEncoded, decodeFromCapData);
}
case 'slot': {

@@ -388,5 +420,26 @@ // See note above about how the current encoding cannot reliably

// both must be the same and it doesn't matter which we call.
return decodeRemotableFromCapData(jsonEncoded, decodeFromCapData);
const decoded = decodeRemotableFromCapData(
jsonEncoded,
decodeFromCapData,
);
const passStyle = passStyleOf(decoded);
if (passStyle === 'remotable' || passStyle === 'promise') {
return decoded;
}
assert.fail(
X`internal: decodeRemotableFromCapData option must return a remotable or promise: ${decoded}`,
);
}
case 'error': {
const decoded = decodeErrorFromCapData(
jsonEncoded,
decodeFromCapData,
);
if (passStyleOf(decoded) === 'error') {
return decoded;
}
assert.fail(
X`internal: decodeErrorFromCapData option must return an error: ${decoded}`,
);
}
case 'hilbert': {

@@ -422,3 +475,2 @@ // Using @ts-ignore rather than @ts-expect-error below because

}
// @ts-expect-error This is the error case we're testing for

@@ -436,21 +488,13 @@ case 'ibid': {

}
} else if (isArray(jsonEncoded)) {
const result = [];
const { length } = jsonEncoded;
for (let i = 0; i < length; i += 1) {
result[i] = decodeFromCapData(jsonEncoded[i]);
}
return result;
} else {
assert(typeof jsonEncoded === 'object' && jsonEncoded !== null);
const result = {};
for (const name of ownKeys(jsonEncoded)) {
assert.typeof(
name,
'string',
X`Property ${name} of ${jsonEncoded} must be a string`,
);
result[name] = decodeFromCapData(jsonEncoded[name]);
}
return result;
const decodeEntry = ([name, encodedVal]) => {
typeof name === 'string' ||
assert.fail(
X`Property ${q(name)} of ${jsonEncoded} must be a string`,
);
return [name, decodeFromCapData(encodedVal)];
};
const decodedEntries = entries(jsonEncoded).map(decodeEntry);
return fromEntries(decodedEntries);
}

@@ -457,0 +501,0 @@ };

@@ -31,3 +31,3 @@ // @ts-check

const { isArray } = Array;
const { is, fromEntries } = Object;
const { is, entries, fromEntries } = Object;
const { details: X, quote: q } = assert;

@@ -286,3 +286,3 @@

const encodeToSmallcaps = passable => {
if (ErrorHelper.canBeValid(passable, x => x)) {
if (ErrorHelper.canBeValid(passable)) {
// We pull out this special case to accommodate errors that are not

@@ -404,3 +404,5 @@ // valid Passables. For example, because they're not frozen.

}
case '+':
case '+': {
return BigInt(encoding.slice(1));
}
case '-': {

@@ -477,7 +479,4 @@ return BigInt(encoding);

const result = {};
for (const encodedName of ownKeys(encoding)) {
// TypeScript confused about `||` control flow so use `if` instead
// https://github.com/microsoft/TypeScript/issues/50739
if (typeof encodedName !== 'string') {
const decodeEntry = ([encodedName, encodedVal]) => {
typeof encodedName === 'string' ||
assert.fail(

@@ -488,3 +487,2 @@ X`Property name ${q(

);
}
!encodedName.startsWith('#') ||

@@ -499,5 +497,6 @@ assert.fail(

);
result[name] = decodeFromSmallcaps(encoding[encodedName]);
}
return result;
return [name, decodeFromSmallcaps(encodedVal)];
};
const decodedEntries = entries(encoding).map(decodeEntry);
return fromEntries(decodedEntries);
}

@@ -504,0 +503,0 @@ default: {

@@ -20,24 +20,19 @@ // @ts-check

canBeValid: (candidate, check) =>
check(isArray(candidate), X`Array expected: ${candidate}`),
isArray(candidate) ||
(!!check && check(false, X`Array expected: ${candidate}`)),
assertValid: (candidate, passStyleOfRecur) => {
CopyArrayHelper.canBeValid(candidate, assertChecker);
assert(
getPrototypeOf(candidate) === arrayPrototype,
X`Malformed array: ${candidate}`,
TypeError,
);
getPrototypeOf(candidate) === arrayPrototype ||
assert.fail(X`Malformed array: ${candidate}`, TypeError);
// Since we're already ensured candidate is an array, it should not be
// possible for the following test to fail
checkNormalProperty(candidate, 'length', 'string', false, assertChecker);
checkNormalProperty(candidate, 'length', false, assertChecker);
const len = candidate.length;
for (let i = 0; i < len; i += 1) {
checkNormalProperty(candidate, i, 'number', true, assertChecker);
checkNormalProperty(candidate, i, true, assertChecker);
}
assert(
// +1 for the 'length' property itself.
ownKeys(candidate).length === len + 1,
X`Arrays must not have non-indexes: ${candidate}`,
TypeError,
);
// +1 for the 'length' property itself.
ownKeys(candidate).length === len + 1 ||
assert.fail(X`Arrays must not have non-indexes: ${candidate}`, TypeError);
// Recursively validate that each member is passable.

@@ -44,0 +39,0 @@ candidate.every(v => !!passStyleOfRecur(v));

@@ -11,3 +11,3 @@ // @ts-check

const { details: X } = assert;
const { details: X, quote: q } = assert;
const { ownKeys } = Reflect;

@@ -28,5 +28,6 @@ const {

canBeValid: (candidate, check) => {
const reject = !!check && (details => check(false, details));
const proto = getPrototypeOf(candidate);
if (proto !== objectPrototype && proto !== null) {
return check(false, X`Unexpected prototype for: ${candidate}`);
return reject && reject(X`Unexpected prototype for: ${candidate}`);
}

@@ -39,5 +40,7 @@ const descs = getOwnPropertyDescriptors(candidate);

// Pass by copy
return check(
false,
X`Records can only have string-named own properties: ${candidate}`,
return (
(reject &&
reject(
X`Records can only have string-named own properties: ${candidate}`,
))
);

@@ -47,5 +50,7 @@ }

if (canBeMethod(desc.value)) {
return check(
false,
X`Records cannot contain non-far functions because they may be methods of an implicit Remotable: ${candidate}`,
return (
(reject &&
reject(
X`Records cannot contain non-far functions because they may be methods of an implicit Remotable: ${candidate}`,
))
);

@@ -60,3 +65,7 @@ }

for (const name of ownKeys(candidate)) {
checkNormalProperty(candidate, name, 'string', true, assertChecker);
typeof name === 'string' ||
assert.fail(
X`${q(name)} must be a string-named property: ${candidate}`,
);
checkNormalProperty(candidate, name, true, assertChecker);
}

@@ -63,0 +72,0 @@ // Recursively validate that each member is passable.

@@ -11,4 +11,4 @@ export function getErrorConstructor(name: any): ErrorConstructor | undefined;

* To resolve this, such a malformed error object will still pass
* `canBeValid(err, x => x)` so marshal can use this for top
* level error to report from, even if it would not actually validate.
* `canBeValid` so marshal can use this for top level error to report from,
* even if it would not actually validate.
* Instead, the diagnostics that `assertError` would have reported are

@@ -15,0 +15,0 @@ * attached as notes to the malformed error. Thus, a malformed

@@ -38,4 +38,4 @@ // @ts-check

* To resolve this, such a malformed error object will still pass
* `canBeValid(err, x => x)` so marshal can use this for top
* level error to report from, even if it would not actually validate.
* `canBeValid` so marshal can use this for top level error to report from,
* even if it would not actually validate.
* Instead, the diagnostics that `assertError` would have reported are

@@ -51,5 +51,6 @@ * attached as notes to the malformed error. Thus, a malformed

canBeValid: (candidate, check) => {
const reject = !!check && (details => check(false, details));
// TODO: Need a better test than instanceof
if (!(candidate instanceof Error)) {
return check(false, X`Error expected: ${candidate}`);
return reject && reject(X`Error expected: ${candidate}`);
}

@@ -62,3 +63,3 @@ const proto = getPrototypeOf(candidate);

// Only terminate if check throws
check(false, note);
reject && reject(note);
assert.note(candidate, note);

@@ -77,3 +78,3 @@ }

// Only terminate if check throws
check(false, note);
reject && reject(note);
assert.note(candidate, note);

@@ -85,3 +86,3 @@ }

// Only terminate if check throws
check(false, note);
reject && reject(note);
assert.note(candidate, note);

@@ -92,3 +93,3 @@ }

// Only terminate if check throws
check(false, note);
reject && reject(note);
assert.note(candidate, note);

@@ -95,0 +96,0 @@ }

@@ -23,5 +23,5 @@ export type Checker = import('../types.js').Checker;

*/
canBeValid: (candidate: any, check: Checker) => boolean;
canBeValid: (candidate: any, check?: import("../types.js").Checker | undefined) => boolean;
assertValid: (candidate: any, passStyleOfRecur: PassStyleOf) => void;
};
//# sourceMappingURL=internal-types.d.ts.map

@@ -26,3 +26,3 @@ // @ts-check

*
* @property {(candidate: any, check: Checker) => boolean} canBeValid
* @property {(candidate: any, check?: Checker) => boolean} canBeValid
* If `canBeValid` returns true, then the candidate would

@@ -29,0 +29,0 @@ * definitely not be valid for any of the other helpers.

export function hasOwnPropertyOf(obj: any, prop: any): any;
export function isObject(val: any): boolean;
export function isTypedArray(object: unknown): boolean;
export const PASS_STYLE: unique symbol;

@@ -14,9 +15,9 @@ export function canBeMethod(func: any): boolean;

export const assertChecker: Checker;
export function checkNormalProperty(candidate: any, propertyName: string | number | symbol, nameType: string, shouldBeEnumerable: boolean, check: Checker): boolean;
export function checkNormalProperty(candidate: any, propertyName: string | number | symbol, shouldBeEnumerable: boolean, check?: import("../types.js").Checker | undefined): boolean;
export function getTag(tagRecord: any): any;
export function checkTagRecord(tagRecord: {
[PASS_STYLE]: string;
}, passStyle: PassStyle, check: Checker): boolean;
}, passStyle: PassStyle, check?: import("../types.js").Checker | undefined): boolean;
export type Checker = import('../types.js').Checker;
export type PassStyle = import('../types.js').PassStyle;
//# sourceMappingURL=passStyle-helpers.d.ts.map

@@ -9,4 +9,6 @@ // @ts-check

const { details: X, quote: q } = assert;
const { isArray } = Array;
const {
getOwnPropertyDescriptor,
getPrototypeOf,
hasOwnProperty: objectHasOwnProperty,

@@ -16,4 +18,13 @@ isFrozen,

const { apply } = Reflect;
const { isArray } = Array;
const { toStringTag: toStringTagSymbol } = Symbol;
const typedArrayPrototype = getPrototypeOf(Uint8Array.prototype);
const typedArrayToStringTagDesc = getOwnPropertyDescriptor(
typedArrayPrototype,
toStringTagSymbol,
);
assert(typedArrayToStringTagDesc);
const getTypedArrayToStringTag = typedArrayToStringTagDesc.get;
assert(typeof getTypedArrayToStringTag === 'function');
export const hasOwnPropertyOf = (obj, prop) =>

@@ -26,2 +37,14 @@ apply(objectHasOwnProperty, obj, [prop]);

/**
* Duplicates packages/ses/src/make-hardener.js to avoid a dependency.
*
* @param {unknown} object
*/
export const isTypedArray = object => {
// The object must pass a brand check or toStringTag will return undefined.
const tag = apply(getTypedArrayToStringTag, object, []);
return tag !== undefined;
};
harden(isTypedArray);
export const PASS_STYLE = Symbol.for('passStyle');

@@ -61,7 +84,8 @@

/**
* Checks for the presence and enumerability of an own data property.
*
* @param {Object} candidate
* @param {string|number|symbol} propertyName
* @param {string} nameType
* @param {boolean} shouldBeEnumerable
* @param {Checker} check
* @param {Checker} [check]
* @returns {boolean}

@@ -72,34 +96,31 @@ */

propertyName,
nameType,
shouldBeEnumerable,
check,
) => {
const reject = !!check && (details => check(false, details));
const desc = getOwnPropertyDescriptor(candidate, propertyName);
if (desc === undefined) {
return check(false, X`${q(propertyName)} property expected: ${candidate}`);
return (
reject && reject(X`${q(propertyName)} property expected: ${candidate}`)
);
}
return (
check(
// eslint-disable-next-line valid-typeof
nameType === undefined || typeof propertyName === nameType,
X`${q(propertyName)} must be a ${q(
nameType,
)}-named property: ${candidate}`,
) &&
(hasOwnPropertyOf(desc, 'value') ||
check(
false,
X`${q(propertyName)} must not be an accessor property: ${candidate}`,
)) &&
(reject &&
reject(
X`${q(propertyName)} must not be an accessor property: ${candidate}`,
))) &&
(shouldBeEnumerable
? check(
!!desc.enumerable,
X`${q(propertyName)} must be an enumerable property: ${candidate}`,
)
: check(
!desc.enumerable,
X`${q(
propertyName,
)} must not be an enumerable property: ${candidate}`,
))
? desc.enumerable ||
(reject &&
reject(
X`${q(propertyName)} must be an enumerable property: ${candidate}`,
))
: !desc.enumerable ||
(reject &&
reject(
X`${q(
propertyName,
)} must not be an enumerable property: ${candidate}`,
)))
);

@@ -115,33 +136,31 @@ };

* @param {PassStyle} passStyle
* @param {Checker} check
* @param {Checker} [check]
* @returns {boolean}
*/
export const checkTagRecord = (tagRecord, passStyle, check) => {
const reject = !!check && (details => check(false, details));
return (
((typeof tagRecord === 'object' && tagRecord !== null) ||
check(false, X`A non-object cannot be a tagRecord: ${tagRecord}`)) &&
check(isFrozen(tagRecord), X`A tagRecord must be frozen: ${tagRecord}`) &&
(isObject(tagRecord) ||
(reject &&
reject(X`A non-object cannot be a tagRecord: ${tagRecord}`))) &&
(isFrozen(tagRecord) ||
(reject && reject(X`A tagRecord must be frozen: ${tagRecord}`))) &&
(!isArray(tagRecord) ||
check(false, X`An array cannot be a tagRecords: ${tagRecord}`)) &&
checkNormalProperty(tagRecord, PASS_STYLE, 'symbol', false, check) &&
check(
tagRecord[PASS_STYLE] === passStyle,
X`Expected ${q(passStyle)}, not ${q(
tagRecord[PASS_STYLE],
)}: ${tagRecord}`,
) &&
checkNormalProperty(
tagRecord,
Symbol.toStringTag,
'symbol',
false,
check,
) &&
(reject && reject(X`An array cannot be a tagRecords: ${tagRecord}`))) &&
checkNormalProperty(tagRecord, PASS_STYLE, false, check) &&
(tagRecord[PASS_STYLE] === passStyle ||
(reject &&
reject(
X`Expected ${q(passStyle)}, not ${q(
tagRecord[PASS_STYLE],
)}: ${tagRecord}`,
))) &&
checkNormalProperty(tagRecord, Symbol.toStringTag, false, check) &&
(typeof getTag(tagRecord) === 'string' ||
check(
false,
X`A [Symbol.toString]-named property must be a string: ${tagRecord}`,
))
(reject &&
reject(
X`A [Symbol.toStringTag]-named property must be a string: ${tagRecord}`,
)))
);
};
harden(checkTagRecord);

@@ -34,18 +34,21 @@ // @ts-check

* @param {InterfaceSpec} iface
* @param {Checker} check
* @param {Checker} [check]
*/
const checkIface = (iface, check) => {
const reject = !!check && (details => check(false, details));
return (
// TODO other possible ifaces, once we have third party veracity
(typeof iface === 'string' ||
check(
false,
X`For now, interface ${iface} must be a string; unimplemented`,
)) &&
check(
iface === 'Remotable' || iface.startsWith('Alleged: '),
X`For now, iface ${q(
iface,
)} must be "Remotable" or begin with "Alleged: "; unimplemented`,
)
(reject &&
reject(
X`For now, interface ${iface} must be a string; unimplemented`,
))) &&
(iface === 'Remotable' ||
iface.startsWith('Alleged: ') ||
(reject &&
reject(
X`For now, iface ${q(
iface,
)} must be "Remotable" or begin with "Alleged: "; unimplemented`,
)))
);

@@ -69,6 +72,7 @@ };

* @param {any} original
* @param {Checker} check
* @param {Checker} [check]
* @returns {boolean}
*/
const checkRemotableProtoOf = (original, check) => {
const reject = !!check && (details => check(false, details));
// A valid remotable object must inherit from a "tag record" -- a

@@ -102,17 +106,16 @@ // plain-object prototype consisting of only

if (
!(
check(
// Since we're working with TypeScript's unsound type system, mostly
// to catch accidents and to provide IDE support, we type arguments
// like `val` according to what they are supposed to be. The following
// tests for a particular violation. However, TypeScript complains
// because *if the declared type were accurate*, then the condition
// would always return true.
// @ts-ignore TypeScript assumes what we're trying to check
proto !== objectPrototype,
X`Remotables must be explicitly declared: ${q(original)}`,
) && checkTagRecord(proto, 'remotable', check)
)
) {
// Since we're working with TypeScript's unsound type system, mostly
// to catch accidents and to provide IDE support, we type arguments
// like `val` according to what they are supposed to be. The following
// tests for a particular violation. However, TypeScript complains
// because *if the declared type were accurate*, then the condition
// would always return true.
// @ts-ignore TypeScript assumes what we're trying to check
if (proto === objectPrototype) {
return (
reject &&
reject(X`Remotables must be explicitly declared: ${q(original)}`)
);
}
if (!checkTagRecord(proto, 'remotable', check)) {
return false;

@@ -122,36 +125,44 @@ }

if (typeof original === 'object') {
if (
!check(
protoProto === objectPrototype || protoProto === null,
X`The Remotable Proto marker cannot inherit from anything unusual`,
)
) {
return false;
const valid = protoProto === objectPrototype || protoProto === null;
if (!valid) {
return (
reject &&
reject(
X`The Remotable Proto marker cannot inherit from anything unusual`,
)
);
}
} else if (typeof original === 'function') {
if (
!check(
protoProto === functionPrototype ||
getPrototypeOf(protoProto) === functionPrototype,
X`For far functions, the Remotable Proto marker must inherit from Function.prototype, in ${original}`,
)
) {
return false;
const valid =
protoProto === functionPrototype ||
getPrototypeOf(protoProto) === functionPrototype;
if (!valid) {
return (
reject &&
reject(
X`For far functions, the Remotable Proto marker must inherit from Function.prototype, in ${original}`,
)
);
}
} else {
// XXX Should this be reject instead?
assert.fail(X`unrecognized typeof ${original}`);
}
// Typecasts needed due to https://github.com/microsoft/TypeScript/issues/1863
const passStyleKey = /** @type {unknown} */ (PASS_STYLE);
const tagKey = /** @type {unknown} */ (Symbol.toStringTag);
const {
[PASS_STYLE]: _passStyle,
[Symbol.toStringTag]: iface,
...rest
} = proto;
// checkTagRecord already verified PASS_STYLE and Symbol.toStringTag own data properties.
[/** @type {string} */ (passStyleKey)]: _passStyleDesc,
[/** @type {string} */ (tagKey)]: { value: iface },
...restDescs
} = getOwnPropertyDescriptors(proto);
return (
(ownKeys(rest).length === 0 ||
check(
false,
X`Unexpected properties on Remotable Proto ${ownKeys(rest)}`,
)) &&
(ownKeys(restDescs).length === 0 ||
(reject &&
reject(
X`Unexpected properties on Remotable Proto ${ownKeys(restDescs)}`,
))) &&
checkIface(iface, check)

@@ -163,9 +174,9 @@ );

* @param {Remotable} val
* @param {Checker} check
* @param {Checker} [check]
* @returns {boolean}
*/
const checkRemotable = (val, check) => {
const not = (cond, details) => !check(cond, details);
if (not(isFrozen(val), X`cannot serialize non-frozen objects like ${val}`)) {
return false;
const reject = !!check && (details => check(false, details));
if (!isFrozen(val)) {
return reject && reject(X`cannot serialize non-frozen objects like ${val}`);
}

@@ -204,10 +215,10 @@ // eslint-disable-next-line no-use-before-define

canBeValid: (candidate, check) => {
if (
!(
(isObject(candidate) ||
check(false, X`cannot serialize non-objects like ${candidate}`)) &&
check(!isArray(candidate), X`Arrays cannot be pass-by-remote`)
)
) {
return false;
const reject = !!check && (details => check(false, details));
if (!isObject(candidate)) {
return (
(reject && reject(X`cannot serialize non-objects like ${candidate}`))
);
} else if (isArray(candidate)) {
// TODO: X`cannot serialize arrays as remotable: ${candidate}`?
return reject && reject(X`Arrays cannot be pass-by-remote`);
}

@@ -218,20 +229,24 @@

const keys = ownKeys(descs); // enumerable-and-not, string-or-Symbol
return keys.every(
key =>
return keys.every(key => {
return (
// Typecast needed due to https://github.com/microsoft/TypeScript/issues/1863
check(
hasOwnPropertyOf(descs[/** @type {string} */ (key)], 'value'),
X`cannot serialize Remotables with accessors like ${q(
String(key),
)} in ${candidate}`,
) &&
check(
canBeMethod(candidate[key]),
X`cannot serialize Remotables with non-methods like ${q(
String(key),
)} in ${candidate}`,
) &&
((hasOwnPropertyOf(descs[/** @type {string} */ (key)], 'value') ||
(reject &&
reject(
X`cannot serialize Remotables with accessors like ${q(
String(key),
)} in ${candidate}`,
))) &&
(canBeMethod(candidate[key]) ||
(reject &&
reject(
X`cannot serialize Remotables with non-methods like ${q(
String(key),
)} in ${candidate}`,
))) &&
(key !== PASS_STYLE ||
check(false, X`A pass-by-remote cannot shadow ${q(PASS_STYLE)}`)),
);
(reject &&
reject(X`A pass-by-remote cannot shadow ${q(PASS_STYLE)}`))))
);
});
} else if (typeof candidate === 'function') {

@@ -243,19 +258,18 @@ // Far functions cannot be methods, and cannot have methods.

return (
(check(
nameDesc && typeof nameDesc.value === 'string',
X`Far function name must be a string, in ${candidate}`,
) &&
(((nameDesc && typeof nameDesc.value === 'string') ||
(reject &&
reject(X`Far function name must be a string, in ${candidate}`))) &&
((lengthDesc && typeof lengthDesc.value === 'number') ||
check(
false,
X`Far function length must be a number, in ${candidate}`,
)) &&
(reject &&
reject(
X`Far function length must be a number, in ${candidate}`,
))) &&
(restKeys.length === 0 ||
check(
false,
X`Far functions unexpected properties besides .name and .length ${restKeys}`,
)))
(reject &&
reject(
X`Far functions unexpected properties besides .name and .length ${restKeys}`,
))))
);
} else {
return check(false, X`unrecognized typeof ${candidate}`);
return reject && reject(X`unrecognized typeof ${candidate}`);
}

@@ -262,0 +276,0 @@ },

@@ -20,2 +20,3 @@ // @ts-check

const checkPromiseOwnKeys = (pr, check) => {
const reject = details => check(false, details);
const keys = ownKeys(pr);

@@ -32,4 +33,3 @@

if (unknownKeys.length !== 0) {
return check(
false,
return reject(
X`${pr} - Must not have any own properties: ${q(unknownKeys)}`,

@@ -79,4 +79,3 @@ );

}
return check(
false,
return reject(
X`Unexpected Node async_hooks additions to promise: ${pr}.${q(

@@ -107,11 +106,16 @@ String(key),

*/
const checkSafePromise = (pr, check) =>
check(isFrozen(pr), X`${pr} - Must be frozen`) &&
check(isPromise(pr), X`${pr} - Must be a promise`) &&
(getPrototypeOf(pr) === Promise.prototype ||
check(
false,
X`${pr} - Must inherit from Promise.prototype: ${q(getPrototypeOf(pr))}`,
)) &&
checkPromiseOwnKeys(/** @type {Promise} */ (pr), check);
const checkSafePromise = (pr, check) => {
const reject = details => check(false, details);
return (
(isFrozen(pr) || reject(X`${pr} - Must be frozen`)) &&
(isPromise(pr) || reject(X`${pr} - Must be a promise`)) &&
(getPrototypeOf(pr) === Promise.prototype ||
reject(
X`${pr} - Must inherit from Promise.prototype: ${q(
getPrototypeOf(pr),
)}`,
)) &&
checkPromiseOwnKeys(/** @type {Promise} */ (pr), check)
);
};
harden(checkSafePromise);

@@ -118,0 +122,0 @@

@@ -14,3 +14,7 @@ // @ts-check

const { ownKeys } = Reflect;
const { getPrototypeOf, prototype: objectPrototype } = Object;
const {
getOwnPropertyDescriptors,
getPrototypeOf,
prototype: objectPrototype,
} = Object;

@@ -28,22 +32,25 @@ /**

TaggedHelper.canBeValid(candidate, assertChecker);
assert.equal(
getPrototypeOf(candidate),
objectPrototype,
X`Unexpected prototype for: ${candidate}`,
);
getPrototypeOf(candidate) === objectPrototype ||
assert.fail(X`Unexpected prototype for: ${candidate}`);
// Typecasts needed due to https://github.com/microsoft/TypeScript/issues/1863
const passStyleKey = /** @type {unknown} */ (PASS_STYLE);
const tagKey = /** @type {unknown} */ (Symbol.toStringTag);
const {
[PASS_STYLE]: _passStyle, // checkTagRecord already checked
[Symbol.toStringTag]: _label, // checkTagRecord already checked
payload: _payload, // value checked by recursive walk at the end
...rest
} = candidate;
(ownKeys(rest).length === 0) ||
assert.fail(X`Unexpected properties on Remotable Proto ${ownKeys(rest)}`);
// checkTagRecord already verified PASS_STYLE and Symbol.toStringTag own data properties.
[/** @type {string} */ (passStyleKey)]: _passStyleDesc,
[/** @type {string} */ (tagKey)]: _labelDesc,
payload: _payloadDesc, // value checked by recursive walk at the end
...restDescs
} = getOwnPropertyDescriptors(candidate);
(ownKeys(restDescs).length === 0) ||
assert.fail(
X`Unexpected properties on tagged record ${ownKeys(restDescs)}`,
);
checkNormalProperty(candidate, 'payload', 'string', true, assertChecker);
checkNormalProperty(candidate, 'payload', true, assertChecker);
// Recursively validate that each member is passable.
!!passStyleOfRecur(candidate.payload);
passStyleOfRecur(candidate.payload);
},
});

@@ -6,3 +6,7 @@ // @ts-check

import { isPromise } from '@endo/promise-kit';
import { isObject, PASS_STYLE } from './helpers/passStyle-helpers.js';
import {
isObject,
isTypedArray,
PASS_STYLE,
} from './helpers/passStyle-helpers.js';

@@ -145,6 +149,11 @@ import { CopyArrayHelper } from './helpers/copyArray.js';

}
(isFrozen(inner)) ||
if (!isFrozen(inner)) {
assert.fail(
X`Cannot pass non-frozen objects like ${inner}. Use harden()`,
// TypedArrays get special treatment in harden()
// and a corresponding special error message here.
isTypedArray(inner)
? X`Cannot pass mutable typed arrays like ${inner}.`
: X`Cannot pass non-frozen objects like ${inner}. Use harden()`,
);
}
if (isPromise(inner)) {

@@ -166,3 +175,3 @@ assertSafePromise(inner);

for (const helper of passStyleHelpers) {
if (helper.canBeValid(inner, x => x)) {
if (helper.canBeValid(inner)) {
helper.assertValid(inner, passStyleOfRecur);

@@ -169,0 +178,0 @@ return helper.styleName;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc