Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
109 changes: 87 additions & 22 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"rehype-highlight": "^6.0.0",
"rehype-katex": "^6.0.3",
"rehype-raw": "^6.1.1",
"rehype-sanitize": "^6.0.0",
"remark-breaks": "^3.0.3",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
Expand Down
30 changes: 30 additions & 0 deletions src/components/MarkdownRender/markdown-without-katex.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ReactMarkdown from 'react-markdown'
import rehypeRaw from 'rehype-raw'
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'
import rehypeHighlight from 'rehype-highlight'
import remarkGfm from 'remark-gfm'
import remarkBreaks from 'remark-breaks'
Expand Down Expand Up @@ -108,6 +109,34 @@ const ThinkComponent = ({ node, children, ...props }) => {
)
}

// Configure sanitize schema to prevent XSS attacks
const sanitizeSchema = {
...defaultSchema,
attributes: {
...defaultSchema.attributes,
// Allow safe attributes
'*': ['className', 'id', 'dir'],
a: ['href', 'title', 'target', 'rel'],
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

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

The sanitization schema allows the 'target' attribute on anchor tags without restricting its values. While not directly an XSS vulnerability, this can be exploited for tabnabbing attacks where malicious links with target="_blank" can access the window.opener object.

To mitigate this, you should either:

  1. Remove 'target' from the allowed attributes for anchor tags, or
  2. Ensure that links with target="_blank" automatically get rel="noopener noreferrer" added

Consider updating the anchor tag configuration to enforce safe rel values or remove the target attribute allowance.

Suggested change
a: ['href', 'title', 'target', 'rel'],
a: ['href', 'title', 'rel'],

Copilot uses AI. Check for mistakes.
img: ['src', 'alt', 'title', 'width', 'height'],
video: ['src', 'controls', 'width', 'height'],
Comment on lines +120 to +121
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

By explicitly defining the 'src' attribute for img and video tags, this configuration overrides the default schema's protocol validation. The defaultSchema includes protocol filtering to prevent javascript: and other dangerous protocols. Consider explicitly adding protocol restrictions using the 'protocols' configuration option or ensuring dangerous protocols are filtered.

Copilot uses AI. Check for mistakes.
code: ['className'],
div: ['className', 'id', 'dir'],
span: ['className', 'id'],
Comment thread
anuxs marked this conversation as resolved.
Outdated
// Add custom element 'think'
think: [],
},
tagNames: [
...(defaultSchema.tagNames || []),
'think', // Add custom think tag
].filter(
// Remove dangerous tags
(tag) =>
!['script', 'iframe', 'object', 'embed', 'link', 'style', 'form', 'input', 'button'].includes(
tag,
),
),
}
Comment on lines +113 to +138
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This sanitizeSchema has some redundancies and is also duplicated in markdown.jsx. Please see my comment in markdown.jsx for a detailed explanation and a simplified version that can be extracted to a shared file to improve maintainability.

Comment on lines +112 to +138
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n src/components/MarkdownRender/markdown-without-katex.jsx | head -150

Repository: ChatGPTBox-dev/chatGPTBox

Length of output: 5355


🏁 Script executed:

sed -n '140,200p' src/components/MarkdownRender/markdown-without-katex.jsx

Repository: ChatGPTBox-dev/chatGPTBox

Length of output: 1047


🏁 Script executed:

sed -n '200,250p' src/components/MarkdownRender/markdown-without-katex.jsx

Repository: ChatGPTBox-dev/chatGPTBox

Length of output: 870


🏁 Script executed:

cat package.json | grep -A 5 -B 5 "rehype-sanitize"

Repository: ChatGPTBox-dev/chatGPTBox

Length of output: 399


🌐 Web query:

rehype-sanitize 6.0.0 defaultSchema video tag protocols

💡 Result:

[email protected] uses the defaultSchema exported by hast-util-sanitize (it does not redefine protocol rules itself). See the rehype-sanitize README and the hast-util-sanitize repo for the canonical defaultSchema (including the protocols map). [1][2]

If you want the exact allowed protocols for video/src (and other URL-bearing properties), inspect the defaultSchema.protocols field in the hast-util-sanitize source. Here are those two references to check:

  • rehype-sanitize README (exports defaultSchema). [1]
  • hast-util-sanitize (defaultSchema + protocols documented in its README / source). [2]

References:
[1] https://github.com/rehypejs/rehype-sanitize — README (defaultSchema).
[2] https://github.com/syntax-tree/hast-util-sanitize — README and defaultSchema (protocols).


🌐 Web query:

hast-util-sanitize defaultSchema tagNames video protocols javascript data

💡 Result:

You can extend hast-util-sanitize's defaultSchema to allow

const {defaultSchema} = require('hast-util-sanitize') // or: import {defaultSchema} from 'hast-util-sanitize'
const deepmerge = require('deepmerge') // or Object.assign / spread for simple merges

