在这里插入图片描述

鸿蒙 PC Markdown 编辑器安全边界:本地优先、CSP 与受限能力

Markdown是文本,但预览会变成 HTML,链接和图片会产生资源请求,ArkWeb还通过 Bridge连接原生文件能力。若默认允许脚本、在线资源、任意文件和宽泛代理,本地编辑器就可能被一份恶意文档扩展成代码执行与数据访问入口。

本文基于 OhMarkdown,分析离线单文件、markdown-it禁用 HTML、DOMPurify、导出 CSP、链接阻断、ArkWeb权限收缩和 Bridge白名单。代码位于 https://gitcode.com/VON-/codex_md_oh,对应提交 3a9146e

安全模型从不信任文档开始

用户可能打开网络下载、邮件附件、仓库 clone或他人共享的 Markdown。文件内容不可信,即使应用不联网,HTML事件、危险协议和 Bridge调用仍可能产生影响。

安全目标:源码可以显示;常用 Markdown可预览;原生 HTML默认不执行;外部资源不自动加载;链接不在 ArkWeb直接导航;Web只调用有限原生事件;导出文件带独立 CSP。

本地优先不是“没有服务器所以安全”,而是把所有能力默认关闭后按工作流开放。

Web资源完全离线

Vite用 singlefile把 JS/CSS内联到 rawfile index.html。生产测试断言没有 <script src>和外部 stylesheet。应用启动不访问 CDN,依赖供应链在构建时固定,不在运行时远程变化。

base: './'避免绝对网络路径。sourcemap关闭。离线完整既提高可用性,也减少远程脚本被替换和网络元数据泄露。

markdown-it 第一层关闭 HTML

const markdownRenderer = new MarkdownIt({
  html: false,
  linkify: true,
  typographer: false,
  breaks: false
});

html: false<script>iframe、带事件属性的标签不成为活动 DOM。Markdown结构仍生成受控 HTML。不能为了兼容“Markdown内嵌 HTML”直接打开,再假设浏览器会安全处理。

如果未来支持 HTML子集,应明确标签/属性白名单并单独开关,默认仍关闭。产品“专业”不等于默认执行文档内任意代码。

DOMPurify 是最终输出边界

function sanitizeMarkdown(content: string): string {
  const unsafeHtml = markdownRenderer.render(content);
  return DOMPurify.sanitize(unsafeHtml, {
    USE_PROFILES: { html: true },
    FORBID_TAGS: [
      'style',
      'iframe',
      'object',
      'embed',
      'form'
    ],
    FORBID_ATTR: ['style']
  });
}

净化发生在所有 Markdown插件生成 HTML之后。先净化源码再渲染会让插件输出绕过。禁止 style防止文档重排或隐藏应用 UI,禁止嵌入与表单缩小导航、插件和提交能力。

DOMPurify还处理事件属性和危险协议。每次升级依赖或增加插件,都要重新运行安全语料,因为输出标签集合可能变化。

允许功能也要验证

安全语料包含危险 script、javascript链接、iframe,同时包含允许相对图片。测试断言危险不存在、标题/加粗/安全图片保留。把所有 HTML删除虽然“安全”,却不是可用 Markdown预览。

任务列表插件生成 disabled input,表格生成结构,链接生成 href。净化配置必须允许必要元素,但不开放交互写回和外部导航。

链接不在 ArkWeb直接打开

preview.querySelectorAll<HTMLAnchorElement>('a')
  .forEach((link) => {
    link.target = '_blank';
    link.rel = 'noopener noreferrer';
    link.addEventListener(
      'click',
      (event) => event.preventDefault()
    );
  });

链接保持视觉与导出语义,应用内点击被阻止。noopener noreferrer为未来外部打开保留隔离。当前没有让文档把 ArkWeb导航到站点或新窗口。

后续由原生接管时,必须解析 URL scheme,只允许 http/https并显示确认;file、javascript、data和自定义协议默认拒绝。相对链接需要工作区解析,不能交浏览器自由访问文件。

ArkWeb 权限最小化

Web({
  src: $rawfile('editor/index.html'),
  controller: this.editorController
})
.javaScriptAccess(true)
.domStorageAccess(false)
.onlineImageAccess(false)
.fileAccess(false)
.geolocationAccess(false)
.zoomAccess(false)

JavaScript是 CodeMirror所需,其他不需要能力关闭。在线图片禁用防止打开文档自动向远端服务器发送 IP、时间和 URL。fileAccess关闭防止 Web页面直接扫描本地,文件统一由 Core File Kit授权后在原生处理。

DOM storage关闭,编辑正文不写浏览器 localStorage;恢复使用应用沙箱受控记录。geolocation与文档编辑无关。

Bridge 只暴露事件白名单

