Skip to content
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

util: restrict custom inspect function + vm.Context interaction #33690

Closed
wants to merge 4 commits into from

Conversation

addaleax
Copy link
Member

@addaleax addaleax commented Jun 1, 2020

When util.inspect() is called on an object with a custom inspect
function, and that object is from a different vm.Context,
that function will not receive any arguments that access
context-specific data anymore.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

When `util.inspect()` is called on an object with a custom inspect
function, and that object is from a different `vm.Context`,
that function will not receive any arguments that access
context-specific data anymore.
@nodejs-github-bot nodejs-github-bot added the util Issues and PRs related to the built-in util module. label Jun 1, 2020
@addaleax addaleax added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Jun 2, 2020
@nodejs-github-bot
Copy link
Collaborator

Copy link
Member

@BridgeAR BridgeAR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it's indeed surprising that the API could in theory pass through the other contexts prototypes it's not something we guard against anywhere else and the VM module is explicitly not a sandbox for security. I wonder if this is really required therefore.

for (const key of ObjectKeys(ret)) {
if ((typeof ret[key] === 'object' || typeof ret[key] === 'function') &&
ret[key] !== null) {
delete ret[key];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about cloning objects and to set the prototype to the destination's Object prototype? The returned object and stylize function could also be set to the destination's Object and Function prototype.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve thought about that, but it’s not trivial to determine what the Object prototype is in that case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like that should work well enough:

let proto = maybeCustom
while(true) {
  const next = Object.getPrototypeOf(proto)
  if (next === null) {
    if (proto === maybeCustom)
      proto = null
    break
  }
  proto = next
}

There is no guarantee that it's actually the Object prototype but it should be. It would also be possible to check for the constructor name (which is also not "reliable") but I would just use this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to have consistent behavior here rather than to take a guess at the correct prototype.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The behavior might actually cause issues inside of the vm context: we return the object prototype in the "regular" context and return a null prototype otherwise. The code inside of the VM might not be aware of that and expect an object as prototype and use e.g., hasOwnProperty. Using this pattern should work in all cases where the user did not actively manipulate the passed through functions prototype and that is very unlikely.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I understand that problem. I still think consistency is more important than the problems that could occur from mis-guessing the prototype.

lib/internal/util/inspect.js Outdated Show resolved Hide resolved
stylized = ctx.stylize(value, flavour);
} catch {}

if (typeof stylized !== 'string') return value;
Copy link
Member

@BridgeAR BridgeAR Jun 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required? The current stylize function does not check for strings and I would keep those aligned (especially, since it's not something supported to do).

We should probably only filter objects and functions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn’t the purpose of .stylize() to transform strings into other strings?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but this function is not filtering anything in the "regular context". I would try to keep the behavior aligned (and it's not a supported function anyway).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but this function is not filtering anything in the "regular context". I would try to keep the behavior aligned

I can do this if you feel strongly about it, but imo the function is broken anyway if it does not return a string.

and it's not a supported function anyway.

It’s used in examples in the documentation, so I think it qualifies as officially supported.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Also, practically speaking, I don’t think anybody is going to override this function with a custom one.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s used in examples in the documentation, so I think it qualifies as officially supported.

Thanks for pointing that out (even though there's no description of what it would do). I was not aware of that.

practically speaking, I don’t think anybody is going to override this function with a custom one.

The guard here would never be triggered with the internal stylize function.

the function is broken anyway if it does not return a string.

Absolutely. It would either trigger an error in such cases or it changes the printed output to the stringified value. It would therefore align the behavior more closely by using my suggestion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

practically speaking, I don’t think anybody is going to override this function with a custom one.

The guard here would never be triggered with the internal stylize function.

See the example above.

the function is broken anyway if it does not return a string.

Absolutely. It would either trigger an error in such cases or it changes the printed output to the stringified value. It would therefore align the behavior more closely by using my suggestion.

I can add stringification here as well, inside the try/catch. If I understand correctly, that is your main concern? I don’t see any issue with that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I’ve implemented that.

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@addaleax
Copy link
Member Author

Landed in 58bae4d

@addaleax addaleax closed this Jun 11, 2020
@addaleax addaleax deleted the util-inspect-context branch June 11, 2020 19:45
addaleax added a commit that referenced this pull request Jun 11, 2020
When `util.inspect()` is called on an object with a custom inspect
function, and that object is from a different `vm.Context`,
that function will not receive any arguments that access
context-specific data anymore.

PR-URL: #33690
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
MylesBorins pushed a commit that referenced this pull request Jul 14, 2020
When `util.inspect()` is called on an object with a custom inspect
function, and that object is from a different `vm.Context`,
that function will not receive any arguments that access
context-specific data anymore.

PR-URL: #33690
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
@MylesBorins MylesBorins mentioned this pull request Jul 14, 2020
MylesBorins pushed a commit that referenced this pull request Jul 16, 2020
When `util.inspect()` is called on an object with a custom inspect
function, and that object is from a different `vm.Context`,
that function will not receive any arguments that access
context-specific data anymore.

PR-URL: #33690
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
addaleax added a commit that referenced this pull request Sep 27, 2020
When `util.inspect()` is called on an object with a custom inspect
function, and that object is from a different `vm.Context`,
that function will not receive any arguments that access
context-specific data anymore.

PR-URL: #33690
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
addaleax added a commit that referenced this pull request Sep 28, 2020
When `util.inspect()` is called on an object with a custom inspect
function, and that object is from a different `vm.Context`,
that function will not receive any arguments that access
context-specific data anymore.

PR-URL: #33690
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
@codebytere codebytere mentioned this pull request Sep 28, 2020
@aduh95 aduh95 mentioned this pull request Oct 6, 2020
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. util Issues and PRs related to the built-in util module.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants