Skip to content

Conversation

@Nauxie
Copy link
Member

@Nauxie Nauxie commented Jan 13, 2026

No description provided.

@greptile-apps
Copy link

greptile-apps bot commented Jan 13, 2026

Greptile Overview

Greptile Summary

This PR adds markdown rendering support to assistant messages using react-markdown v10.1.0. The implementation conditionally renders assistant messages through ReactMarkdown while keeping user messages as plain text. Comprehensive CSS styles are added for markdown elements including code blocks, lists, links, headings, and blockquotes.

Key Implementation Details

Component Changes:

  • Modified Message.tsx to conditionally render assistant messages using <ReactMarkdown> with a custom link component that adds target="_blank" and rel="noopener noreferrer"
  • User messages continue to display as plain text
  • Markdown class applied only to assistant message content

Styling:

  • Added comprehensive CSS styles scoped to .content.markdown class
  • Styles cover paragraphs, lists, code blocks, links, headings, blockquotes, and horizontal rules
  • Compiled styles bundled into styles.css

Critical Issues Found

1. Bundle Configuration Problem:

  • react-markdown is added as a production dependency but NOT externalized in vite.config.ts
  • This causes the entire react-markdown library and ~30+ transitive dependencies (mdast-util-, micromark-, hast-util-, unist-util-) to be bundled into the library output
  • Should be treated as a peer dependency and externalized to avoid bloating the bundle

2. Missing Security Configurations:

  • No URL sanitization to prevent dangerous protocols (javascript:, data:, vbscript:)
  • No disallowedElements specified to block potentially dangerous HTML elements
  • Custom link component doesn't validate href existence before rendering

3. Dependency Management:

  • react-markdown should be a peer dependency following React library best practices
  • Prevents version conflicts and duplicate installations in consuming applications

Recommendations

  1. Immediate: Add react-markdown to Vite's external dependencies array
  2. Immediate: Add URL sanitization via urlTransform prop
  3. Immediate: Add href validation in custom link component
  4. High Priority: Move react-markdown to peerDependencies
  5. Consider: Add disallowedElements prop for defense-in-depth

The markdown rendering functionality itself works correctly, but the implementation has configuration and security gaps that should be addressed before merging.

Confidence Score: 2/5

  • This PR has critical bundle configuration issues and missing security protections that should be addressed before merging
  • Score of 2 reflects multiple critical issues: (1) react-markdown not externalized causing significant bundle bloat with 30+ transitive dependencies being bundled, (2) missing URL sanitization allowing potentially dangerous protocols, (3) no href validation in custom link component, and (4) incorrect dependency configuration treating production dependency instead of peer dependency. While the markdown rendering functionality works and CSS is well-implemented, these architectural and security issues need resolution.
  • Pay close attention to vite.config.ts (needs react-markdown externalized), package.json (dependency reconfiguration), and Message.tsx (security configurations)

Important Files Changed

File Analysis

Filename Score Overview
src/components/Message/Message.tsx 2/5 Added ReactMarkdown rendering for assistant messages with custom link component; missing security configurations (URL sanitization, disallowed elements) and href validation in link component
package.json 2/5 Added react-markdown ^10.1.0 as production dependency and removed @storybook/blocks; react-markdown should be a peerDependency instead to follow React library best practices
vite.config.ts 2/5 No changes to file, but missing react-markdown in external dependencies array causing it to be bundled unnecessarily with all transitive dependencies
src/components/Message/Message.module.css 4/5 Added comprehensive markdown styling for assistant messages including code blocks, lists, links, headings, and blockquotes; styles properly scoped to .content.markdown class

Sequence Diagram

sequenceDiagram
    participant User
    participant API as Chat API
    participant Hook as useChat Hook
    participant MessageList
    participant Message as Message Component
    participant ReactMarkdown
    
    User->>API: Send message via sendMessage()
    API-->>Hook: SSE stream with assistant response
    Hook->>Hook: Process SSE events (message type)
    Hook->>Hook: Update message content incrementally
    Hook->>MessageList: Pass messages array
    MessageList->>Message: Render each message
    
    alt User Message
        Message->>Message: Render as plain text with white-space: pre-wrap
    else Assistant Message
        Message->>ReactMarkdown: Pass message.content
        ReactMarkdown->>ReactMarkdown: Parse markdown syntax
        ReactMarkdown->>ReactMarkdown: Apply custom link component (target="_blank")
        ReactMarkdown-->>Message: Return formatted React elements
        Message->>Message: Apply .content.markdown CSS styles
    end
    
    Message-->>MessageList: Rendered message
    MessageList-->>User: Display formatted message
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

{isUser ? (
message.content
) : (
<ReactMarkdown>{message.content}</ReactMarkdown>
Copy link

Choose a reason for hiding this comment

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

Links rendered in markdown should include security attributes to prevent security vulnerabilities. When rendering external links, it's a best practice to add target="_blank" and rel="noopener noreferrer" to prevent the new page from accessing the window.opener object.

Consider configuring ReactMarkdown with a custom link component:

Suggested change
<ReactMarkdown>{message.content}</ReactMarkdown>
<ReactMarkdown
components={{
a: ({ node, ...props }) => (
<a {...props} target="_blank" rel="noopener noreferrer" />
),
}}
>
{message.content}
</ReactMarkdown>
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/components/Message/Message.tsx
Line: 50:50

Comment:
Links rendered in markdown should include security attributes to prevent security vulnerabilities. When rendering external links, it's a best practice to add `target="_blank"` and `rel="noopener noreferrer"` to prevent the new page from accessing the `window.opener` object.

Consider configuring ReactMarkdown with a custom link component:

```suggestion
            <ReactMarkdown
              components={{
                a: ({ node, ...props }) => (
                  <a {...props} target="_blank" rel="noopener noreferrer" />
                ),
              }}
            >
              {message.content}
            </ReactMarkdown>
```

How can I resolve this? If you propose a fix, please make it concise.

{isUser ? (
message.content
) : (
<ReactMarkdown>{message.content}</ReactMarkdown>
Copy link

Choose a reason for hiding this comment

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

Consider adding URL protocol restrictions to prevent javascript: and other dangerous protocols in links and images. While react-markdown v10 disallows raw HTML by default, it's still best practice to explicitly sanitize URLs.

You can use the urlTransform prop to validate and sanitize URLs:

Suggested change
<ReactMarkdown>{message.content}</ReactMarkdown>
<ReactMarkdown
urlTransform={(url) => {
try {
const parsed = new URL(url, window.location.href);
// Only allow http, https, and mailto protocols
if (['http:', 'https:', 'mailto:'].includes(parsed.protocol)) {
return url;
}
return '';
} catch {
return '';
}
}}
>
{message.content}
</ReactMarkdown>
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/components/Message/Message.tsx
Line: 50:50

Comment:
Consider adding URL protocol restrictions to prevent `javascript:` and other dangerous protocols in links and images. While react-markdown v10 disallows raw HTML by default, it's still best practice to explicitly sanitize URLs.

You can use the `urlTransform` prop to validate and sanitize URLs:

```suggestion
            <ReactMarkdown
              urlTransform={(url) => {
                try {
                  const parsed = new URL(url, window.location.href);
                  // Only allow http, https, and mailto protocols
                  if (['http:', 'https:', 'mailto:'].includes(parsed.protocol)) {
                    return url;
                  }
                  return '';
                } catch {
                  return '';
                }
              }}
            >
              {message.content}
            </ReactMarkdown>
```

How can I resolve this? If you propose a fix, please make it concise.

@Nauxie
Copy link
Member Author

Nauxie commented Jan 13, 2026

@greptileeai

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

4 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +94 to 96
"dependencies": {
"react-markdown": "^10.1.0"
}
Copy link

Choose a reason for hiding this comment

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

react-markdown should be moved from dependencies to peerDependencies to follow best practices for React library development. This ensures:

  1. The consuming application controls the exact version installed
  2. Avoids duplicate React context issues
  3. Reduces bundle size by not bundling the dependency
  4. Maintains compatibility with different versions
Suggested change
"dependencies": {
"react-markdown": "^10.1.0"
}
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"react-markdown": "^10.0.0"
},

And add to devDependencies:

Suggested change
"dependencies": {
"react-markdown": "^10.1.0"
}
"react-markdown": "^10.1.0",

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: package.json
Line: 94:96

Comment:
`react-markdown` should be moved from `dependencies` to `peerDependencies` to follow best practices for React library development. This ensures:
1. The consuming application controls the exact version installed
2. Avoids duplicate React context issues
3. Reduces bundle size by not bundling the dependency
4. Maintains compatibility with different versions

```suggestion
  "peerDependencies": {
    "react": "^18.0.0 || ^19.0.0",
    "react-dom": "^18.0.0 || ^19.0.0",
    "react-markdown": "^10.0.0"
  },
```

And add to `devDependencies`:
```suggestion
    "react-markdown": "^10.1.0",
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 50 to 61
<ReactMarkdown
components={{
a: ({ href, children }) => (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
),
}}
>
{message.content}
</ReactMarkdown>
)}
Copy link

Choose a reason for hiding this comment

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

Missing security configuration for react-markdown. By default, react-markdown v10 does NOT sanitize HTML and allows potentially dangerous content. While react-markdown escapes HTML by default in recent versions, explicit security measures should be configured to protect against:

  1. Dangerous URLs (javascript:, data:, vbscript:)
  2. Malicious link targets
  3. Future security considerations

Add security props to ReactMarkdown:

Suggested change
<ReactMarkdown
components={{
a: ({ href, children }) => (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
),
}}
>
{message.content}
</ReactMarkdown>
)}
<ReactMarkdown
components={{
a: ({ href, children }) => (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
),
}}
urlTransform={(url) => {
// Sanitize URLs to prevent javascript:, data:, vbscript: protocols
if (url.startsWith('javascript:') || url.startsWith('data:') || url.startsWith('vbscript:')) {
return '';
}
return url;
}}
disallowedElements={['script', 'iframe', 'object', 'embed']}
>
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/components/Message/Message.tsx
Line: 50:61

Comment:
Missing security configuration for `react-markdown`. By default, react-markdown v10 does NOT sanitize HTML and allows potentially dangerous content. While react-markdown escapes HTML by default in recent versions, explicit security measures should be configured to protect against:

1. Dangerous URLs (javascript:, data:, vbscript:)
2. Malicious link targets
3. Future security considerations

Add security props to ReactMarkdown:

```suggestion
            <ReactMarkdown
              components={{
                a: ({ href, children }) => (
                  <a href={href} target="_blank" rel="noopener noreferrer">
                    {children}
                  </a>
                ),
              }}
              urlTransform={(url) => {
                // Sanitize URLs to prevent javascript:, data:, vbscript: protocols
                if (url.startsWith('javascript:') || url.startsWith('data:') || url.startsWith('vbscript:')) {
                  return '';
                }
                return url;
              }}
              disallowedElements={['script', 'iframe', 'object', 'embed']}
            >
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 52 to 56
a: ({ href, children }) => (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
),
Copy link

Choose a reason for hiding this comment

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

The custom a component doesn't validate if href exists before rendering, which could lead to invalid HTML. TypeScript types from react-markdown indicate href can be undefined.

Suggested change
a: ({ href, children }) => (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
),
a: ({ href, children }) => {
if (!href) return <>{children}</>;
return (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
);
},
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/components/Message/Message.tsx
Line: 52:56

Comment:
The custom `a` component doesn't validate if `href` exists before rendering, which could lead to invalid HTML. TypeScript types from react-markdown indicate `href` can be `undefined`.

```suggestion
                a: ({ href, children }) => {
                  if (!href) return <>{children}</>;
                  return (
                    <a href={href} target="_blank" rel="noopener noreferrer">
                      {children}
                    </a>
                  );
                },
```

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link

greptile-apps bot commented Jan 13, 2026

Additional Comments (1)

vite.config.ts
react-markdown needs to be added to the external dependencies array to prevent it from being bundled with the library. Currently, react-markdown and all its transitive dependencies (mdast-util-, micromark-, hast-util-, unist-util-, etc.) are being bundled into the library output, which significantly increases the bundle size.

Since react-markdown is listed as a production dependency in package.json, consuming applications will install it themselves. The library should treat it as a peer dependency or external to avoid bundling it twice.

      external: ['react', 'react-dom', 'react/jsx-runtime', 'react-markdown'],
Prompt To Fix With AI
This is a comment left during a code review.
Path: vite.config.ts
Line: 22:22

Comment:
`react-markdown` needs to be added to the external dependencies array to prevent it from being bundled with the library. Currently, `react-markdown` and all its transitive dependencies (mdast-util-*, micromark-*, hast-util-*, unist-util-*, etc.) are being bundled into the library output, which significantly increases the bundle size.

Since `react-markdown` is listed as a production dependency in `package.json`, consuming applications will install it themselves. The library should treat it as a peer dependency or external to avoid bundling it twice.

```suggestion
      external: ['react', 'react-dom', 'react/jsx-runtime', 'react-markdown'],
```

How can I resolve this? If you propose a fix, please make it concise.

@Nauxie Nauxie merged commit f592cae into main Jan 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant