鸿蒙 PC Markdown 编辑器内部隐私与安全评审

仓库地址:https://gitcode.com/VON-/codex_md_oh

代码基线:G3-09 设备收口 fc7de5a,G3-10 内部安全评审 941a1dc

Markdown 编辑器为什么是高敏感本地应用

Markdown 文件往往包含设计文档、会议记录、源码片段、账号说明、未发布文章和本地图片。编辑器没有云服务,不代表安全问题自动消失:恶意 Markdown 可以尝试脚本执行,图片和链接可以诱导越权文件读取,拖放 URI 可能指向工作区外,导出可能把危险活动内容分发出去,分享缓存可能留下敏感副本。

OhMarkdown 的安全目标不是“绝对没有风险”,而是建立清晰的信任边界:用户文档默认不外发;ArkWeb 不直接访问任意文件;不可信渲染结果必须净化;文件和资源操作不越过系统授权;写入失败保留缓冲与旧版本;系统分享只在明确点击时发生;日志不记录正文和敏感路径。

G3-10 本轮完成单轮内部评审。它覆盖代码、配置、测试和 npm 已知漏洞,但不是独立第三方渗透测试,也不声称穷尽每条攻击路径。报告将“内部基线通过”和“最终 Beta 安全退出”分开,后者仍需要真机 PDF/分享权限复核、分享缓存策略和独立人员审查。

资产、入口和受信组件

主要资产包括:CodeMirror 当前正文、磁盘 Markdown、文件格式元数据、恢复记录、保存前备份、工作区根授权、图片资源、搜索结果、导出产物和分享缓存。入口包括 Markdown/HTML、公式、Mermaid、代码围栏、文件名、链接、拖放 URI、剪贴板图片、Bridge JSON 和系统选择器 URI。

受信组件并不是“所有原生代码”。ArkUI 工作台有调用文件和系统 Ability 的权力,必须对来自 Web 的 payload 二次校验;ArkWeb 只负责编辑和渲染,不应因为页面来自 HAP 就获得任意文件权。Core File Kit 的授权 URI 是能力边界,应用仍要限制类型、大小、目录和相对路径。

系统打印、分享和选择器是外部能力。应用可以准备净化文档、申请最小权限和处理失败,却不能假设每台模拟器都有 PDF 服务或 Markdown 接收方。缺失系统目标应成为可恢复错误,不应降级到网络上传。

权限最小化

最终模块只声明两个权限:打印和前台剪贴板读取。

"requestPermissions": [
  {
    "name": "ohos.permission.PRINT",
    "reason": "$string:print_permission_reason",
    "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
  },
  {
    "name": "ohos.permission.READ_PASTEBOARD",
    "reason": "$string:pasteboard_permission_reason",
    "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
  }
]

项目没有 INTERNET、定位、相机、麦克风或通讯录权限。READ_PASTEBOARD 是 G3-09 中文剪切粘贴设备故障后补齐,使用场景限定前台 EntryAbility;应用不轮询剪贴板历史,不在后台分析内容。中英文用途说明明确“粘贴文本和文件”,不使用模糊的“正常运行所需”。

最小权限还意味着功能缺席时不偷偷绕过。模拟器没有分享目标时,应用显示 No matching ability,而不是把文件上传到服务器;没有 PDF 服务时停在系统打印能力矩阵,不引入远程转换。

网络边界由权限和 CSP 双重建立

没有 INTERNET 权限是原生层边界,connect-src 'none' 是 Web 内容边界。编辑器源页面 CSP 如下:

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

生产包是单 HTML,脚本、样式、KaTeX 字体、Mermaid 和 CodeMirror 全部进入 HAP;自动化断言没有外部 script/link 子资源。unsafe-inline 是单文件打包的当前成本,但脚本来源仍是受控 HAP,Markdown 内容先经过解析和 DOMPurify,不能变成新 script 节点。

图片允许 blob、resource 和 file scheme,是本地预览表示需要;ArkWeb 本身仍设置 fileAccess(false),本地资源必须由 AssetService 读取后分块交付。CSP 不是文件授权,二者共同限制才有意义。

ArkWeb 不是系统文件代理

工作台创建 Web 组件时显式关闭文件、DOM 存储和定位:

