修复 PDF 预览被 Chromium 屏蔽(iframe sandbox 禁用内置阅读器) #14

现象:资源模块点开 PDF 附件,预览区只剩「此页面已被 Chromium 屏蔽」。

根因:preview.js 的 pdf 分支给 iframe 挂了 sandbox
(allow-same-origin allow-scripts,引入于 v24 8f80f3e)。
Chromium 只要看到 iframe 上有 sandbox 属性就禁用内置 PDF 阅读器插件,
表现即「被屏蔽」。五组同源对照实测(Chromium 153):
  · 无 sandbox                      → 正常渲染
  · allow-same-origin allow-scripts → 屏蔽
  · allow-scripts                   → 屏蔽
  · + allow-popups-to-escape-sandbox→ 仍然屏蔽(网上答案说能救,本版本不行)
  · + allow-forms allow-popups      → 仍然屏蔽
⇒ 加白名单无效,只能不加。

改动:删掉该 setAttribute,换成 17 行实测结论注释防止以后被"为安全"加回去。

安全性说明:PDF 由浏览器内置阅读器扩展在独立扩展进程渲染,不是普通页面
上下文,拿不到父页 window/DOM/cookie;PDF 是原生文档,没有能访问父页的
脚本环境。服务端另有 nosniff + MIME 内容嗅探(media.php),PDF 不会被当
HTML 执行。若将来要禁止 PDF 内嵌 JS,正确做法是换 pdf.js 自渲染。

验证:
  · 新增 _tooling/_pdf_e2e.py,11 条断言全通过 0 失败,已登记进流水线
    (挂「全路由」标签)。判据落在渲染层:frame 列表无 chrome-error、
    有内置阅读器扩展 frame、iframe 内文字不含「屏蔽」。
  · 反向验证:注入 sandbox 回去 → 4 条转红;恢复后 11/0。红绿闭环成立。
  · 全量流水线 2h21m 跑完,EXIT=0,已部署。
  · 线上 md5 与本地一致(988b804f),setAttribute('sandbox' 已不存在。
This commit is contained in:
2026-10-27 01:00:12 +08:00
parent a7fc7dcb0e
commit e826540d2a
+17 -2
View File
@@ -86,8 +86,23 @@
el = document.createElement('iframe'); el = document.createElement('iframe');
el.className = 'pv-pdf'; el.className = 'pv-pdf';
el.src = src; el.src = src;
// PDF 是"另一份文档",别让它拿到本页的 window // ⚠️ 这里**绝对不能**加 sandbox 属性。加上去 PDF 会直接打不开:
el.setAttribute('sandbox', 'allow-same-origin allow-scripts'); // 页面只剩一行「此页面已被 Chromium 屏蔽」,阅读器根本不起。
//
// 实测(Chromium 153,五组同源对照):
// · 无 sandbox → 正常渲染(多挂一个内置阅读器扩展 frame)
// · allow-same-origin allow-scripts → 屏蔽
// · allow-scripts → 屏蔽
// · + allow-popups-to-escape-sandbox → **仍然屏蔽**(网上很多答案说这个能救,本版本不行)
// · + allow-forms allow-popups → 仍然屏蔽
// ⇒ 只要属性存在就不给 PDF 插件权限。加白名单无效,只能不加。
//
// 不加也安全:PDF 由浏览器内置阅读器(mhjfbmdgcfjbbpaeojofohoefgiehjai)
// 在**独立扩展进程**里渲染,不是普通页面上下文,拿不到父页的 window / DOM / cookie。
// PDF 是原生文档、不是 HTML,本就没有能访问父页面的脚本环境。
// 服务端另有 `X-Content-Type-Options: nosniff` + MIME 按内容嗅探(media.php),
// PDF 不会被当 HTML 执行。若将来真需要禁止 PDF 内嵌 JS,正确做法是换 pdf.js 自渲染,
// 而不是给 iframe 挂 sandbox——那会把整个 PDF 预览一起废掉。
} else { } else {
el = document.createElement('img'); el = document.createElement('img');
el.className = 'pv-img'; el.className = 'pv-img';