-
Notifications
You must be signed in to change notification settings - Fork 80
fix(ses): fix #2951 stronger sniffing for v8 #2952
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
base: master
Are you sure you want to change the base?
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.
Pull Request Overview
This PR implements stronger platform detection specifically for v8 JavaScript engine in the SES error constructor taming functionality, addressing issue #2951. The change improves the reliability of v8 detection by testing the prepareStackTrace mechanism rather than just checking for the presence of captureStackTrace.
Key changes:
- Enhanced v8 detection logic using
prepareStackTracefunctionality testing - Added import for
isArrayutility function - Replaced simple function existence check with comprehensive platform sniffing
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
6c7e4b6 to
3630c30
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.
Pull Request Overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
|
Get ready, this will hurt. Hermes does implement CallSite implementation is incomplete, mainly because Hermes doesn't have a concept of source files.
Result of calling all V8 CallSite methods:
Result of calling all the same methods in Hermes
We could either make sniffing even stronger by testing for usefulness of |
|
I've pushed two commits:
|
41009a8 to
0fca5fc
Compare
| ;;;; | ||
| var TEST; | ||
| function test(name, fn) { |
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 to the test going in a function I can start the concatenation with it, so if errors are thrown, the line numbers will be correct. SES gets concatenated after but the test runs synchronously after SES still.
| // like v8. | ||
| platform = 'v8'; | ||
|
|
||
| if (`${sst[0]}` === '[object CallSite]') { |
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.
Here we detect Hermes providing a broken toString
| uncurryThis(csProto.getColumnNumber), | ||
| ]; | ||
|
|
||
| callSiteToStringFallback = callSite => |
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.
Instead of creating it and passing around, we could instead repair the original, but I'm afraid someone else might be doing similar feature detection
| // Error.captureStackTrace = null | ||
|
|
||
| /* global test */ | ||
| test('error taming unsafe', () => { |
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 the one that fails without my toStringFallback
| @@ -0,0 +1,92 @@ | |||
| /* global test */ | |||
| test('known issue: CallSite implementation', () => { | |||
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.
full test documenting broken callsite implementation
| @@ -0,0 +1,36 @@ | |||
| /* global test */ | |||
| /* global repairIntrinsics */ | |||
| test('knonw issue: Hermes Promise is non-standard', () => { | |||
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 test documents the broken promise implementation and verifies that the fix we're applying in lavamoat still works.
Are we interested in adding the fix to ses-hermes? I think it'd make sense - Promise constructors will throw often if not fixed.
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.
Where can I find this fix?
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 fix is demonstrated here (capturing all truthy underscore-prefixed fields from Promise and preserving them till after repair to bring them back)
It's used in @lavamoat/react-native-lockdown but I think if we claim SES works under Hermes, we should consider adding this exception to the hermes build by default.
Another awful detail - the field that is exposed and must exist or the Promie implementation falls apart is containing a no-op function that the implementation reuses in a few places, which could have been a local scope function. Other fields, null by default, allow instrumenting all promises, so good riddance.
Thanks for the warning. It did indeed hurt :/ |
| if (typeof originalPrepareStackTrace === 'function') { | ||
| // This case should not occur on v8 or any other platform. | ||
| // But if it does, we assume we're on v8, or a platform whose | ||
| // error stack logic is close enough that we can treat it | ||
| // like v8. | ||
| platform = 'v8'; |
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 believe it does happen in Node.js according to the realm init logic I'm seeing: https://github.com/nodejs/node/blob/b8870c4a61f9c4c4490e43472e98655b00055359/lib/internal/bootstrap/realm.js#L444-L470
FYI that logic shows that it's not v8 that calls Error.prepareStackTrace but the embedder that sets a callback (in this case setPrepareStackTraceCallback), which itself does the forwarding to Error.prepareStackTrace (or some other fallback)
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.
Huh. Testing just now, it was added sometime between Node v18.19.0 exclusive and Node v20.18.3 inclusive.
Btw, given the topic, I first read this as "repairStackTrace" and became unreasonably hopeful for a moment ;) . |
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.
Given the new modular design of prepareStackTrace in Node.js introduced in nodejs/node#50827, I think we should at least restore (or not touch?) the original prepareStackTrace if the errorTaming mode is 'unsafe'.
Also, since this is so related, maybe we could attempt to leverage the original prepareStackTrace to build the stack string (see #1798), but happy to have this as a follow up PR.
(@mhofman , restoring your original. Somehow when I thought I was replying to it, I accidentally revised it instead. In any case, I found a better place for the reply, above.)
0fca5fc to
6fb1c1c
Compare
fb86608 to
bd4fcbc
Compare
bd4fcbc to
bad2617
Compare
|
@naugtur , since the problematic case is Hermes, and since you've contributed so much of this PR anyway, should you take it from here? Github will allow me to assign you. But due to the fact that I started the PR, Github won't let me review. So if you do take it from here, just informally consider me a reviewer. Thanks! |
|
Happy to take over, but may need some help with specifying the desired scope / what's left to do. @mhofman, care to elaborate on the comment about who calls If Node is doing things, should I expect the behavior to differ between Node and Chrome? |
My understanding is that v8 itself only implements a mechanism so that the host can specify the callback to use to prepare stack traces, and Chrome and v8 both register and internal callback ( Node.js further has an internal default behavior for Instead of bailing out and assume v8, we may want to still test behavior when Furthermore I recommend that we go the extra mile, save the original |
Closes: #2951
Refs: #1798 , https://github.com/tc39/proposal-error-capturestacktrace , https://github.com/tc39/proposal-error-stacks , https://github.com/tc39/proposal-error-stack-accessor
Description
(Description mostly from Copilot's overview below, which is quite good!)
This PR's error taming implements stronger platform detection for v8, addressing issue #2951. The change improves the reliability of v8 detection by testing the
prepareStackTracemechanism, in addition to checking for the presence ofcaptureStackTrace.This is needed because some non-v8 platforms have implemented
captureStackTracewithoutprepareStackTrace. The https://github.com/tc39/proposal-error-capturestacktrace proposal would make it standard, eventually causing all conformant JS engines to implement it.A verbally reported bug motivated this, even though that bug itself may be a false alarm. The report raised the possibility that our error-taming of treating the Hermes platform as if it was v8, and then misbehaving because it does not act like v8. Whether or not this actually happens on Hermes, it is plausible and would happen elsewhere with ses prior to this PR.
Security Considerations
If non-v8 engines start implementing the
prepareStackTracemechanism, we may need to tighten the sniff test once again. Such is the nature of sniff tests. Since we do not yet know the specifics of such possible additions, it does not yet make sense to tighten the sniff test, unless I'm missing some we should sniff instead.During such a transition period, it is possible that the falsly-triggered v8 error taming could misbehave, including in ways that may be exploitable.
Scaling Considerations
none
Documentation Considerations
This is a bug fix and does not need documentation beyond the issue, this PR, and the inline comments added by this PR.
Testing Considerations
I did not add automated tests, because I am unclear on how automated tests would test this. Reviewers: suggestions welcome.
What I did do is manually step through the logic in the vscode debugger while running on Node. The sniff test code worked as expected there. If we do rely on such manual testing, I should at least test on some non-v8 platform that does implement
captureStackTrace.Compatibility Considerations
See "Security Considerations" above.
Upgrade Considerations
none