Web({ src: $rawfile('editor/index.html'), controller: this.editorController })
  .javaScriptAccess(true)
  .domStorageAccess(false)
  .imageAccess(true)
  .fileAccess(false)
  .geolocationAccess(false)
  .javaScriptProxy({
    object: this.editorBridge,
    name: 'ohMarkdownBridge',
    methodList: ['onReady', 'onState', 'onChange', 'onSnapshot',
      'onAssetImport', 'onAssetRead', 'onCommand']
  })

JavaScript 必须开启,因为 CodeMirror 和渲染管线运行在 ArkWeb;安全策略不是关掉 JavaScript,而是固定页面来源、固定 Proxy、限制文件与网络、净化不可信内容。七个 Bridge 方法都是事件或受限请求,没有 executereadFile(path) 或通用对象反射。

图片与链接 payload 分别有 14 MiB 和 8 MiB 总上限。原生层再次验证 requestId、sessionId、MIME、Base64、长度、路径段和当前文档会话。Web 页面即使构造伪造 JSON,也不能直接选择任意目标路径。

原生向 Web 动态传参的注入检查

WorkspaceShell 多处使用 runJavaScript,这是混合架构最需要审查的表面。内部评审逐项检查文件名、正文、相对路径、错误信息、搜索词、替换词和快捷键 JSON,全部使用 JSON.stringify 形成合法字符串字面量;偏移与尺寸经过数值解析和范围校验。

例如图片插入:

await this.editorController.runJavaScript(
  `window.OhMarkdownEditor?.insertNativeDroppedAsset(` +
  `${JSON.stringify(asset.relativePath)}, ` +
  `${JSON.stringify(asset.storedName)}, null, null)`
);

文件名即使包含引号、换行或 );alert(1);//,也只成为字符串内容。请求命令由固定分支和白名单处理,不将菜单文案或用户查询反向解析为代码。

长期风险是 WorkspaceShell 很大,新路径容易忘记编码。评审将超大组件记为 P2 可维护性风险,G4 应按真实领域拆分 Bridge 调用封装,但 Beta 退出前不做无行为收益的大规模重构。

普通 Markdown 的第一层净化

markdown-it 把 Markdown 转为 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']
  });
}

原始 HTML 默认不作为可信能力。脚本、事件属性和危险 URL 由 DOMPurify 处理,style、iframe、object、embed 和 form 进一步禁止。预览外部链接点击统一 preventDefault,只把 #anchor 或无 scheme 的相对目标交给 LinkService;HTTP 链接当前不会从预览直接打开。

恶意测试包含 script、标题闭合、javascript URL、远程图片、危险属性和 HTML 表单。预览 script 节点必须为零,页面标志不能被执行。安全测试和正常 GFM、公式、图片共同跑,避免修安全时把普通渲染完全破坏。

KaTeX、Mermaid 和代码的第二层净化

专业渲染器不信任库输出。KaTeX 使用 trust:falsethrowOnError:true、maxExpand 1000 和 maxSize 20,输出再以 HTML/MathML/SVG profile 净化。Highlight.js 只处理已知语言,结果只允许 span 与 class。

Mermaid 面更复杂:strict securityLevel、htmlLabels false、最大 24 图、每图 50,000 字符、最多 500 边;禁止 %%{...}%% 配置指令;render 结果再次禁止 script、foreignObject、iframe、object、embed、a、href 和 xlink:href。这样用户图表不能覆盖应用安全配置,也不能用 SVG 链接建立第二条导航通道。

单个公式或图表错误被替换为可点击的错误按钮,只影响当前节点。局部错误“settled”后导出仍可继续,不会因为攻击者提供复杂语法把整个输出锁死 30 秒。

本地图片与路径约束

AssetService 只接受 PNG、JPEG、GIF 和 WebP,单项最大 10 MiB。文件名去除控制字符和桌面保留字符,目标只能进入 assets 或“文档名.assets”。读取请求必须正好两段相对路径,不接受 ..、反斜线、查询串或片段。

Reference 模式并非“任意路径引用”。源必须位于已授权父目录下,且第一段是受管资源目录;工作区外图片在设备中明确拒绝。Copy/Move 先写临时文件、确认字节和 fsync,再替换目标;Move 只有落盘成功后才删除源;任何失败都不插入 Markdown 链接。

Web 本地预览只接收原生验证后的 MIME、长度和 Base64,分块协议绑定 requestId、sessionId 与 relativePath。切换标签后迟到的图片不会进入错误会话。

工作区搜索和链接不会越界

SearchService 上限为 5000 文档、2000 目录、单目录 2000 条、单文档 4 MiB和 500 结果,使用 lstat 跳过符号链接。单篇匹配在 TaskPool 执行,取消通过代际和 taskpool.cancel 双重实现。本轮 1000 文件模拟器压力发现/扫描 1000、跳过 0。

LinkService 拒绝绝对路径、外部 scheme、反斜线、查询参数、NUL 和工作区外归一化结果;读取文档使用 NOFOLLOW。中文路径逐段编码,标题锚点来自离线文档。预览不直接导航 URL,而是把 href 和当前正文交给原生解析,目标通过后才打开授权内 Markdown。

“本地优先”不意味着用户写的 ../../secret.md 应被信任。应用必须把工作区根作为显式边界,错误要解释“outside workspace”而不是默默跳转。

保存与恢复安全

DocumentService 读取上限 20 MiB,按 64 KiB 解码 UTF-8,要求最终字节数与读取前 stat 一致;无效 UTF-8 和读取期间变化会失败。保存比较实际写入字节与预期 UTF-8 字节,随后 truncate 和 fsync。

用户 URI 不一定支持同目录 rename,所以应用先把旧正文和格式写入沙箱 PendingSaveBackup。RecoveryService 使用 .new.bak、fsync 与 rename 维护恢复记录;启动时修复中断事务。保存失败不会把编辑缓冲标成已保存,也不会清掉备份。

ohosTest 覆盖 BOM/CRLF 字节一致、混合换行、目标不可用时旧版本恢复和写入后指纹变化。安全评审将数据完整性与脚本安全视为同等级底线,因为对编辑器而言,静默覆盖就是最高影响问题之一。

导出需要重新净化

导出不是直接复用 Markdown 字符串,也不是盲取 preview.innerHTML。应用等待 KaTeX、Mermaid、Highlight 和本地图片最终代际;克隆预览;把受管图片转 Data URL;移除内部 data 属性;再次 DOMPurify;用静态 HTML 外壳和严格 CSP 封装。

导出 CSP 使用 script-src 'none'connect-src 'none'img-src data:font-src data:。HTML 离开应用后仍不依赖脚本执行或 CDN。标题单独 HTML 转义,文件名单独清理,不能把两个边界混为一谈。

PNG 捕获校验宽高和 16000 px 系统上限,PixelMap 与 ImagePacker 在 finally 释放;错误恢复原视图。PDF 交给系统打印适配器,应用不引入远程转换。模拟器打印预览已通过但没有 PDF 服务,所以文章不把预览写成文件成功。

分享权限与缓存残留

分享从当前 CodeMirror 缓冲生成 cacheDir Markdown,不直接分享可能落后的磁盘 URI。Want 使用 text/markdown、stream URI 和 FLAG_AUTH_READ_URI_PERMISSION,不给写权限,也不指定固定第三方 bundle。系统无接收方时返回失败,正文和脏标记保持。

当前发现一个 P2:不同文件名会形成多个 share-*.md,依赖系统缓存清理或同名覆盖。文件仍在应用沙箱,只有用户明确分享的单 URI 获得临时读权,所以不是已验证的跨应用泄露;但最小留存原则要求 G4 前增加有界过期清理。

不能在 startAbility 返回后立即删除,因为目标可能稍后读取。正确方案要在真机测量分享目标生命周期,再选择例如下一次启动清理超过 24 小时的受控前缀文件。为了追求“立即无残留”而让分享偶发失败,同样不可靠。

日志与隐私

生产日志记录读取字节、字符数、耗时和 UDMF record type,不记录正文、文件名或完整路径。Ability 生命周期和窗口加载错误不包含用户文档。项目没有遥测 SDK,也没有网络发送点。

性能测试使用合成“唯一压力命中”和 report 序号。内部试用和封闭测试的数据字典规定不收集正文、文件名、路径、链接或图片。以后若增加崩溃附件,必须让用户查看、关闭和清除,不能因为“本地工具需要排错”默认上传。