methodList: [
  'onReady',
  'onState',
  'onChange',
  'onSnapshot',
  'onCommand'
]

Web不能传方法名让原生动态执行,也不能直接获得文件读写对象。onCommand只接受 new/open/save/find,原生 switch分发。onSnapshot限制五兆和整数 revision。

原生到 Web调用参数全部 JSON.stringify,防止正文或查询闭合脚本字符串。字符串返回按 JSON解码。协议边界同时解决正确性与注入。

导出 HTML 带独立 CSP

<meta http-equiv="Content-Security-Policy"
  content="default-src 'none';
  style-src 'unsafe-inline';
  img-src data: file:;
  font-src data:;
  base-uri 'none';
  form-action 'none'">

导出文件可能离开应用在浏览器打开,不能依赖 ArkWeb设置。default-src 'none'默认拒绝,允许内嵌样式、data/file图片和 data字体;禁止 base改变相对 URL和 form提交。

导出 title先 HTML转义,正文复用 sanitizeMarkdown。测试输入 </title><script>,断言没有脚本;危险链接不出现在 href/src。

style-src 'unsafe-inline'用于内嵌导出 CSS,是明确例外。没有 script-src允许,文档不运行脚本。

打印复用净化预览

打印先 preparePrint,确保预览由同一净化链生成,再创建 Web打印适配器。打印媒体隐藏编辑器,只显示预览。不会把原始 Markdown HTML直接交给打印系统。

大文档模式拒绝打印,避免构建巨大 DOM造成资源耗尽。安全也包括可用性攻击边界。

内容安全测试

Playwright输入 script并设置全局副作用:

const source =
  '# 标题\n\n' +
  '<script>' +
  'window.unsafeScriptExecuted=true' +
  '</script>';

断言预览 script数量0且 unsafeScriptExecuted未定义。仅检查节点不存在不够,脚本可能先执行后删除;副作用证明没有执行。

导出测试断言没有 script、javascript URI和外部 stylesheet,同时保留 h1、strong和相对图片。

安全预览界面

下图是鸿蒙 PC应用内的源码与预览工作面。预览来自 markdown-it和 DOMPurify,不是任意 HTML浏览器页面。

在这里插入图片描述

截图不能证明脚本未执行,自动化副作用断言与 ArkWeb权限检查才是证据。应用图满足文章的真实运行画面要求。

恢复记录与隐私

未保存正文写入应用沙箱,未加密,依赖 HarmonyOS应用隔离。日志不记录正文和 URI,内部试用不收文档内容。卸载应用会删除沙箱恢复。

高敏感场景未来可提供关闭恢复、系统密钥加密和安全删除策略,但必须评估密钥丢失与性能。默认恢复与隐私之间需要产品明确说明。

依赖与供应链

markdown-it-task-lists固定版本,package-lock固定实际树,CI使用 npm ci和 --ignore-scripts。依赖升级单独评审渲染输出和安全测试。

长期需要 SBOM、许可证和漏洞扫描。不能仅因为运行时离线就忽略构建依赖风险,恶意包可以在构建时进入单 HTML。

DoS 与规模边界

五兆以上关闭 Markdown预览、字数和恢复全文;文件读取上限二十兆;目录每层两千项;恢复 JSON有字节上限。规模限制防止恶意或异常文件耗尽内存。

正则表达式仍可能灾难性回溯,超大 Replace All也可能建立大量对象。这是尚未完全解决的可用性安全风险,应增加数量上限和安全正则策略。

当前边界

没有外部链接安全打开流程;相对图片解析有限;恢复记录未加密;未建立 SBOM与自动漏洞扫描;正则 DoS仍需处理;导出 file图片权限在不同浏览器需验证;插件体系尚未设计沙箱。

安全回归的发布红线

任何测试发现 script执行、javascript协议保留、ArkWeb加载外部 CDN、Bridge出现未审查方法、文件访问被放宽或恶意文档造成稳定崩溃,都应停止功能发布。修复必须补最小语料和自动断言,不能只改配置后人工看一遍。

安全报告区分源码预览、导出 HTML、打印和未来插件,因为它们运行环境不同。一个入口净化通过不代表另一入口自动安全。发布候选还要扫描最终 HAP,而不是只审查源码配置。

结语

OhMarkdown安全边界由多层组成:离线单文件、禁用原生 HTML、最终 DOMPurify、链接阻断、ArkWeb权限关闭、窄 Bridge、导出 CSP和规模限制。任何一层都不是万能,组合后把不可信 Markdown限制在阅读与编辑所需范围。

鸿蒙 PC编辑器的本地优先必须落实为默认不联网、不扫描文件、不执行文档代码,并且每次新增能力都明确打开了哪一扇门、由什么测试守住。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