Skip to content

Fix: Allow advanced markdown syntax in MarkdownRenderer (fixes #1912) - #1935

Open
anshika1179 wants to merge 1 commit into
durdana3105:mainfrom
anshika1179:fix/issue-1912
Open

Fix: Allow advanced markdown syntax in MarkdownRenderer (fixes #1912)#1935
anshika1179 wants to merge 1 commit into
durdana3105:mainfrom
anshika1179:fix/issue-1912

Conversation

@anshika1179

@anshika1179 anshika1179 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

This PR fixes #1912 by configuring rehype-sanitize to allow className, style, and table/input tags. This ensures that remark-gfm can properly render nested code blocks and complex tables without stripping their styling and alignment attributes.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Markdown rendering so more table and form-related content displays correctly.
    • Preserved common formatting and styling attributes, helping content appear more accurate and consistent.
    • Enabled support for additional table alignment and spanning options in rendered Markdown.

@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

@anshika1179 is attempting to deploy a commit to the durdana3105's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The Markdown renderer expands its rehype sanitization schema to support form inputs, table markup, styling attributes, input attributes, and table cell alignment and span attributes.

Changes

Markdown rendering

Layer / File(s) Summary
Expand Markdown sanitization schema
src/components/MarkdownRenderer.tsx
The strict schema permits input and table elements, global className and style attributes, input state and type attributes, and th/td alignment and span attributes.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested labels: type:bug

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The sanitization changes support complex tables, but the provided diff does not show that remark-gfm is configured as required by issue #1912. Confirm that MarkdownRenderer already enables remark-gfm, or add the plugin configuration and verify nested code blocks and complex tables.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the MarkdownRenderer change and links it to issue #1912.
Out of Scope Changes check ✅ Passed The changes are limited to MarkdownRenderer sanitization and directly support the advanced Markdown rendering objective in issue #1912.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/MarkdownRenderer.tsx`:
- Line 22: Remove the global style allowance from the rehypeSanitize attribute
configuration in MarkdownRenderer, leaving only the existing globally permitted
attributes. If styling is required, allow style only on the specific elements
that need it; preserve table alignment through align attributes.
- Around line 20-26: Update the attribute configuration in the Markdown renderer
to merge each relevant defaultSchema entry before adding the explicitly required
custom attributes, rather than replacing those entries. Preserve existing global
attributes and retain default ARIA, className, style, and table attributes such
as scope and headers while keeping the current custom allowlists for links and
inputs.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e968454b-5dfe-467b-8885-fa985813564e

📥 Commits

Reviewing files that changed from the base of the PR and between 880eed9 and b95d243.

📒 Files selected for processing (1)
  • src/components/MarkdownRenderer.tsx

Comment on lines 20 to +26
attributes: {
...defaultSchema.attributes,
'*': ['className', 'style'],
a: ['href', 'title', 'target', 'rel'],
input: ['type', 'checked', 'disabled'],
th: ['align', 'colSpan', 'rowSpan'],
td: ['align', 'colSpan', 'rowSpan'],

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.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)(package\.json|package-lock\.json|yarn\.lock|pnpm-lock\.yaml|MarkdownRenderer\.tsx|ChatMessage\.tsx|ChatBox\.tsx)$' || true

echo
echo "== package versions =="
for f in package.json package-lock.json yarn.lock pnpm-lock.yaml; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    if [ "$f" = "package.json" ]; then
      node -e "const p=require('./package.json'); console.log(JSON.stringify({dependencies:p.dependencies,devDependencies:p.devDependencies,engines:p.engines}, null, 2))"
    elif [ "$f" = "package-lock.json" ]; then
      node -e "const p=require('./package-lock.json'); for (const key of ['node_modules/rehype-sanitize','node_modules/hast-util-sanitize','node_modules/remark-gfm','node_modules/react-markdown']) console.log(key, p.packages?.[key]?.version)"
    else
      rg -n "rehype-sanitize|hast-util-sanitize|remark-gfm|react-markdown" "$f" -C 2 || true
    fi
  fi
done

echo
echo "== MarkdownRenderer =="
fd -a 'MarkdownRenderer\.tsx$' . | while read -r f; do
  echo "--- $f ---"
  sed -n '1,180p' "$f" | cat -n
done

echo
echo "== usages =="
for f in $(fd 'ChatMessage\.tsx$|ChatBox\.tsx$|MarkdownRenderer\.tsx$' .); do
  echo "--- $f ---"
  wc -l "$f"
  sed -n '1,220p' "$f" | cat -n
done

Repository: durdana3105/peer-learning

Length of output: 14265


🌐 Web query:

rehype-sanitize 6.0.0 default schema hast-util-sanitize 5.0.0 GFM footnote attributes tables

💡 Result:

In rehype-sanitize 6.0.0, which utilizes hast-util-sanitize 5.0.0, the default schema includes sanitization logic that affects GFM (GitHub Flavored Markdown) features like footnotes, tables, and IDs [1][2][3]. The core issue involves DOM clobbering prevention [4]. By default, rehype-sanitize and hast-util-sanitize rewrite specific attributes (such as id and name) to prefix them, which is intended to protect against security vulnerabilities [5][6][4]. Specifically regarding GFM footnotes and internal links: - The default schema's clobbering protection often causes mismatches between footnote references and their corresponding footnote definitions because the IDs and the referenced fragments in href attributes are rewritten inconsistently or clobbered [5][6][4]. - While the default schema allows for elements used in GFM tables (such as table, thead, tbody, tr, td, th), the aggressive sanitization of IDs and link targets can still break navigation within those tables or footnoted content [4][7]. If you are encountering broken GFM footnote links or table-related ID issues, it is typically because the library is sanitizing these identifiers by default to mitigate DOM clobbering [4]. You can address this by providing a custom schema to rehype-sanitize, extending the default schema, or adjusting the clobbering configuration if necessary [8][4]. Developers often work around this by either disabling specific clobbering rules or adjusting the schema to allow the required attributes and values without the default prefixing [4].

Citations:


🌐 Web query:

GitHub rehype-sanitize v6.0.0 package.json dependencies hast-util-sanitize

💡 Result:

The rehype-sanitize version 6.0.0 package relies on hast-util-sanitize as its underlying utility for cleaning HAST (HTML Abstract Syntax Tree) nodes [1][2]. In the package.json for rehype-sanitize v6.0.0, hast-util-sanitize is listed as a dependency, specifically requiring version ^5.0.0 [1][3]. Rehype-sanitize functions as a higher-level abstraction, utilizing hast-util-sanitize to perform the actual sanitization of the syntax trees while simplifying the integration for users within the unified/rehype ecosystem [1][4][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp="$(mktemp -d)"
cd "$tmp"

echo "== fetch packages =="
npm pack github:syntax-tree/hast-util-sanitize@5.0.2 --silent 2>/dev/null || true
tar -xzf hast-util-sanitize-5.0.2.tgz

echo
echo "== locate schema assets =="
find package -maxdepth 3 -type f \( -name '*.js' -o -name '*.mjs' -o -name '*.d.ts' -o -name 'schema.*' \) -print | sort | sed -n '1,120p'

echo
echo "== search default attributes for relevant keys =="
rg -n "default|attributes|footnote|aria|role|scope|headers|clobber|className|data-footnote|id:" package -S || true

echo
echo "== inspect likely default/schema file slices =="
for f in $(find package -maxdepth 3 -type f \( -name '*.js' -o -name '*.mjs' -o -name 'schema.*' \) | sort); do
  if rg -q "defaultSchema|attributes|footnote|role|scope|headers" "$f"; then
    echo "--- $f ---"
    wc -l "$f"
    sed -n '1,260p' "$f" | cat -n
  fi
done

Repository: durdana3105/peer-learning

Length of output: 389


🌐 Web query:

github.com syntax-tree/hast-util-sanitize v5.0.2 default schema attributes

💡 Result:

In hast-util-sanitize version 5.0.2, the default schema is exported as defaultSchema [1][2]. It follows GitHub-style sanitation rules to determine which nodes and properties are permitted [1][3][2]. The attributes configuration is defined as a mapping of tag names to allowed property names, where defaultSchema.attributes provides the default values [1][4]. Key characteristics of this configuration include: 1. Special Wildcard Key: The key '' is used to define property names that are allowed on all elements [1][5]. 2. Data Properties: The special value 'data' can be used as a property name to allow all data attributes [1][2]. 3. Granular Control: You can specify simple string arrays for property names, or use nested arrays to restrict specific values (e.g., input: [['type', 'checkbox', 'radio']]) [1][5]. 4. Extending the Schema: To modify the default attributes, you can clone defaultSchema and override the attributes key in your custom configuration [1][4]. If a top-level key is missing in a user-provided schema, the library automatically falls back to the corresponding value from defaultSchema [1][2]. For the full list of default allowed attributes, you can access the defaultSchema object directly in your code: import {defaultSchema} from 'hast-util-sanitize' console.log(defaultSchema.attributes)

Citations:


🌐 Web query:

raw.githubusercontent.com syntax-tree/hast-util-sanitize v5.0.2 default.js

💡 Result:

In the hast-util-sanitize package version 5.0.2, there is no file named default.js [1]. The package exports an identifier called defaultSchema, which is defined within the library's source code (typically in lib/index.js or similar) and made available to users [2][3][4]. The defaultSchema serves as the standard configuration for the sanitize function, designed to mimic GitHub's sanitization style [2][3]. It specifies which HTML nodes, attributes, and properties are permitted [5][4]. Because the package uses named exports, you do not import a file named default.js; instead, you import the schema directly from the package [2][3]: import { defaultSchema, sanitize } from 'hast-util-sanitize'; Users can access, inspect, or extend this schema by cloning it or using tools like deepmerge to modify it for their specific requirements [2][5][4]. The package documentation provides examples on how to utilize this default configuration when sanitizing HAST (Hypertext Abstract Syntax Tree) nodes [2][4].

Citations:


🌐 Web query:

unpkg.com hast-util-sanitize 5.0.2 lib/index.js defaultSchema attributes footnote role scope headers

💡 Result:

The hast-util-sanitize package (version 5.0.2) provides a defaultSchema that defines which nodes and properties are permitted in a HAST (Hypertext Abstract Syntax Tree) [1][2]. This default schema mimics GitHub-style sanitation [1][3]. Regarding the specific attributes you mentioned (footnote, role, scope, headers): 1. defaultSchema is an exported object that maps tag names to allowed property names [1][3]. 2. The package does not explicitly list footnote, role, scope, or headers in its core documentation as being part of the defaultSchema [3][4]. 3. The defaultSchema can be extended or modified by the user [1][3]. To allow these attributes, you can clone the defaultSchema and merge your own modifications into the attributes object [1][3]. For example, to allow these attributes on specific elements, you can use a library like deepmerge [1][4]: javascript import deepmerge from 'deepmerge'; import { defaultSchema } from 'hast-util-sanitize'; const mySchema = deepmerge(defaultSchema, { attributes: { // Example: allow 'scope' and 'headers' on 'td' or 'th' elements td: ['scope', 'headers'], th: ['scope', 'headers'], // Example: allow 'role' on any element '*': ['role'] } }); Because hast-util-sanitize follows GitHub's sanitation rules, attributes not included in the allow-list are stripped by default when using defaultSchema [1][3]. If you require footnote, role, scope, or headers, you must explicitly add them to your configuration schema [1][4].

Citations:


🌐 Web query:

raw.githubusercontent.com syntax-tree/hast-util-sanitize v5.0.2 default.js attributes | rg footnote role scope headers className

💡 Result:

In hast-util-sanitize v5.0.2, the default schema (defaultSchema) defines allowed attributes for HAST nodes, primarily following GitHub's sanitation rules [1][2]. Regarding the specific attributes queried: - className: This is allowed on many elements by default, but it is often restricted to specific values (e.g., for syntax highlighting classes) on certain tags [1][3]. - footnote, role, scope, headers: These attributes are generally not included in the default schema for elements unless specifically permitted by the underlying GitHub sanitation logic that the library mimics [1][2]. The attributes configuration in hast-util-sanitize is a mapping of tag names to allowed property names [1][4]. You can extend or override the default behavior by merging your own schema with defaultSchema [2][5]. For example, to allow these attributes globally or on specific elements, you can define them in your schema configuration: const schema = deepmerge(defaultSchema, { attributes: { '*': ['className', 'role'], // Example: allowing className and role on all elements th: ['scope', 'headers'], // Example: allowing specific table attributes //... } }); Because hast-util-sanitize uses an allow-list approach, any attribute not explicitly defined in the schema (either in defaultSchema or your custom extension) will be stripped from the HAST nodes during sanitization [1][2].

Citations:


🌐 Web query:

raw.githubusercontent.com syntax-tree/hast-util-sanitize v5.0.2 default.js table th td footnote role scope headers className

💡 Result:

The hast-util-sanitize package, version 5.0.2, uses a default schema (defaultSchema) designed to mimic GitHub's sanitization logic [1][2]. This schema determines which HTML tags and attributes are permitted in the output tree [3][4]. Regarding the specific properties and tags mentioned: 1. className: By default, the schema includes specific rules for the className attribute. While className is not allowed on all elements by default, you can allow it globally by modifying the schema [1][3]. This is commonly done by merging a custom configuration with the defaultSchema [1][2]: import deepmerge from 'deepmerge'; import { defaultSchema } from 'hast-util-sanitize'; const schema = deepmerge(defaultSchema, {attributes: {'*': ['className']}}); 2. table, th, td, footnote (note: footnote is not a standard HTML element), role, scope, headers: The defaultSchema explicitly defines which attributes are permitted for specific elements, including table-related tags [3][5]. - Attributes like scope and headers are typically managed within the attributes configuration for table elements (th/td) [3][6]. - The role attribute is generally stripped by default unless explicitly allowed in your custom schema, as it is often restricted to prevent accessibility-related XSS or spoofing issues. - If an element or attribute is not included in the defaultSchema for a specific tag, it is removed during sanitization [4][5]. To allow these, you must extend the defaultSchema by adding them to the attributes object for the corresponding tag names [1][7]. The package does not have a single flat file that lists all allowed attributes; instead, it uses a structured schema object [4]. You can inspect the actual allowed values by importing defaultSchema from the package in a Node.js environment to view the full default configuration [1][8].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp="$(mktemp -d)"
cd "$tmp"

echo "== fetch unrejected package tarball if possible =="
url="$(node - <<'JS'
const fs = require('fs')
const lock = JSON.parse(fs.readFileSync('../package-lock.json', 'utf8'))
const pkg = lock.packages?.['node_modules/hast-util-sanitize']?.resolved || lock.packages?.['node_modules/hast-util-sanitize']?.version
console.log(pkg)
JS
)"
echo "resolved: $url"
npm fetch "$url" --silent > hast-util-sanitize.tgz 2>&1 || cat hast-util-sanitize.tgz
tar -tzf hast-util-sanitize.tgz | sed -n '1,120p'
tar -xzf hast-util-sanitize.tgz
echo
echo "== default schema source =="
find package -maxdepth 3 -type f \( -name '*.js' -o -name '*.mjs' -o -name '*.d.ts' \) -print0 | sort -z | while IFS= read -r -d '' f; do
  if grep -qE "defaultSchema|attributes|className|aria|role|scope|headers|footnote|data-footnote|clobber" "$f"; then
    echo "--- $f ---"
    wc -l "$f"
    grep -nE "defaultSchema|attributes|className|aria|role|scope|headers|footnote|data-footnote|clobber|protocols" "$f" -C 3 || true
  fi
done

echo
echo "== lockfile exact package info =="
node - <<'JS'
const fs = require('fs')
const lock = JSON.parse(fs.readFileSync('../../package-lock.json', 'utf8'))
for (const key of ['node_modules/react-markdown','node_modules/remark-gfm','node_modules/rehype-sanitize','node_modules/hast-util-sanitize']) {
  console.log(key, lock.packages?.[key])
}
JS

Repository: durdana3105/peer-learning

Length of output: 981


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cd "$(pwd)"
tmp="$(mktemp -d)"
cd "$tmp"

echo "== inspect local lock package info =="
node - <<'JS'
const fs = require('fs')
const lock = JSON.parse(fs.readFileSync('/workspace/package-lock.json', 'utf8'))
for (const key of ['node_modules/react-md','node_modules/react-markdown','node_modules/remark-gfm','node_modules/rehype-sanitize','node_modules/hast-util-sanitize']) {
  console.log(key, JSON.stringify(lock.packages?.[key], null, 2))
}
JS

echo
echo "== fetch hast-util-sanitize lock resolved source if resolvable =="
node - <<'JS'
const fs = require('fs')
const lock = JSON.parse(fs.readFileSync('/workspace/package-lock.json', 'utf8'))
const pkg = lock.packages?.['node_modules/hast-util-sanitize']
console.log(JSON.stringify(pkg, null, 2))
JS

Repository: durdana3105/peer-learning

Length of output: 984


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package files and cwd =="
pwd
git ls-files 'package-lock.json' 'package.json' | cat -n

echo
echo "== local lock package info =="
node - <<'JS'
const fs = require('fs')
const paths = ['package-lock.json', 'package-lock.json', '../package-lock.json', '/workspace/package-lock.json']
let lock
for (const p of paths) {
  try {
    if (fs.existsSync(p)) {
      lock = JSON.parse(fs.readFileSync(p, 'utf8'))
      console.log('loaded', p)
      break
    }
  } catch (e) { console.error(p, e.message) }
}
if (!lock) throw new Error('no package-lock.json loaded')
for (const key of ['node_modules/react-markdown','node_modules/remark-gfm','node_modules/rehype-sanitize','node_modules/hast-util-sanitize']) {
  console.log(key)
  console.log(JSON.stringify(lock.packages?.[key], null, 2))
}
JS

Repository: durdana3105/peer-learning

Length of output: 2685


🌐 Web query:

hast-util-sanitize 5.0.2 npm files lib default.js source

💡 Result:

In the npm package hast-util-sanitize version 5.0.2, the source code is located within the lib directory, and it exports an identifier named defaultSchema [1][2][3]. This defaultSchema provides a predefined configuration for the sanitize function, which is designed to mirror how GitHub handles HTML sanitization [4][5]. Users can customize sanitization by providing their own schema or by extending the defaultSchema [4][5]. The package is written as an ESM-only module and is maintained by the syntax-tree collective [1][3]. You can view the package contents and source structure directly via services like UNPKG [6].

Citations:


🌐 Web query:

hast-util-sanitize v5.0.2 npmx default attributes role scope headers className table footnote

💡 Result:

In hast-util-sanitize v5.0.2, the default configuration is determined by the defaultSchema object, which is modeled after GitHub's sanitation practices [1][2]. Regarding the specific attributes you inquired about: 1. className: This property is generally allowed on elements if explicitly configured, but it is not included in the default schema's wildcard ('') list by default [1][3]. To allow className on all elements, you must merge a custom schema with the defaultSchema, for example: deepmerge(defaultSchema, {attributes: {'': ['className']}}) [1][4][3]. 2. role: ARIA attributes (such as role) are typically allowed by the default schema for accessibility, as they are part of the standard set of attributes deemed safe by GitHub-style sanitation [5]. 3. scope and headers: These attributes are standard for HTML table elements and are included within the defaultSchema for relevant tags (such as th) to maintain valid, accessible table structures [5]. 4. table and footnote: These refer to HTML tags rather than attributes. The defaultSchema permits standard HTML elements like table (and associated tags like thead, tbody, tr, th, td), provided they do not contain dangerous properties or nested structures [1][3]. A footnote is not a native HTML tag and is not explicitly handled by the default schema; if you use custom tags for footnotes (e.g., ), they will be stripped or sanitized unless you add them to the allowed tagNames in your schema [2]. To modify these behaviors, you can pass a custom schema to the sanitize function. If any top-level key (such as attributes) is missing in your provided schema, the corresponding value from defaultSchema is used automatically [1][3].

Citations:


Merge the defaultSchema attribute entries before overriding allowed attributes.

The nested object spread replaces each schema entry instead of extending it. This keeps className, ARIA attributes, and table attributes such as scope and headers out when they would otherwise be allowed. Only explicitly needed additional attributes should remain in the custom arrays.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/MarkdownRenderer.tsx` around lines 20 - 26, Update the
attribute configuration in the Markdown renderer to merge each relevant
defaultSchema entry before adding the explicitly required custom attributes,
rather than replacing those entries. Preserve existing global attributes and
retain default ARIA, className, style, and table attributes such as scope and
headers while keeping the current custom allowlists for links and inputs.

],
attributes: {
...defaultSchema.attributes,
'*': ['className', 'style'],

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.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files =="
git ls-files | sed -n '1,120p'

echo
echo "== find MarkdownRenderer and relevant files =="
fd -a 'MarkdownRenderer|ChatMessage|ChatBox' . 2>/dev/null || true

echo
echo "== package manifests =="
for f in package.json pnpm-lock.yaml package-lock.json yarn.lock bun.lockb; do
  [ -f "$f" ] && { echo "--- $f"; if [ "$f" != bun.lockb ]; then sed -n '1,220p' "$f"; fi; }
done

echo
echo "== MarkdownRenderer outline/content =="
if [ -f src/components/MarkdownRenderer.tsx ]; then
  wc -l src/components/MarkdownRenderer.tsx
  cat -n src/components/MarkdownRenderer.tsx
fi

echo
echo "== search rehype/rehypePlugins/hast-util-to-jsx/runtime usage =="
rg -n "rehypePlugins|rehype|remark|hast-util-to-jsx|toJsxRuntime|style|rawHtml|stripHtml" src package.json || true

Repository: durdana3105/peer-learning

Length of output: 26834


🌐 Web query:

rehype-sanitize v6 inline-style-parser style sanitizer defaultSchema allows style property

💡 Result:

In rehype-sanitize v6, the defaultSchema does not allow the style property [1][2][3]. The library is designed to be restrictive by default to prevent security risks such as CSS-based attacks (e.g., overlaying invisible elements or data exfiltration via external resources) [3]. If your application requires support for inline styles, you must explicitly extend the defaultSchema by adding the style attribute to the elements where you want to permit it [3]. Because rehype-sanitize uses inline-style-parser to handle style strings, providing access to the style attribute allows the plugin to parse and sanitize the content within that attribute rather than stripping it entirely [4][5][6]. To allow the style attribute, you can configure the plugin as follows [3]: import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'; const customSchema = {...defaultSchema, attributes: {...defaultSchema.attributes, // Add 'style' to the allowed attributes for the desired elements span: [...(defaultSchema.attributes?.span || []), 'style'], }, }; // Use the custom schema in your unified pipeline //.use(rehypeSanitize, customSchema) When enabling this, it is recommended to be deliberate about which elements you allow the style attribute on, as it increases the attack surface of your application [3].

Citations:


Security Misconfiguration (CWE-20): Improper Input Validation

Remove the global style allowance unless inline styling is required.

Adding style to attributes["*"] lets user/assistant markdown pass inline styles through rehypeSanitize. inline-style-parser sanitizes those values, but this still extends the attack surface and can throw on invalid CSS. Use style only on specific elements when needed; GFM tables should rely on align attributes instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/MarkdownRenderer.tsx` at line 22, Remove the global style
allowance from the rehypeSanitize attribute configuration in MarkdownRenderer,
leaving only the existing globally permitted attributes. If styling is required,
allow style only on the specific elements that need it; preserve table alignment
through align attributes.

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.

[Bug] MarkdownRenderer fails to render nested code blocks or complex tables

1 participant