This is an automated email from the ASF dual-hosted git repository.

ovilia pushed a commit to branch fix-security
in repository https://gitbox.apache.org/repos/asf/echarts-handbook.git

commit 38e28b1f5e7cbc920e917fd26701c5de18dda128
Author: Ovilia <[email protected]>
AuthorDate: Tue Mar 3 10:05:38 2026 +0800

    fix(security): disclose ReDoS risk for dataset filter reg
---
 contents/en/best-practices/security.md | 27 +++++++++++++++++++++++++--
 contents/zh/best-practices/security.md | 24 ++++++++++++++++++++++++
 2 files changed, 49 insertions(+), 2 deletions(-)

diff --git a/contents/en/best-practices/security.md 
b/contents/en/best-practices/security.md
index bc2d678..7aa0298 100644
--- a/contents/en/best-practices/security.md
+++ b/contents/en/best-practices/security.md
@@ -24,7 +24,8 @@ Before deploying charts, please review this **checklist** to 
ensure your usage i
 | **option [tooltip.formatter](${optionPath}tooltip.formatter)**<br>· 
`formatter` allows HTML string or DOM elements input, which are later rendered 
directly inside the tooltip, where XSS risks need to be 
considered.<br>(exceptions): A string directly set to the `formatter` is 
treated as a simple template for later combining with data internally. 
[tooltip.renderMode: 'richText'](${optionPath}tooltip.renderMode) is another 
level of templating syntax for styling. Both of them are internall [...]
 | **option [tooltip.extraCssText](${optionPath}tooltip.extraCssText)**<br>· 
