-
Notifications
You must be signed in to change notification settings - Fork 212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rework xsnap/src/object-inspect.js
#4767
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is how far I got before the dinner bell rang...
packages/xsnap/src/object-inspect.js
Outdated
const mapSize = Object.getOwnPropertyDescriptor(Map.prototype, 'size').get; | ||
const mapForEach = Map.prototype.forEach; | ||
const setSize = Object.getOwnPropertyDescriptor(Set.prototype, 'size').get; | ||
const setForEach = Set.prototype.forEach; | ||
const weakMapHas = WeakMap.prototype.has; | ||
const weakSetHas = WeakSet.prototype.has; | ||
const weakRefDeref = WeakRef.prototype.deref; | ||
const booleanValueOf = Boolean.prototype.valueOf; | ||
const objectToString = Object.prototype.toString; | ||
const functionToString = Function.prototype.toString; | ||
const $match = String.prototype.match; | ||
const $slice = String.prototype.slice; | ||
const $replace = String.prototype.replace; | ||
const $toUpperCase = String.prototype.toUpperCase; | ||
const $test = RegExp.prototype.test; | ||
const $concat = Array.prototype.concat; | ||
const $join = Array.prototype.join; | ||
const bigIntValueOf = BigInt.prototype.valueOf; | ||
const getOwnPropertySymbols = Object.getOwnPropertySymbols; | ||
const symToString = Symbol.prototype.toString; | ||
// ie, `has-tostringtag/shams | ||
const toStringTag = Symbol.toStringTag; | ||
const isEnumerable = Object.prototype.propertyIsEnumerable; | ||
const dateToISOString = Date.prototype.toISOString; | ||
|
||
const gPO = Reflect.getPrototypeOf; | ||
const hasOwn = Object.prototype.hasOwnProperty; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why alias all this stuff? I have some vague ideas, but I don't understand well enough to maintain this list. In particular, the mapSize
and setSize
contortions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this meant to guard against some other vetted shim mutating one of the standard objects? I like defensive, but is that still in the threat model?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this meant to guard against some other vetted shim mutating one of the standard objects? I like defensive, but is that still in the threat model?
This is a light refactoring of the original object-inspect.js
code. I didn't rewrite it further because I didn't see the need to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a locked down environment this shouldn't be necessary. If we do want to adopt this style for this file, maybe we could enable the eslint rule to disable polymorphic calls like the one endo uses: endojs/endo#827
@FUDCo , @michaelfig is here doing a permissive unprivileged object-quotation system much like I originally wanted to do for With that as background, could you confirm that, once this PR is merged, you would not object to |
How does test coverage for this version compare with what it replaces? Help me figure it out? https://agoric-sdk-coverage.netlify.app/packages/xsnap/lib/object-inspect.js.html ? |
@erights, let's discuss how to review this:
|
@michaelfig , Why would it need to be a vetted shim, rather than unprivileged code running under normal post-lockdown SES restrictions? |
Sorry, my confusion. Yes, it's unprivileged code in the post-lockdown start compartment. I'd like ideally to run it in a compartment, but am unsure about how. |
Excellent. Yes, let's find time to discuss. Thanks! |
Really? It looks to me like we run this to build the console that we give to SES: agoric-sdk/packages/xsnap/lib/ses-boot.js Lines 1 to 2 in 17e161e
|
Ahh, yes. The reason it's a vetted shim is because of initialisation ordering in SES, not because we actually need any of that power. Instinctively, we should be able to relax that constraint and install the object inspector from a post-lockdown compartment. |
Yes, we used to have a lo-fi console arg quoter that we used for SES initialization, and then we replaced the lo-fi console quoter with https://github.com/Agoric/agoric-sdk/blob/a3306d01d8e87c4bc7483a61e42cc30b006feb81/packages/xsnap/lib/console-shim.js |
TIL |
ac9e24a
to
0c64c64
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ya shoor okay
0c64c64
to
57c121f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lots of minor nits but one genuine bug -- where an implementation limitation of the SES shim led you astray.
packages/xsnap/src/object-inspect.js
Outdated
const mapSize = Object.getOwnPropertyDescriptor(Map.prototype, 'size').get; | ||
const mapForEach = Map.prototype.forEach; | ||
const setSize = Object.getOwnPropertyDescriptor(Set.prototype, 'size').get; | ||
const setForEach = Set.prototype.forEach; | ||
const weakMapHas = WeakMap.prototype.has; | ||
const weakSetHas = WeakSet.prototype.has; | ||
const weakRefDeref = WeakRef.prototype.deref; | ||
const booleanValueOf = Boolean.prototype.valueOf; | ||
const objectToString = Object.prototype.toString; | ||
const functionToString = Function.prototype.toString; | ||
const $match = String.prototype.match; | ||
const $slice = String.prototype.slice; | ||
const $replace = String.prototype.replace; | ||
const $toUpperCase = String.prototype.toUpperCase; | ||
const $test = RegExp.prototype.test; | ||
const $concat = Array.prototype.concat; | ||
const $join = Array.prototype.join; | ||
const bigIntValueOf = BigInt.prototype.valueOf; | ||
const getOwnPropertySymbols = Object.getOwnPropertySymbols; | ||
const symToString = Symbol.prototype.toString; | ||
// ie, `has-tostringtag/shams | ||
const toStringTag = Symbol.toStringTag; | ||
const isEnumerable = Object.prototype.propertyIsEnumerable; | ||
const dateToISOString = Date.prototype.toISOString; | ||
|
||
const gPO = Reflect.getPrototypeOf; | ||
const hasOwn = Object.prototype.hasOwnProperty; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a locked down environment this shouldn't be necessary. If we do want to adopt this style for this file, maybe we could enable the eslint rule to disable polymorphic calls like the one endo uses: endojs/endo#827
packages/xsnap/src/object-inspect.js
Outdated
const gPO = Reflect.getPrototypeOf; | ||
const hasOwn = Object.prototype.hasOwnProperty; | ||
|
||
export default function inspect0(obj, opts = {}, depth = 0, seen = new Set()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: s/obj/val
packages/xsnap/src/object-inspect.js
Outdated
if (typeof depth === 'undefined') { | ||
depth = 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're already setting a default above, aren't we?
const indent = getIndent(opts, depth); | ||
|
||
if (seen.has(obj)) { | ||
return '[Circular]'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't duplicated values can also trigger this. E.g.
const obj = {};
inspect0({foo: obj, bar: obj});
return false; | ||
} | ||
try { | ||
weakRefDeref.call(x); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is sad as it forces the target to stay alive another turn
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anything I should try to do about that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see another way. deref
is the only thing that brand checks the [[WeakRefTarget]]
slot.
remaining > 1 ? 's' : '' | ||
}`; | ||
return ( | ||
inspectString($slice.call(str, 0, opts.maxStringLength), opts) + trailer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
technically this can cut in the middle of a surrogate pair.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anything I should do about that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We add a trailer which starts with a non surrogate char, so it may just technically be an invalid string, but all implementations handle malformed strings like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mhofman I don't see how that trailer helps. In the case in question, it would already be invalid as a unicode string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what I meant is that this won't be concatenated with another string which may start with the second half of another surrogate pair, so if we split in the middle, the first half will just be detected as a broken pair, and processed as such by every algorithms which handles strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I understand now, and it is a valid concern.
packages/xsnap/src/object-inspect.js
Outdated
continue; | ||
} | ||
if ($test.call(/[^\w$]/, key)) { | ||
xs.push(`${inspect(key, obj)}: ${inspect(obj[key], obj)}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confused why obj
is included in ${inspect(key, obj)}
but is not in the symbol version below: ${inspect(syms[j])}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The symbol cannot possibly be the same as the object, so no need to check it for circularity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the symbol's value can refer to the object, can't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the symbol's value can refer to the object, can't it?
As far as I read it, the inspect(syms[j])
inspects the symbol itself, not the symbol property's value.
y'all are reviewing this better than I could possibly do, I'll defer to you |
57c121f
to
6041179
Compare
116c1f7
to
e1f4ff6
Compare
const src = objectInspectSources.replace( | ||
/(^|\s)(export\s+default)(\s+)/g, | ||
'$1/* $2 */$3', | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OMG. Definitely worth a comment to explain why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely!
remaining > 1 ? 's' : '' | ||
}`; | ||
return ( | ||
inspectString($slice.call(str, 0, opts.maxStringLength), opts) + trailer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mhofman I don't see how that trailer helps. In the case in question, it would already be invalid as a unicode string.
const registered = symKeyFor(obj); | ||
if (registered !== undefined) { | ||
// Registered symbol. | ||
return `Symbol.for(${registered})`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
d78fc36
to
a92037a
Compare
a92037a
to
e075ea0
Compare
closes: #2146
closes: #4814
Description
This is the next step toward improving the code used by xsnap to provide a working
console.log
.Security Considerations
Improves the security of our xsnap process by providing a vetted shim that really has been vetted.
Documentation Considerations
Testing Considerations