Skip to content

Enforce server-only invariant on MDXRenderer's new Function() eval #70

@lwwmanning

Description

@lwwmanning

Surfaced during the supply-chain hardening audit in #68.

Location: src/components/MDXRenderer.tsx:13

Issue: The component uses new Function(code) to instantiate the velite-emitted MDX. The next.config.mjs CSP allows 'unsafe-inline' for script-src but not 'unsafe-eval' — which is the right posture if and only if MDXRenderer runs exclusively on the server.

The next.config.mjs comment claims this is server-only:

MDXRenderer compiles velite-emitted JSX via new Function(code), but it's a server component — the eval happens at build/SSR time on the server, so the browser never sees the dynamic code and CSP doesn't need 'unsafe-eval'.

…but the component file itself has no "use server" directive or any other build-time assertion that enforces this. If someone imports MDXRenderer from a client component (knowingly or accidentally — the import path doesn't make the constraint obvious), the eval moves to the browser. CSP would block it (which is itself defense-in-depth), but the error would surface as a runtime failure in the catch block, not at build time.

Proposed fix: make the invariant explicit. Two complementary options:

  1. Add "use server" (or import "server-only") at the top of MDXRenderer.tsx so the bundler errors at build time if it gets pulled into a client bundle.
  2. Add a build-time grep / lint rule in CI or velite.config.ts that fails if MDXRenderer is imported from a file with "use client".

Either alone is sufficient; both is belt-and-braces.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsecuritySecurity findings, hardening, and vulnerability disclosure

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions