Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions src/components/codeTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,30 @@ const showSigninNote = (children: ReactNode) => {
});
};

// Resolve React lazy elements from RSC serialization (Next.js 15.5+).
// Client component children serialized through RSC boundaries arrive as
// lazy elements with _init/_payload instead of the usual type/props shape.
function resolveElement(child: any): ReactElement<CodeBlockProps> | null {
if (child == null || typeof child !== 'object') {
return null;
}
if (child.props) {
return child;
}
if (child._init && child._payload) {
try {
return child._init(child._payload);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unvalidated _init result may crash downstream destructuring

Medium Severity

The return value of child._init(child._payload) is passed through without validating it has props. The filter only checks !== null, so if _init returns undefined or a non-null object without props, it passes through. This causes a TypeError on the possibleChoices line where {props: {title, language}} is destructured. The old .filter(child => child?.props) was more defensive. The resolved result from _init needs a props check before being returned.

Additional Locations (1)
Fix in Cursor Fix in Web

} catch {
return null;
}
}
return null;
}

export function CodeTabs({children}: CodeTabProps) {
const codeBlocks = (Array.isArray(children) ? [...children] : [children]).filter(
child => child?.props
);
const codeBlocks = (Array.isArray(children) ? [...children] : [children])
.map(resolveElement)
.filter((child): child is ReactElement<CodeBlockProps> => child !== null);

// The title is what we use for sorting and also for remembering the
// selection. If there is no title fall back to the title cased language name
Expand Down
Loading