Skip to content

[Bug]: CodeLine component uses dangerouslySetInnerHTML without HTML entity escaping — breaks display for JSX/typed code #462

@nyxsky404

Description

@nyxsky404

Short summary

modules/home/code-line.tsx uses dangerouslySetInnerHTML to inject syntax-highlighting <span> tags, but never escapes HTML special characters (<, >, &) in the input first. Any code line containing angle brackets (TypeScript generics, JSX, comparison operators) will render as broken or corrupted HTML. The component itself has an internal TODO comment acknowledging it should be replaced with a proper library.

Steps to reproduce

  1. Open modules/home/hero-code.tsx
  2. Add a line containing angle brackets to codeSnippet, for example:
const codeSnippet = `...
const elements: Array<string> = [];
const isSmaller = a < b;
return <div>Hello</div>;
`;
  1. Run the dev server and visit the homepage (/)
  2. Observe the "code demo" card

Expected behavior

The code lines should render as readable, syntax-highlighted text. Angle brackets and HTML-special characters should be displayed as literal characters.

Actual behavior

  • Array<string> → the <string> part is parsed as an HTML element and disappears from the DOM
  • a < b< b opens a malformed HTML tag, corrupting everything that follows
  • <div>Hello</div> → renders as a real DOM <div> inside the code block

The highlightCode function in code-line.tsx (lines 24–30) does string replacements that produce raw HTML, then passes the result directly to dangerouslySetInnerHTML without escaping entities first:

// modules/home/code-line.tsx:24-30
const highlightCode = (code: string) => {
    return code
        .replace(/import|from|export|.../g, '<span class="text-red-500...">$&</span>')
        .replace(/'[^']*'/g, '<span class="text-amber-600...">$&</span>')
        // ...  no HTML entity escaping anywhere
}

// line 21
return <span dangerouslySetInnerHTML={{ __html: highlightCode(highlighted) }} />;

The component even has a self-aware comment at lines 6-8:

// Note: This is a very simplistic implementation and should be replaced with a proper library like prismjs or shiki in production

Affected area

Other

Environment

All browsers, all OSes. Reproducible locally by editing hero-code.tsx to include any JSX or generic TypeScript in codeSnippet. The current hardcoded snippet happens to avoid angle brackets, which is why this hasn't surfaced yet — but it's a latent bug waiting to break the homepage.

Root cause:

// Should escape entities BEFORE injecting spans:
const escapeHtml = (str: string) =>
  str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");

const highlightCode = (code: string) => {
  const safe = escapeHtml(code);  // escape first
  return safe.replace(/import|from|.../g, '<span ...>$&</span>');
};

Better long-term fix (as the existing TODO suggests): replace the entire CodeLine component with shiki or prism-react-renderer, both of which handle escaping correctly and provide real language-aware tokenization.

Screenshots, logs, or recordings

Affected file: modules/home/code-line.tsx lines 24–39
Consumer: modules/home/hero-code.tsx lines 72–79

Confirmation

  • I searched existing issues before opening this report.
  • I included enough detail for reproduction.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions