From bd8f964a08fb5298f61423a332a4b6cd54cbf594 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Thu, 21 Mar 2024 13:01:34 -0700 Subject: [PATCH] Don't allow Symbols to be passed to a reply --- .../src/ReactFlightReplyClient.js | 20 +++++++------------ .../src/ReactFlightTemporaryReferences.js | 10 +++++----- .../src/ReactFlightReplyServer.js | 4 ---- scripts/error-codes/codes.json | 3 ++- 4 files changed, 14 insertions(+), 23 deletions(-) diff --git a/packages/react-client/src/ReactFlightReplyClient.js b/packages/react-client/src/ReactFlightReplyClient.js index 5f4bd00deb305..e3d19319c2f45 100644 --- a/packages/react-client/src/ReactFlightReplyClient.js +++ b/packages/react-client/src/ReactFlightReplyClient.js @@ -105,10 +105,6 @@ function serializeTemporaryReferenceID(id: number): string { return '$T' + id.toString(16); } -function serializeSymbolReference(name: string): string { - return '$S' + name; -} - function serializeFormDataReference(id: number): string { // Why K? F is "Function". D is "Date". What else? return '$K' + id.toString(16); @@ -479,18 +475,16 @@ export function processReply( } if (typeof value === 'symbol') { - // $FlowFixMe[incompatible-type] `description` might be undefined - const name: string = value.description; - if (Symbol.for(name) !== value) { + if (temporaryReferences === undefined) { throw new Error( - 'Only global symbols received from Symbol.for(...) can be passed to Server Functions. ' + - `The symbol Symbol.for(${ - // $FlowFixMe[incompatible-type] `description` might be undefined - value.description - }) cannot be found among global symbols.`, + 'Symbols cannot be passed to a Server Function without a ' + + 'temporary reference set. Pass a TemporaryReferenceSet to the options.' + + (__DEV__ ? describeObjectForErrorMessage(parent, key) : ''), ); } - return serializeSymbolReference(name); + return serializeTemporaryReferenceID( + writeTemporaryReference(temporaryReferences, value), + ); } if (typeof value === 'bigint') { diff --git a/packages/react-client/src/ReactFlightTemporaryReferences.js b/packages/react-client/src/ReactFlightTemporaryReferences.js index 2f7453f0f9fa5..7060562eb2291 100644 --- a/packages/react-client/src/ReactFlightTemporaryReferences.js +++ b/packages/react-client/src/ReactFlightTemporaryReferences.js @@ -9,7 +9,7 @@ interface Reference {} -export opaque type TemporaryReferenceSet = Array; +export opaque type TemporaryReferenceSet = Array; export function createTemporaryReferenceSet(): TemporaryReferenceSet { return []; @@ -17,7 +17,7 @@ export function createTemporaryReferenceSet(): TemporaryReferenceSet { export function writeTemporaryReference( set: TemporaryReferenceSet, - object: Reference, + object: Reference | symbol, ): number { // We always create a new entry regardless if we've already written the same // object. This ensures that we always generate a deterministic encoding of @@ -27,15 +27,15 @@ export function writeTemporaryReference( return newId; } -export function readTemporaryReference( +export function readTemporaryReference( set: TemporaryReferenceSet, id: number, -): Reference { +): T { if (id < 0 || id >= set.length) { throw new Error( "The RSC response contained a reference that doesn't exist in the temporary reference set. " + 'Always pass the matching set that was used to create the reply when parsing its response.', ); } - return set[id]; + return (set[id]: any); } diff --git a/packages/react-server/src/ReactFlightReplyServer.js b/packages/react-server/src/ReactFlightReplyServer.js index cf308ba5204ea..98d9c0fe046e6 100644 --- a/packages/react-server/src/ReactFlightReplyServer.js +++ b/packages/react-server/src/ReactFlightReplyServer.js @@ -396,10 +396,6 @@ function parseModelString( const chunk = getChunk(response, id); return chunk; } - case 'S': { - // Symbol - return Symbol.for(value.slice(2)); - } case 'F': { // Server Reference const id = parseInt(value.slice(2), 16); diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 2d7f833bff10d..4e4334e346a41 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -501,5 +501,6 @@ "513": "Cannot render a Client Context Provider on the Server. Instead, you can export a Client Component wrapper that itself renders a Client Context Provider.", "514": "Cannot access %s on the server. You cannot dot into a temporary client reference from a server component. You can only pass the value through to the client.", "515": "Cannot assign to a temporary client reference from a server module.", - "516": "Attempted to call a temporary Client Reference from the server but it is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component." + "516": "Attempted to call a temporary Client Reference from the server but it is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.", + "517": "Symbols cannot be passed to a Server Function without a temporary reference set. Pass a TemporaryReferenceSet to the options.%s" }