npm 已知漏洞审计

本轮分别执行生产依赖和全部依赖审计。运行时 143 个依赖、总计 221 个依赖,info、low、moderate、high、critical 均为 0。检查以 2026-07-21 当前 lockfile 与 npm 官方审计接口为准。

npm audit 的零不是永久证明。它不覆盖未披露漏洞、恶意包维护者或 HarmonyOS SDK;版本升级也会改变结论。DOMPurify、Mermaid、KaTeX、CodeMirror 和 markdown-it 每次升级都必须重跑恶意语料、CSP、离线单 HTML 和导出净化。

供应链的另一个边界是运行时不从 npm/CDN加载。所有依赖在构建期进入单 HTML,应用安装后不执行包管理器,也不动态下载插件。这大幅缩小运行时依赖变更面。

应用内部安全证据

下图是最终 HAP 的设置与导出面板。它展示 HTML、PDF、PNG、Markdown 分享、自动保存和图片资源规则都位于同一原生工作台;这些入口分别受到文件选择器、打印、ImagePacker、只读分享 URI 和受管资源目录约束。

在这里插入图片描述

截图证明应用入口和本地化,不单独证明脚本零执行或路径不越界。安全结论来自代码边界、恶意自动化、ohosTest、设备失败路径和 npm 审计共同组成。系统打印和分享图也保留在测试报告中,明确记录环境能力缺席。

评审发现与严重度

本轮未发现 P0/P1。P2 一是分享缓存过期清理;P2 二是 WorkspaceShell 与 Web main 文件较大,安全规则分布广,未来改动遗漏 JSON 编码或状态清理的概率上升。第二项是可维护性风险,不是已验证漏洞。

还有环境项:PDF 文件、成功分享目标和独立 HTML/PNG查看未闭环;真机系统权限策略未复验。这些不把现有代码判成漏洞,但阻止导出能力与本地隐私记分升到 4。

若后续出现正文外发、路径越权、危险脚本执行、保存覆盖或恢复丢失,必须立即升级 P0/P1,停止扩展并撤销当前基线通过结论。

内部通过不等于最终发布通过

内部评审完成了 G3-10 一个独立小阶段,可以继续竞品和真机工作。但 G3 Beta 退出仍要求 50-100 人封闭测试、核心任务领先目标、崩溃恢复指标和完整系统导出。安全方面建议在对外 Beta 前由独立人员复核高影响路径,而不是只依赖实现者自审。

真机重点包括:剪贴板权限撤销、分享目标读取期限、PDF 服务、DocumentSave 用户 URI、只读文件、空间不足、系统字体、物理触控板和多输入法。每项保留 HAP 哈希与原始结果。

对产品优势的意义

OhMarkdown 想形成的优势不是“功能最多”,而是文件可靠、本地优先和鸿蒙 PC 原生任务连续。安全边界让这些优势可成立:图片自动管理但不越权;链接补全但不离开工作区;Mermaid 可渲染但不执行活动内容;分享当前正文但不暴露原目录;导出复杂内容但不携带脚本。

当前内容安全和离线完整保持 3 分,有模拟器与自动化证据;没有真机和独立竞品任务前不能升 4。宣传也不能使用“绝对安全”或“全面领先”。更合适的表达是“默认离线、不申请网络权限、受限 Bridge、危险内容净化”,并提供可追溯报告。

结论

鸿蒙 PC Markdown 编辑器的内部安全评审必须同时覆盖权限、网络、ArkWeb、Bridge、渲染、文件、恢复、导出、分享、日志和依赖。OhMarkdown 当前没有 INTERNET,编辑器 CSP 禁止连接,ArkWeb 禁止任意文件访问,Proxy 只有七项白名单,动态参数 JSON 编码,不可信 HTML/MathML/SVG 多层净化,路径与文件大小有界,保存失败保留恢复事实。

两轮 npm 审计为零已知漏洞,Playwright 44/44、ohosTest 10/10 和设备失败路径共同通过。本轮内部基线未发现 P0/P1,同时公开记录分享缓存与超大组件两个 P2。安全结论因此既不是“什么都没问题”,也不是“环境不全所以无法前进”,而是一份明确说明已经证明什么、还必须证明什么的 Beta 工程事实。

Logo

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

更多推荐