`extraCssText` accepts a raw CSS style string for later directly appending to 
`tooltipEl.style.cssText`(via the DOM API).<br>(exceptions): this option is not 
applicable when [tooltip.renderMode: 
'richText'](${optionPath}tooltip.renderMode).<br> | Safe if the input comes 
from trusted sources; otherwise, a careful assessment is required.<br><br>See 
section ["Passing inline CSS Safely"](${lang}/best-practices/securi [...]
 | **option [title.link](${optionPath}title.link)**<br>**option 
[title.sublink](${optionPath}title.sublink)**<br>**option 
[series-treemap.data.link](${optionPath}series-treemap.data.link)**<br>**option 
[series-sunburst.data.link](${optionPath}series-sunburst.data.link)**<br>· They 
accept raw URLs directly for these links. | Safe if the input comes from 
trusted sources; otherwise, XSS risks should be considered.<br><br>See section 
["Passing Raw URLs Safely"](${lang}/best-practices/security [...]
-**option 
[toolbox.feature.saveAsImage.name](${optionPath}toolbox.feature.saveAsImage.name)**<br>**option
 
[toolbox.feature.saveAsImage.type](${optionPath}toolbox.feature.saveAsImage.type)**<br>**option
 [title[0].text](${optionPath}title.text)**<br>· The download filename is 
assembled by `{name}.{type}` without validation or sanitization. If `name` is 
not provided, `title[0].text` (if any) has historically been used instead, 
although this usage is not recommended. | See section ["Passing D [...]
+| **option 
[toolbox.feature.saveAsImage.name](${optionPath}toolbox.feature.saveAsImage.name)**<br>**option
 
[toolbox.feature.saveAsImage.type](${optionPath}toolbox.feature.saveAsImage.type)**<br>**option
 [title[0].text](${optionPath}title.text)**<br>· The download filename is 
assembled by `{name}.{type}` without validation or sanitization. If `name` is 
not provided, `title[0].text` (if any) has historically been used instead, 
although this usage is not recommended. | See section ["Passing [...]
+| **option [dataset.transform](${optionPath}dataset.transform) with type: 
'filter' and config.reg**<br>· The filter transform's `config` supports a `reg` 
key (string or RegExp) for filtering rows by regex match. String values are 
compiled directly into `RegExp` and executed against every row, with no 
validation of pattern complexity, length, or execution time. | If `config` 
(including `reg`) can come from untrusted sources, ReDoS (Regular Expression 
Denial of Service) risk exists and may [...]
 | All JS-function inputs (callbacks) | This is generally not a concern, unless 
special requirements involve untrusted code.<br><br>See section ["Passing JS 
Function Safely"](${lang}/best-practices/security#passing_js_function_safely) 
for details. |
 
 
@@ -110,9 +111,31 @@ Currently, only the 
[saveAsImage](${optionPath}toolbox.feature.saveAsImage) feat
 Otherwise, although modern browsers have significantly improved their handling 
of unsanitized download filenames (e.g., path traversal sequences `../`, `..\\` 
are stripped, and special characters are correctly handled), some risks still 
remain, including the inconsistent behaviors with reserved characters and 
length limitations. Furthermore, the behavior of older clients or browsers is 
unclear and less robust. Therefore, sanitization or preprocessing are required 
for these untrusted inputs.
 
 
+## Dataset Filter Transform and the reg Option [[[#dataset_filter_reg_safely]]]
+
+The [dataset filter transform](${optionPath}dataset.transform) `config` 
supports a `reg` key to filter data rows by matching a dimension value against 
a regular expression. `reg` accepts a string or a `RegExp`; strings are 
**compiled directly into `RegExp`** and **each row** is matched via `test()`, 
with **no validation or limit on pattern complexity, length, or execution 
time**. This design supports serializable configurations (e.g., `reg: '^asdf$'` 
from JSON or APIs), but when `config` [...]
+
+### Risk
+
+If an attacker can influence chart options (e.g., saved dashboard or analytics 
configs), they can supply a pattern with **catastrophic backtracking** (e.g., 
nested quantifiers like `^(a+)+$`) together with long non-matching input. A 
single `test()` call can then cause exponential CPU usage:
+
+- **Client-side**: The browser tab rendering the chart may freeze and require 
force-closing.
+- **Server-side**: With ECharts server-side rendering (SSR), one malicious 
request can block the Node.js event loop and affect all concurrent users.
+- **Persistent**: If a malicious config is stored (e.g., in a dashboard), 
every user who opens that view can trigger the DoS.
+
+This does not involve data disclosure or modification; impact is primarily on 
availability.
+
+### Recommendations
+
+- **If `config` (including `reg`) for `dataset.transform` comes entirely from 
trusted sources** (e.g., your application or a controlled backend), no extra 
measures are needed.
+- **If `config` may come from untrusted sources** (e.g., user input, external 
APIs, unvalidated database content):
+  - Before passing options to ECharts, enforce a **maximum length** and 
**complexity checks** on the `reg` string (e.g., reject patterns with nested 
quantifiers or other known catastrophic-backtracking constructs), or allow only 
a whitelist of simple patterns.
+  - Alternatively, consider a regex engine with linear-time guarantees (e.g., 
RE2 on the server) or add a **timeout** for matching (e.g., run the work in a 
Web Worker with a time limit) to reduce ReDoS risk.
+  - If the product does not need untrusted parties to supply regex patterns, 
disallow `reg` from external config and only allow safe patterns defined in 
code.
+
+
 ## Passing JS Function Safely [[[#passing_js_function_safely]]]
 
 ECharts options (i.e., the input to 
[chart.setOption()](${apiPath}echartsInstance.setOption)) are primarily 
declarative, but some options accept JS-function (callbacks) to provide greater 
expressiveness and flexibility. Examples include 
[label.formatter](${optionPath}series-scatter.label.formatter), 
[axisTick.interval](${optionPath}xAxis.axisTick.interval), and similar. In most 
use cases, these JS-function options are part of the source code of the 
application itself and thus fully trust [...]
 
 However, certain products may allow JS-function options to originate from 
untrusted sources, for example, end users. Allowing this introduces both 
security risks and maintenance costs. Essentially, this scenario carries the 
same level of risk as allowing code execution in untrusted raw HTML, and can be 
mitigated using similar approaches, as discussed in ["Passing Raw HTML 
Safely"](${lang}/best-practices/security#passing_raw_html_safely).
-
diff --git a/contents/zh/best-practices/security.md 
b/contents/zh/best-practices/security.md
index 5972a29..1a0b3f9 100644
--- a/contents/zh/best-practices/security.md
+++ b/contents/zh/best-practices/security.md
@@ -26,6 +26,7 @@ ECharts 通过 Canvas 或 SVG 渲染,只有几个特殊组件例外,允许 H
 | **option [tooltip.extraCssText](${optionPath}tooltip.extraCssText)**<br>· 
`extraCssText` 接受一个原始 CSS style 字符串,并接下来直接拼接进 
`tooltipEl.style.cssText`。<br>(例外):[tooltip.renderMode: 
'richText'](${optionPath}tooltip.renderMode) 时此 `extraCssText` 无效。 | 
若输入来自可信来源,则一般无安全问题,否则需要仔细评估风险。<br><br>详细描述见 [“传入内联 CSS 
时的安全考虑”](${lang}/best-practices/security#passing_inline_css_safely)。 |
 | **option [title.link](${optionPath}title.link)**<br>**option 
[title.sublink](${optionPath}title.sublink)**<br>**option 
[series-treemap.data.link](${optionPath}series-treemap.data.link)**<br>**option 
[series-sunburst.data.link](${optionPath}series-sunburst.data.link)**<br>· 这些 
option 直接接受原始 URL 字符串。 | 若若输入来自可信来源,则一般无安全问题,否则须要考虑 XSS 风险。<br><br>详细描述见 ["传入 
URL 时的安全考虑"](${lang}/best-practices/security#passing_raw_urls_safely)。 |
 | **option 
[toolbox.feature.saveAsImage.name](${optionPath}toolbox.feature.saveAsImage.name)**<br>**option
 
[toolbox.feature.saveAsImage.type](${optionPath}toolbox.feature.saveAsImage.type)**<br>**option
 [title[0].text](${optionPath}title.text)**<br>· `saveAsImage` 功能的下载文件名由 
`{name}.{type}` 拼装而成,并未做额外校验或净化(sanitization)处理。如果 `name` 没有指定,则使用 
`title[0].text` 替代,尽管这种用法并不推荐。 | 详细描述见 
[“传入下载文件名时的安全考虑”](${lang}/best-practices/security#passing_download_filename_safely)。
 |
+| **option [dataset.transform](${optionPath}dataset.transform) 中 type: 
'filter' 的 config.reg**<br>· filter 变换的 `config` 支持 `reg` 键,接受字符串或 
RegExp,用于按正则匹配筛选数据行。字符串会直接被编译为 `RegExp`,并对每一行数据执行匹配,未对正则复杂度或长度做校验。 | 若 
`config`(含 `reg`)来自不受信任来源,存在 
ReDoS(正则表达式拒绝服务)风险,可能导致浏览器标签页卡死或服务端渲染阻塞。<br><br>详细描述见 [“Dataset 
筛选变换中正则(reg)的安全考虑”](${lang}/best-practices/security#dataset_filter_reg_safely)。 
|
 | 所有“JS 函数”输入(回调) | 通常无安全问题,除非存在特殊使用场景(例如需要执行不可信来源的函数)。<br><br>详细描述见 [“传入 JS 
函数时的安全考虑”](${lang}/best-practices/security#passing_js_function_safely)。 |
 
 
@@ -111,6 +112,29 @@ HTML 的安全(见 [“传入 HTML 时的安全考虑”](${lang}/best-practic
 否则,虽然现代浏览器已显著改进了文件名的自动转义和净化处理(如去除 `../` 
之类试图访问上级目录的路径、正确处理特殊字符等),但也存在一些风险,例如不同系统对保留字符的处理有差异,长度限制也不同。此外,旧浏览器或客户端的行为不够明确且不足够可信赖。因此,对这些“不受信任”的文件名仍应在输入前净化处理。
 
 
+## Dataset 筛选变换中正则(reg)的安全考虑 [[[#dataset_filter_reg_safely]]]
+
+[dataset 的 filter 变换](${optionPath}dataset.transform) 的 `config` 支持 `reg` 
键,用于按正则表达式匹配某一维度的值以筛选数据行。`reg` 可接受字符串或 `RegExp`;字符串会**直接被编译为 
`RegExp`**,并对**每一行**数据执行 `test()`,且**未对正则模式的复杂度、长度或执行时间做任何校验或限制**。该设计便于从 
JSON/API 等可序列化配置中传入正则(如 `reg: '^asdf$'`),但当 `config` 来自“不受信任”的来源时,会引入 
**ReDoS(Regular Expression Denial of Service,正则表达式拒绝服务)** 风险。
+
+### 风险说明
+
+若攻击者能控制或影响图表配置(例如看板、分析平台中用户可保存的图表配置),可构造带有**灾难性回溯**(catastrophic 
backtracking)的正则模式(例如嵌套量词 `^(a+)+$`),并配合不匹配的长字符串数据。此时单次 `test()` 即可导致 CPU 
呈指数级消耗:
+
+- **客户端**:渲染该图表的浏览器标签页会卡死,需强制关闭。
+- **服务端**:若使用 ECharts 的服务端渲染(SSR),单次恶意请求会阻塞 Node.js 事件循环,影响所有并发用户。
+- **持久化场景**:若恶意配置被存入数据库,每次有用户打开该看板或报表都会触发 DoS。
+
+此类问题不涉及数据泄露或篡改,主要影响可用性。
+
+### 建议
+
+- **若 `dataset.transform` 的 `config`(含 `reg`)完全来自可信任来源**(如应用自身或受控后台),则无需额外措施。
+- **若 `config` 可能来自不受信任来源**(如用户输入、外部 API、未校验的数据库内容):
+  - 在传入 ECharts 前,对 `reg` 
字符串做**长度上限**与**复杂度校验**(例如拒绝包含嵌套量词等已知易引发灾难性回溯的写法),或仅允许白名单内的简单模式。
+  - 或考虑使用具备线性时间保证的正则引擎(如服务端使用 RE2)或为匹配过程增加**超时**(如将耗时逻辑放入 Web Worker 并设上限),以降低 
ReDoS 风险。
+  - 若业务上不需要由不可信方指定正则,可禁止从外部配置中传入 `reg`,仅允许写死在代码中的安全模式。
+
+
 ## 传入 JS 函数时的安全考虑 [[[#passing_js_function_safely]]]
 
 ECharts 的 `option`(即 [chart.setOption()](${apiPath}echartsInstance.setOption) 
的输入)主要是声明式的,但部分 option 可接受 JS 函数(回调)输入以增强表达能力和灵活性,如 
[label.formatter](${optionPath}series-scatter.label.formatter)、[axisTick.interval](${optionPath}xAxis.axisTick.interval)
 等。在大多数情况下,这些函数是 app 源代码的一部分于是完全可信任,因此不会引入风险。


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to