-
Notifications
You must be signed in to change notification settings - Fork 31.3k
crypto: fix cross-realm ArrayBuffer validation in WebCrypto #57828
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: main
Are you sure you want to change the base?
crypto: fix cross-realm ArrayBuffer validation in WebCrypto #57828
Conversation
Review requested:
|
In general, a patch should land on the main branch first. The same patch can apply to the main branch: node/lib/internal/crypto/webidl.js Lines 196 to 198 in 795dd8e
Would you mind re-target the PR to the main branch? Thank you |
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 PR should target the main
branch not the nodejs:v22.x
branch.
941520a
to
4a2a300
Compare
This patch modifies the isNonSharedArrayBuffer function in the WebIDL implementation for the SubtleCrypto API to properly handle ArrayBuffer instances created in different JavaScript realms. Before this fix, when a TypedArray.buffer from a different realm (e.g., from a VM context or worker thread) was passed to SubtleCrypto.digest(), it would fail with: "TypeError: Failed to execute 'digest' on 'SubtleCrypto': 2nd argument is not instance of ArrayBuffer, Buffer, TypedArray, or DataView." The fix use the isArrayBuffer function from internal/util/types to detect cross-realm ArrayBuffer instances when the prototype chain check fails. This ensures compatibility with TypedArray.buffer across JavaScript realms. See storacha/w3up#1591 for more details.
4a2a300
to
982d482
Compare
Thanks for the review @jasnell, @ljharb, and @legendecas. This is ready for another round. |
lib/internal/crypto/webidl.js
Outdated
@@ -194,7 +195,7 @@ converters.object = (V, opts) => { | |||
}; | |||
|
|||
function isNonSharedArrayBuffer(V) { | |||
return ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V); | |||
return isArrayBuffer(V); |
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.
Alternatively this could be:
const isNonSharedArrayBuffer = isArrayBuffer;
Also, should isSharedArrayBuffer(...)
also be similarly updated here?
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 would actually just remove the method and use isArrayBuffer()
directly instead. It should be clear that it's not shared.
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'm confused - a SharedArrayBuffer is also an ArrayBuffer, in terms of internal slots - what actual check does isArrayBuffer
perform?
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.
util.types.isArrayBuffer()
invokes v8::Value::isArrayBuffer
, which returns false if it is a SharedArrayBuffer: https://source.chromium.org/chromium/chromium/src/+/main:v8/src/api/api.cc;l=3534-3538?q=Value::IsArrayBuffer.
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.
(with or without "remove isNonSharedArrayBuffer and use isArrayBuffer" directly)
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #57828 +/- ##
=======================================
Coverage 90.24% 90.24%
=======================================
Files 630 630
Lines 185670 185689 +19
Branches 36405 36408 +3
=======================================
+ Hits 167555 167576 +21
+ Misses 10998 10995 -3
- Partials 7117 7118 +1
🚀 New features to boost your workflow:
|
There's a linter and a test error that would need to be adressed, let us know if you need help. |
can you point out which test it that? thank you! |
You can find it in the CI output, here it is:
|
@aduh95 - I've made the change to enable the |
Problem
The Web Crypto API in Node.js v22+ fails to properly validate ArrayBuffer instances
created in different JavaScript realms. When an ArrayBuffer from a different realm
is passed to SubtleCrypto.digest(), it fails with:
This is a common issue when:
The problem is in
isNonSharedArrayBuffer()
which checks using prototypeinheritance (
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V)
) which failsfor cross-realm objects, even though structurally they are valid ArrayBuffers.
Solution
This PR modifies
isNonSharedArrayBuffer()
to useisArrayBuffer
frominternal/util/types
to properly check for cross-realm objects.Testing
Added a new test that verifies that ArrayBuffers created in different VM contexts
are correctly recognized as valid inputs to SubtleCrypto.digest().
Workaround
Until this fix is merged, users can work around this issue by wrapping cross-realm ArrayBuffers in a TypedArray view before passing them to WebCrypto functions. Here's a simple example demonstrating the workaround:
Ref: storacha/w3up#1591