Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Unescaped Text in `ReactEmailExporter` Causing HTML Injection / XSS and Output Corruption

クローズ 初心者向け
#3,072 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
85/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
react, typescript
領域
security

調査の方向性

packages/xl-email-exporter/src/react-email/reactEmailExporter.tsx の ReactEmailExporter.transformStyledText から開始し、styledText.text が dangerouslySetInnerHTML にどのように渡されているかを確認します。改行を br に変換する前に生テキストがエスケープされていることを確認し、その後、再現ケースで比較演算子がリテラルのまま保持され、注入された HTML やスクリプトがレンダリングされないことを検証します。

索引モデルが issue の本文から書いたものです。

説明

needs-triage
What’s broken?

In @blocknote/xl-email-exporter, user text content is injected directly into dangerouslySetInnerHTML without HTML entity encoding.
Inside packages/xl-email-exporter/src/react-email/reactEmailExporter.tsx, the method transformStyledText converts newlines to <br /> but passes the raw unescaped styledText.text directly into the DOM:

public transformStyledText(styledText: StyledText<S>) {
  const stylesArray = this.mapStyles(styledText.styles);
  const styles = Object.assign({}, ...stylesArray);
  return (
    <span
      style={styles}
      dangerouslySetInnerHTML={{
        __html: styledText.text.replace(/\n/g, "<br />"),
      }}
    />
  );
}


This causes two critical defects:

- Security (HTML / XSS Injection): If an application exports user-generated BlockNote documents into emails, malicious payloads (e.g. <img src=x onerror=...>, <script>, or phishing HTML structures) are rendered verbatim in the generated email.
- Rendering & Layout Corruption: Standard text containing mathematical or programming comparison operators (e.g., 5 < 10 && 10 > 5, array[i < 5], or <CustomComponent> in documentation) is parsed by the email engine as raw HTML elements. This causes the text to either be stripped/hidden or break the email's DOM layout.

### What did you expect to happen?

All raw text characters (such as <, >, &, ", and ') should be properly HTML-escaped before inserting <br /> tags into dangerouslySetInnerHTML, ensuring:

- Safe rendering of untrusted user content.
- Visual preservation of literal < and > characters in exported email clients.

### Steps to reproduce

import { BlockNoteSchema, defaultBlockSpecs } from "@blocknote/core";
import { ReactEmailExporter, reactEmailDefaultSchemaMappings } from "@blocknote/xl-email-exporter";

const schema = BlockNoteSchema.create({ blockSpecs: defaultBlockSpecs });
const exporter = new ReactEmailExporter(schema, reactEmailDefaultSchemaMappings);

const blocks = [
  {
    id: "block-1",
    type: "paragraph" as const,
    props: {},
    content: [
      {
        type: "text" as const,
        text: "Condition check: x < 10 & y > 20, or <script>alert(1)</script>",
        styles: {},
      },
    ],
    children: [],
  },
];

const emailHtml = await exporter.toReactEmailDocument(blocks);
console.log(emailHtml);

### BlockNote version

Version: 0.54.0 (and main branch) Package: @blocknote/xl-email-exporter

### Environment

OS: Any (Windows / macOS / Linux) Node.js: >=18.0.0 React: 18.x / 19.x Browser/Runtime: Node.js, Next.js, or browser export environments

### Additional context

Proposed Fix
Add an escapeHtml utility function and sanitize styledText.text before newline substitution:

--- a/packages/xl-email-exporter/src/react-email/reactEmailExporter.tsx
+++ b/packages/xl-email-exporter/src/react-email/reactEmailExporter.tsx
@@ -24,6 +24,15 @@ import React, { CSSProperties } from "react";
+function escapeHtml(str: string): string {

  • return str
  • .replace(/&/g, "&")
  • .replace(/</g, "<")
  • .replace(/>/g, ">")
  • .replace(/"/g, """)
  • .replace(/'/g, "'");
    +}

export class ReactEmailExporter<
B extends BlockSchema,
S extends StyleSchema,
@@ -66,7 +75,7 @@ export class ReactEmailExporter<
<span
style={styles}
dangerouslySetInnerHTML={{

  •      __html: styledText.text.replace(/\n/g, "<br />"),
    
  •      __html: escapeHtml(styledText.text).replace(/\n/g, "<br />"),
       }}
     />
    
    );

### Contribution

- [ ] I'd be interested in contributing a fix for this issue

### Sponsor

- [ ] I'm a [sponsor](https://www.blocknotejs.org/pricing) and would appreciate if you could look into this sooner than later 💖
主要言語
TypeScript
スター
10.2k
フォーク
772
平均マージ
6日 6時間
マージ済み PR(30日)
25

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

TypeCellOS/BlockNote のほかの issue

TypeCellOS/BlockNote の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。