diff --git a/README.md b/README.md index 18040d3b..675f75c4 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,36 @@ For example, {{ use: partial-version(version = ${version|minVersion('6.0.0')}) }} That is, if the ${version} is empty or smaller than '6.0.0', use '6.0.0'. Follow the version comparison rules in Semver 2.0 . + +{{ use: partial-version( + feature = '`dataIndex` is available', + version = '5.3.0' +) }} + +{{ use: partial-version( + deprecated = 'Use xxx instead', + version = '5.1.0' +) }} ``` +### Security Warning +Security waning is required for unsafe APIs/options. +For example, +``` +{{ use: partial-security-warning }} +Or +{{ use: partial-security-warning( + desc: 'Raw HTML is allowed in this option.' +)}} +Or +{{ use: partial-security-warning( + desc: 'Raw HTML is allowed in this option.', + securityRiskExclamation: '**Security risks** must be considered when xxx.' +)}} +``` + +See ["Security"](https://echarts.apache.org/handbook/en/best-practices/security) for more details. + ### Global Variables These global variables can be used in doc: diff --git a/en/api/version.md b/en/api/version.md index 09dee9a6..e3c2d8c7 100644 --- a/en/api/version.md +++ b/en/api/version.md @@ -3,6 +3,8 @@
{{ if: ${deprecated} }} Deprecated since `v${version}`. ${deprecated} +{{ elif: ${feature} }} +${feature} since `v${version}` {{ else }} Since `v${version}` {{ /if }} diff --git a/en/option-gl/partial/version.md b/en/option-gl/partial/version.md index 825ccf4a..dd7e8b4c 100644 --- a/en/option-gl/partial/version.md +++ b/en/option-gl/partial/version.md @@ -4,6 +4,8 @@
{{ if: ${deprecated} }} Deprecated since{{ if: ${isECharts} }} ECharts{{ /if }} `v${version}`. ${deprecated} +{{ elif: ${feature} }} +${feature} since{{ if: ${isECharts} }} ECharts{{ /if }} `v${version}` {{ else }} Since{{ if: ${isECharts} }} ECharts{{ /if }} `v${version}` {{ /if }} diff --git a/en/option/component/title.md b/en/option/component/title.md index f448dacb..ff23218e 100644 --- a/en/option/component/title.md +++ b/en/option/component/title.md @@ -26,10 +26,16 @@ Set this to `false` to prevent the title from showing The main title text, supporting for `\n` for newlines. +{{ use: partial-security-warning( + securityRiskExclamation: 'When enabling [toolbox.feature.saveAsImage](~toolbox.feature.saveAsImage), and [toolbox.feature.saveAsImage.name](~toolbox.feature.saveAsImage.name) is not provided, it has historically been using `title[0].text` instead. This usage is not recommended -- [toolbox.feature.saveAsImage.name](~toolbox.feature.saveAsImage.name) should always be specified explicitly; otherwise, **correctness** and **security risks** for a filename have to be considered in this `title.text` option.' +)}} + ## link(string) = '' The hyper link of main title text. +{{ use: partial-security-url-common-warning }} + ## target(string) = 'blank' Open the hyper link of main title in specified tab. @@ -63,6 +69,8 @@ Subtitle text, supporting for `\n` for newlines. The hyper link of subtitle text. +{{ use: partial-security-url-common-warning }} + ## subtarget(string) = 'blank' Open the hyper link of subtitle in specified tab, options: diff --git a/en/option/component/toolbox.md b/en/option/component/toolbox.md index caf1b23e..6cd510f7 100644 --- a/en/option/component/toolbox.md +++ b/en/option/component/toolbox.md @@ -63,6 +63,11 @@ Padding of text area. Whether to show the tool. #### title(string) = '${title}' +{{ if: ${warnToolboxDataViewSecurity} }} +{{ use: partial-security-warning( + desc: '`dataView` panel is implemented using HTML, allowing users to customize certain parts for styling and formatting. The `title` is embedded in the HTML without HTML-escaping, so it should be properly escaped before being passed in.' +)}} +{{ /if }} #### icon(string) @@ -169,11 +174,20 @@ File suffix of the image saved. + If the `renderer` is set to be `'canvas'` when chart [initialized](api.html#echarts.init) (default), then `'png'` (default) and `'jpg'` are supported. + If the `renderer` is set to be `'svg'` when when chart [initialized](api.html#echarts.init), then only `'svg'` is supported for `type` (`'svg'` type is supported since `v4.8.0`). +{{ use: partial-security-warning( + securityRiskExclamation: 'This value will be used as the file extension. However, it has not historically been validated internally. So do not input an invalid value; otherwise, **security risks** have to be considered.' +)}} + #### name(string) -Name to save the image, whose default value is [title.text](~title.text). +Name (file stem) to save the image. If it is not provided, [title[0].text](~title.text) (if any) has historically been used. The full download filename is `{name}.{type}` + +{{ use: partial-security-warning( + securityRiskExclamation: 'It is recommended to always specify this `name` explicitly, and do not use text from untrusted sources. Otherwise, **correctness** and **security risks** for a filename have to be considered.' +)}} + #### backgroundColor(Color) = 'auto' @@ -214,7 +228,8 @@ Restore configuration item. Data view tool, which could display data in current chart and updates chart after being edited. {{ use: feature-common( - title = "data view" + title = "data view", + warnToolboxDataViewSecurity = true ) }} #### readOnly(boolean) = false @@ -231,6 +246,10 @@ Whether it is read-only. Define a function to present dataView. It is used to replace default textarea for richer data editing. It can return a DOM object, or an HTML string. +{{ use: partial-security-warning( + desc: '`dataView` panel is implemented using HTML, allowing users to customize certain parts for styling and formatting. The HTML provided by `optionToContent` is embedded in the panel HTML without HTML-escaping, so it should be properly escaped before being passed in.' +)}} + For example: ```ts optionToContent: function(opt) { @@ -265,6 +284,10 @@ When optionToContent is used, if you want to support refreshing chart after data There are 3 names in data view, which are `['data view', 'turn off' and 'refresh']`. +{{ use: partial-security-warning( + desc: '`dataView` panel is implemented using HTML, allowing users to customize certain parts for styling and formatting. The items in `lang` are embedded in the HTML without HTML-escaping, so it should be properly escaped before being passed in.' +)}} + #### backgroundColor(string) = '#fff' diff --git a/en/option/partial/security-warning.md b/en/option/partial/security-warning.md new file mode 100644 index 00000000..af413563 --- /dev/null +++ b/en/option/partial/security-warning.md @@ -0,0 +1,13 @@ +{{ target: partial-security-warning }} +
+[WARNING]: ${desc} {{ if: ${securityRiskExclamation} }}${securityRiskExclamation}{{ else }}**Security risks** must be considered when using it.{{ /if }} See document ["Security"](${handbookPath}best-practices/security) for recommendations on safe usage. +
+{{ /target }} + + +{{ target: partial-security-url-common-warning }} +{{ use: partial-security-warning( + desc: 'This URL string is accepted directly without any internal sanitization.', + securityRiskExclamation: '**Security risks** must be considered if it comes from untrusted sources.' +)}} +{{ /target }} diff --git a/en/option/partial/tooltip-common.md b/en/option/partial/tooltip-common.md index ee7b9214..3237b0c8 100644 --- a/en/option/partial/tooltip-common.md +++ b/en/option/partial/tooltip-common.md @@ -305,7 +305,33 @@ formatter: '{b0}: {c0}
{b1}: {c1}' The format of callback function: ```ts -(params: Object|Array, ticket: string, callback: (ticket: string, html: string)) => string | HTMLElement | HTMLElement[] +(params: Object|Array, ticket: string, callback: (ticket: string, html: string | HTMLElement | HTMLElement[])) => string | HTMLElement | HTMLElement[] +``` + +{{ use: partial-security-warning( + desc: '`tooltip` is implemented in HTML (unless [tooltip.renderMode](~tooltip.renderMode) is set as `richText`), allowing users to customize the HTML in this way. The content in the HTML must be properly escaped before being passed in.' +)}} + +**HTML-escaping must be enforced** before passing the HTML to ECharts. For example, +```js +{ + tooltip: { + formatter: params => { + const { name, value } = params; + // HTML-escaping must be performed. + // Otherwise, the rendering may be incorrect if `name` or + // `value` contain special charactors like '<', '>', etc. + // Additionally, unescaped strings may introduces XSS risks + // if `name` or `value` come from untrusted sources, where + // malicious code may be injected into that strings. + return echarts.format.encodeHTML(name) + + '' + echarts.format.encodeHTML(value + '') + ''; + // NOTE: `echarts.format.encodeHTML` is an utility that converts special + // characters ('&', '<', '>', '"', "'") to their corresponding HTML entities. + // This is just an example -- any HTML-escaping utility can be used. + } + } +} ``` The first parameter `params` is the data that the formatter needs. Its format is shown as follows: @@ -369,13 +395,18 @@ Interface: (value: number | string, dataIndex: number) => string ``` -> `dataIndex` is provided since `v5.5.0`. +{{ use: partial-version( + feature = '`dataIndex` is provided', + version = '5.5.0' +) }} Example: ```ts // Add $ prefix valueFormatter: (value) => '$' + value.toFixed(2) ``` + +> **[NOTE]:** Different from [tooltip.formater](~tooltip.formatter), raw HTML is NOT accepted in this approach -- the returned content will be escaped internally before rendering. {{ /if }} #${prefix} backgroundColor(Color) = 'rgba(50,50,50,0.7)' @@ -446,3 +477,7 @@ Extra CSS style for floating layer. The following is an example for adding shado extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);' ``` +{{ use: partial-security-warning( + desc: '`tooltip` is implemented in HTML (unless [tooltip.renderMode](~tooltip.renderMode) is set as `richText`), allowing users to customize the CSS text of the box in this way.', + securityRiskExclamation: '**Security risks** must be considered if the CSS text comes from untrusted sources.' +)}} diff --git a/en/option/partial/version.md b/en/option/partial/version.md index 60974bb5..04430156 100644 --- a/en/option/partial/version.md +++ b/en/option/partial/version.md @@ -4,6 +4,8 @@
{{ if: ${deprecated} }} Deprecated since `v${version}`. ${deprecated} +{{ elif: ${feature} }} +${feature} since `v${version}` {{ else }} Since `v${version}` {{ /if }} diff --git a/en/option/series/sunburst.md b/en/option/series/sunburst.md index 600e45ba..5e975b44 100644 --- a/en/option/series/sunburst.md +++ b/en/option/series/sunburst.md @@ -215,6 +215,8 @@ Link address that redirects to when this sector is clicked. Only useful when [se See [series-sunburst.data.target](~series-sunburst.data.target). +{{ use: partial-security-url-common-warning }} + ### target(string) = 'blank' Like `target` attribute of HTML ``, which can either be `'blank'` or `'self'`. See [series-sunburst.data.link](~series-sunburst.data.link). diff --git a/en/option/series/treemap.md b/en/option/series/treemap.md index 465ed63a..a5e9d8ed 100644 --- a/en/option/series/treemap.md +++ b/en/option/series/treemap.md @@ -391,6 +391,8 @@ Enable hyperlink jump when clicking on node. It is available when [series-treema See [series-treemap.data.target](~series-treemap.data.target). +{{ use: partial-security-url-common-warning }} + ### target(string) = 'blank' The same meaning as `target` in `html` `` label, See [series-treemap.data.link](~series-treemap.data.link). Option values are: `'blank'` or `'self'`. diff --git a/src/style/mixin.scss b/src/style/mixin.scss index 9dc0917f..7f68e23f 100644 --- a/src/style/mixin.scss +++ b/src/style/mixin.scss @@ -93,4 +93,22 @@ color: #0086b3; } } + + .doc-partial-security-warning { + font-size: 13px; + background: rgba(254, 226, 226, 1); + margin: 10px 20px 10px 0; + padding: 10px; + border-left: 2px solid rgba(248, 113, 113, 1); + color: rgb(100, 116, 139); + + code { + background: rgba(254, 202, 202, 1); + } + + .warning-title { + font-weight: bold; + color: rgba(185, 28, 28, 1); + } + } } diff --git a/zh/api/version.md b/zh/api/version.md index 36f0782a..c97a893d 100644 --- a/zh/api/version.md +++ b/zh/api/version.md @@ -3,6 +3,8 @@
{{ if: ${deprecated} }} 从 `v${version}` 开始不推荐使用(deprecated)。${deprecated} +{{ elif: ${feature} }} +${feature} 从 `v${version}` 开始支持 {{ else }} 从 `v${version}` 开始支持 {{ /if }} diff --git a/zh/option-gl/partial/version.md b/zh/option-gl/partial/version.md index cf9cb8a5..60d19e9b 100644 --- a/zh/option-gl/partial/version.md +++ b/zh/option-gl/partial/version.md @@ -4,6 +4,8 @@
{{ if: ${deprecated} }} 从{{ if: ${isECharts} }} ECharts{{ /if }} `v${version}` 开始不推荐使用(deprecated)。${deprecated} +{{ elif: ${feature} }} +${feature} 从{{ if: ${isECharts} }} ECharts{{ /if }} `v${version}` 开始支持 {{ else }} 从{{ if: ${isECharts} }} ECharts{{ /if }} `v${version}` 开始支持 {{ /if }} diff --git a/zh/option/component/title.md b/zh/option/component/title.md index 4520c13b..fe2c1490 100644 --- a/zh/option/component/title.md +++ b/zh/option/component/title.md @@ -43,10 +43,16 @@ const option = { 主标题文本,支持使用 `\n` 换行。 +{{ use: partial-security-warning( + securityRiskExclamation: '如果使用了 [toolbox.feature.saveAsImage](~toolbox.feature.saveAsImage),并且没有设置 [toolbox.feature.saveAsImage.name](~toolbox.feature.saveAsImage.name),会用 `title[0].text` 替代。这种用法是历史实现但并不推荐,应该总是显式指定[toolbox.feature.saveAsImage.name](~toolbox.feature.saveAsImage.name);否则,不得不考虑 `title.text` 是否是个正确的文件名,以及其 **安全性**。' +)}} + ## link(string) = '' 主标题文本超链接。 +{{ use: partial-security-url-common-warning }} + ## target(string) = 'blank' 指定窗口打开主标题超链接。 @@ -80,6 +86,8 @@ const option = { 副标题文本超链接。 +{{ use: partial-security-url-common-warning }} + ## subtarget(string) = 'blank' 指定窗口打开副标题超链接,可选: diff --git a/zh/option/component/toolbox.md b/zh/option/component/toolbox.md index b05af9f2..12cb25fb 100644 --- a/zh/option/component/toolbox.md +++ b/zh/option/component/toolbox.md @@ -63,6 +63,11 @@ ${name} icon 样式设置。由于 icon 的文本信息只在 icon hover 时候 是否显示该工具。 #### title(string) = '${title}' +{{ if: ${warnToolboxDataViewSecurity} }} +{{ use: partial-security-warning( + desc: '`dataView` 面板是用 HTML 实现的,一些部分允许用户自定义格式和样式。`title` 字段会直接拼接到此 HTML 中,并没有做转义。因此传入前须要正确转义。' +)}} +{{ /if }} #### icon(string) @@ -243,11 +248,19 @@ option = { + 如果 `renderer` 的类型在 [初始化图表](api.html#echarts.init) 时被设为 `'canvas'`(默认),则支持 `'png'`(默认)和 `'jpg'`; + 如果 `renderer` 的类型在 [初始化图表](api.html#echarts.init) 时被设为 `'svg'`,则 `type` 只支持 `'svg'`(`'svg'` 格式的图片从 `v4.8.0` 开始支持)。 +{{ use: partial-security-warning( + securityRiskExclamation: '`type` 会被用于文件的扩展名。但是实现中并没有在内部对其进行合法性校验(历史因素)。所以不要传入不合法的值;否则,需要考虑 **安全风险**。' +)}} + #### name(string) -保存的文件名称,默认使用 [title.text](~title.text) 作为名称。 +保存的文件名称的扩展名之前的部分。如果没有指定,会使用 [title[0].text](~title.text)(如果存在的话)。完整的文件名是 `{name}.{type}`。 + +{{ use: partial-security-warning( + securityRiskExclamation: '建议总是显式指定 `name`,以及不使用“不可信任来源”的字符串。否则,须要考虑作为文件名的 **正确性** 和 **安全性**。' +)}} #### backgroundColor(Color) = 'auto' @@ -288,7 +301,8 @@ option = { 数据视图工具,可以展现当前图表所用的数据,编辑后可以动态更新。 {{ use: feature-common( - title = "数据视图" + title = "数据视图", + warnToolboxDataViewSecurity = true ) }} #### readOnly(boolean) = false @@ -305,6 +319,10 @@ option = { 自定义 dataView 展现函数,用以取代默认的 textarea 使用更丰富的数据编辑。可以返回 dom 对象或者 html 字符串。 +{{ use: partial-security-warning( + desc: '`dataView` 面板是用 HTML 实现的,一些部分允许用户自定义格式和样式。`optionToContent` 给出的 HTML 会直接拼接到面板 HTML 中,并没有做转义。因此传入前须要正确转义。' +)}} + 如下示例使用表格展现数据值: ```ts optionToContent: function(opt) { @@ -339,6 +357,10 @@ optionToContent: function(opt) { 数据视图上有三个话术,默认是`['数据视图', '关闭', '刷新']`。 +{{ use: partial-security-warning( + desc: '`dataView` 面板是用 HTML 实现的,一些部分允许用户自定义格式和样式。`lang` 中的内容会直接拼接到此 HTML 中,并没有做转义。因此传入前须要正确转义。' +)}} + #### backgroundColor(string) = '#fff' diff --git a/zh/option/partial/security-warning.md b/zh/option/partial/security-warning.md new file mode 100644 index 00000000..d1b85049 --- /dev/null +++ b/zh/option/partial/security-warning.md @@ -0,0 +1,12 @@ +{{ target: partial-security-warning }} +
+[警告]: ${desc} {{ if: ${securityRiskExclamation} }}${securityRiskExclamation}{{ else }}使用时必须考虑 **安全风险**。{{ /if }}文档 [“安全性”](${handbookPath}best-practices/security) 给出了安全使用建议。 +
+{{ /target }} + +{{ target: partial-security-url-common-warning }} +{{ use: partial-security-warning( + desc: '此 URL 字符串直接被使用,并未在内部做其他净化处理(sanitization)', + securityRiskExclamation: '如果他们来自于“不受信任”的来源,必须考虑 **安全风险**。' +)}} +{{ /target }} diff --git a/zh/option/partial/tooltip-common.md b/zh/option/partial/tooltip-common.md index d4fbb02e..0f63ed22 100644 --- a/zh/option/partial/tooltip-common.md +++ b/zh/option/partial/tooltip-common.md @@ -311,11 +311,36 @@ formatter: '{b0}: {c0}
{b1}: {c1}' 回调函数格式: ```ts -(params: Object|Array, ticket: string, callback: (ticket: string, html: string)) => string | HTMLElement | HTMLElement[] +(params: Object|Array, ticket: string, callback: (ticket: string, html: string | HTMLElement | HTMLElement[])) => string | HTMLElement | HTMLElement[] ``` 支持返回 HTML 字符串或者创建的 DOM 实例。 +{{ use: partial-security-warning( + desc: 'tooltip 是用 HTML 实现的(除非 [tooltip.renderMode](~tooltip.renderMode) 设为 `richText`)。允许用此方式定制 HTML。传入 HTML 前须要对其内容进行正确转义。' +)}} + +组装 HTML 字符串时,**必须进行 HTML 转义(HTML-escaping)**。例如: +```js +{ + tooltip: { + formatter: params => { + const { name, value } = params; + // 必须进行 HTML 转义。 + // 否则,如果 name 或 value 中含有功能性字符,如 '<' '>' 等, + // 则可能渲染不正确。 + // 同时,如果 name 或 value 的值来自于“非受信任”的来源,则可能被注入恶意代码; + // 如果未被转义,则会被运行。 + return echarts.format.encodeHTML(name) + + '' + echarts.format.encodeHTML(value + '') + ''; + // 注:`echarts.format.encodeHTML` 是个工具函数,把特殊字符 + // ('&'、'<'、'>'、'"'、"'")转换成他们对应的 HTML entities. + // 这只是个例子,任何 HTML 转义工具函数都可使用。 + } + } +} +``` + 第一个参数 `params` 是 formatter 需要的数据集。格式如下: {{ use: partial-formatter-params-structure( @@ -375,7 +400,10 @@ tooltip 中数值显示部分的格式化回调函数。 (value: number | string, dataIndex: number) => string ``` -> 自 `v5.5.0` 版本起提供 `dataIndex`。 +{{ use: partial-version( + feature = '`dataIndex` 参数', + version = '5.3.0' +) }} 示例: @@ -383,6 +411,7 @@ tooltip 中数值显示部分的格式化回调函数。 // 添加 $ 前缀 valueFormatter: (value) => '$' + value.toFixed(2) ``` +> **[注]:** 不同于 [tooltip.formater](~tooltip.formatter),本方式不支持返回原始 HTML。返回内容渲染前会被自动按需转义。 {{ /if }} #${prefix} backgroundColor(Color) = 'rgba(50,50,50,0.7)' @@ -453,3 +482,7 @@ valueFormatter: (value) => '$' + value.toFixed(2) extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);' ``` +{{ use: partial-security-warning( + desc: 'tooltip 是用 HTML 实现的(除非 [tooltip.renderMode](~tooltip.renderMode) 设为 `richText`)。允许用此方式定制 toolbox 外壳的 CSS text。', + securityRiskExclamation: '如果此 CSS text 来自于“不受信任”的来源,必须考虑 **安全风险**。' +)}} diff --git a/zh/option/partial/version.md b/zh/option/partial/version.md index fbcd0fe5..ddd5265a 100644 --- a/zh/option/partial/version.md +++ b/zh/option/partial/version.md @@ -4,6 +4,8 @@