Skip to content

Commit

Permalink
Don't allow Symbols to be passed to a reply
Browse files Browse the repository at this point in the history
  • Loading branch information
sebmarkbage committed Mar 21, 2024
1 parent a493901 commit bd8f964
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 23 deletions.
20 changes: 7 additions & 13 deletions packages/react-client/src/ReactFlightReplyClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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') {
Expand Down
10 changes: 5 additions & 5 deletions packages/react-client/src/ReactFlightTemporaryReferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@

interface Reference {}

export opaque type TemporaryReferenceSet = Array<Reference>;
export opaque type TemporaryReferenceSet = Array<Reference | symbol>;

export function createTemporaryReferenceSet(): TemporaryReferenceSet {
return [];
}

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
Expand All @@ -27,15 +27,15 @@ export function writeTemporaryReference(
return newId;
}

export function readTemporaryReference(
export function readTemporaryReference<T>(
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);
}
4 changes: 0 additions & 4 deletions packages/react-server/src/ReactFlightReplyServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion scripts/error-codes/codes.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}

0 comments on commit bd8f964

Please sign in to comment.