const schema = deepmerge(defaultSchema, {
tagNames: [...(defaultSchema.tagNames || []), 'video', 'source'],
attributes: {
...defaultSchema.attributes,
video: [
// allow common video properties
'controls',
'autoplay',
'loop',
'muted',
'playsInline',
['poster'], // allow poster value
['src'] // allow src on

Notes:

  • Do NOT add 'javascript' to protocols (e.g., 'javascript:') — that re-enables JS URLs and is an XSS risk. The defaultSchema blocks javascript: by design. If you need inline blob/mediadata use data: or blob:, not javascript:. [1][2]
  • Add any extra allowed attributes or class names similarly (use arrays or regexes where needed). If you use rehype-sanitize, pass this schema to it. [1][3]

Sources:

  • hast-util-sanitize docs (defaultSchema, attributes, protocols, tagNames). [1]
  • StackOverflow example showing adding 'data' to default src protocols. [2]
  • rehype-sanitize README (usage with defaultSchema). [3]

🌐 Web query:

rehype-sanitize allowedElements vs tagNames difference reactmarkdown

💡 Result:

Short answer

  • allowedElements is a React‑Markdown prop that filters which HTML tag names React‑Markdown will render (client/renderer-level). [1]
  • tagNames is the rehype‑sanitize schema field (passed to rehype-sanitize) that lists which HAST tag names the sanitizer will keep—it’s a lower‑level sanitization schema used when you run rehype‑sanitize (server/AST‑level). [2][3]

Implications / practical differences

  • Scope: allowedElements runs inside react-markdown’s rendering pipeline; tagNames is applied by the rehype plugin during AST sanitization. Use allowedElements to quick‑filter rendered tags; use tagNames (and attributes) to configure what rehype‑sanitize will strip or allow. [1][2]
  • Attributes/classes: rehype‑sanitize’s schema controls allowed attributes/class names (so enabling a tag there doesn’t automatically allow its className/href/etc.); react-markdown’s allowedElements does not control attributes—if you use rehype-sanitize you must also allow attributes in the sanitizer schema. [2][3]
  • Order: when using rehypeRaw + rehypeSanitize, sanitize’s tagNames/attributes must include any tags/props you want preserved (e.g., span + className for syntax highlighting). React‑Markdown’s allowedElements is applied in its own pipeline and won’t override a sanitizer that removed nodes earlier. [2][3]

References

  • react-markdown options (allowedElements / disallowedElements). [1]
  • rehype-sanitize README / defaultSchema and examples (tagNames, attributes). [2][3]

Sources
[1] react-markdown README (options: allowedElements / disallowedElements).
[2] rehype-sanitize (GitHub README: defaultSchema, tagNames/attributes examples).
[3] rehype-sanitize (npm / examples showing adding tagNames and attributes).


Fix video tag handling: explicitly add to tagNames array in sanitizeSchema.

The video tag and its attributes are defined in sanitizeSchema.attributes and included in allowedElements, but not explicitly added to sanitizeSchema.tagNames. Since rehypeSanitize processes the AST before React-Markdown rendering (line 211), any tag absent from tagNames gets stripped during sanitization. Add 'video' to the tagNames array explicitly (similar to how 'think' is added on line 130) to prevent sanitization from removing video elements.

Additionally:

  • Verify defaultSchema.protocols blocks javascript: URLs for href/src attributes (should be safe by default, but confirm the hast-util-sanitize version handles this correctly).
  • Consider reducing global attributes '*': ['className', 'id', 'dir'] to just className if id and dir are not required on all untrusted elements (reduces potential CSS-based or text-direction attack surfaces).
🤖 Prompt for AI Agents
In @src/components/MarkdownRender/markdown-without-katex.jsx around lines 112 -
138, sanitizeSchema currently lists video attributes but doesn’t include 'video'
in sanitizeSchema.tagNames, so rehypeSanitize will strip video nodes; add
'video' to the tagNames array alongside 'think', ensure rehypeSanitize is still
applied where intended (references: sanitizeSchema, tagNames, rehypeSanitize),
and while here verify defaultSchema.protocols blocks javascript: for href/src
(confirm your hast-util-sanitize version or explicitly whitelist safe
protocols), and consider tightening the global attributes entry ('*':
['className', 'id', 'dir']) to only include required globals (e.g., just
'className') to reduce attack surface.

Comment on lines +112 to +138
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

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

The sanitization schema is missing protocol restrictions, which creates a potential XSS vulnerability. Malicious content can still use dangerous protocols like javascript:, data:, or vbscript: in href and src attributes.

For example, an attacker could inject:

  • <a href="javascript:alert('XSS')">Click me</a>
  • <img src="data:text/html,<script>alert('XSS')</script>">

To fix this, add a protocols configuration to the sanitizeSchema that whitelists only safe protocols. The defaultSchema from rehype-sanitize already includes safe protocol restrictions, but when you spread and override the attributes, you should also explicitly define protocols to ensure they're applied correctly.

Add this to the sanitizeSchema object:

protocols: {
  href: ['http', 'https', 'mailto'],
  src: ['http', 'https'],
  cite: ['http', 'https'],
  longdesc: ['http', 'https'],
}

This ensures that only safe HTTP(S) protocols are allowed in URLs, preventing javascript:, data:, and other dangerous URI schemes.

Copilot uses AI. Check for mistakes.

export function MarkdownRender(props) {
return (
<div dir="auto">
Expand Down Expand Up @@ -178,6 +207,7 @@ export function MarkdownRender(props) {
remarkPlugins={[remarkGfm, remarkBreaks]}
rehypePlugins={[
rehypeRaw,
[rehypeSanitize, sanitizeSchema], // Add sanitization after rehypeRaw to prevent XSS
[
rehypeHighlight,
{
Expand Down
Loading
Loading