Files
aurora-admin/安全审计修复与待决事项.md
T
aurora-admin f1fbfc2ddb
Regression / regression (push) Canceled after 0s
feat(品牌标识): 几何 K 图标(favicon/顶栏标记/theme-color) + 并行会话成果入库
## 品牌标识(本次会话)

起因:品牌此前没有任何图形标识 —— 唯一 favicon 是内联 data-URI 里的字母「A」,
那是 v2.0.0「Aurora Admin → Kole UI」改名漏掉的一处(PC 顶栏也是「A」,
移动端站已是「K」;移动端文档站则完全没有 favicon)。

- 几何:24 网格三个互不接触的笔画(竖 + 两斜),圆头描边;
  描边 2.25 → 16px 标签页尺寸下正好 1.5px = 规范原文「描边1.5px」
- 取色分两套(刻意):favicon 硬编码品牌蓝/白(渲染在浏览器标签栏,不继承 kole-dark);
  顶栏标记走 currentColor(实测暗色下自动转 rgb(20,22,28))
- 新增 theme-color 双条(light #FFFFFF / dark #1C1F26,取 --kole-color-card-bg)
- 修 site/app.js hero 标语 KOLE ADMIN → KOLE UI(改名变形残留)
- 移动端 7 个模板补 favicon(此前计数 0)

验收:门禁 9 条全 OK(site-routing/site-routes/mobile-docs/mobile-site/isolation/
theme/nav/i18n/icons);PC 回归 1464/1464 · 移动端 807/807,各连跑 8 次一致;
两端 favicon 405 字节逐字节一致;PC 站控制台错误 1→0。

## 并行会话成果(本次一并入库)

- 图标系统:2576 图标(TDesign/Element Plus,MIT)+ 11 端注入 + 5 个构建门禁工具
  + IconPreview 预览页 + ICON-SPEC.md 冻结规格
- 移动端平台:47 组件 × 6 端 + 文档站 53 页 + 隔离门禁
- PC 组件:103 个大后台组件 / 组件11 批次
- uni-app:PC 端试点 + 移动端端实现 + 真实编译验证

## 工程

- .gitignore 补 .scratch/ 与 .zcode-preexisting-*.txt(会话中间产物,实测 9.1MB,不入库)
- CHANGELOG 补品牌标识条目
- ROADMAP 登 S8-P4(品牌标识任务包 + og:image/apple-touch-icon 未做部分)
2026-09-21 10:05:48 +08:00

21 KiB
Raw Blame History

安全审计修复说明与待决事项

生成时间:2026-09-19 | 审计对象:本仓库 + 192.168.5.7 上运行的 kole-ui-showcase 容器 本文档分两部分:已修复(我直接做了,含证据)与 待你拍板(我做不了主的,逐项给选项与建议)。


一页速览

状态 数量 说明
已在仓库修复并验证 6 项 见第二节,全部有 exit 0 证据
已部署并在线上验收 全部 2026-09-19 重建容器:6/6 断言 + 396/396 URL + 响应头全项通过,见第三节
仍待你拍板 4 项 D3 / D4 / D6 / D8(D1、D2、D5、D7、D9、D10 已决,见第四节开头)
过程异常 4 项 见第五节 E1–E4(你已确认无其它会话,属当时并发写入)

一句话:洞已堵上且已上线——线上访问 specs/组件1.txt 现在返回 404,站点 396 条 URL 全部 200。


二、已修复(仓库层,全部已实测验证)

1. 规范原文泄漏的根因(.dockerignore)

问题:.dockerignore 里 *.md / *.ps1 / *.yml / *.png 这类写法,在 Docker 下只匹配构建上下文的根目录。实测(真实 docker build):

.dockerignore: *.md
COPY . /out/   ->   /out/sub/nested.md      ← 嵌套文件照样进镜像

所以 .design_library/kole-ui/SKILL.md、嵌套 README.md 等一直会被打进镜像;而部署目录 /opt/kole-ui 干脆没有 .dockerignore,于是 specs/、agent-reports/、preview/、ui_kits/ 全部随 COPY .design_library/ 进了镜像并对外可下载。

修复:glob 全部加 **/ 前缀,保留显式目录排除与 !README.md(只放行根级 README)。

验证(我在服务器上用当前文件做真实构建):

应排除 -> specs/ 0 | agent-reports/ 0 | preview/ 0 | ui_kits/ 0 | nested.md 0 | AGENTS.md 0 | docker-compose.yml 0
应保留 -> site/index.html 1 | site/data.json 1 | site/tokens/tokens.css 1 | site/sources/x.txt 1
          frameworks/*.css/.vue3 1 | tests/index.html 1 | tests/report.json 1
          .design_library/kole-ui/components/button.json 1 | README.md 1
BUILD_EXIT=0

2. nginx.conf 加固

  • 补 absolute_redirect off; —— 修复根路径 302 丢失宿主端口(Location: http://192.168.5.7/site/,而该机 :80 无服务 → 根地址直接打不开)。
  • 补 server_tokens off; —— 不再暴露 nginx/1.31.3。
  • 补 CSP + X-Content-Type-Options + Referrer-Policy + Permissions-Policy + X-Frame-Options,CSP 与 site/dev-server.js 里的 SECURITY_HEADERS 逐字一致(本地长期验证可用的组合,避免过严导致演示页白屏)。
  • /healthz 改用 default_type,消除重复的 Content-Type(原先是 return 200 的默认类型与 add_header 叠加)。

验证:子 agent 在真容器里跑 nginx -t → syntax is ok / test is successful / exit 0;并做了修复前/修复后运行时对照(Location 由绝对变相对、Server 由 nginx/1.31.3 变 nginx、/healthz 由两条 Content-Type 变一条且不丢安全头、gzip 行为等价)。

3. Dockerfile

补 COPY sitemap.xml /usr/share/nginx/html/sitemap.xml(此前生产 /sitemap.xml → 404)。 未动基础镜像 tag、未加 USER(nginx 官方镜像 master 需 root 绑 80,擅自改会让容器起不来)——运行期加固见 D6。

4. site/dev-server.js:单请求崩溃

问题:GET /site/%00 → decodeURIComponent 解出 NUL → fs.readFile 同步抛 ERR_INVALID_ARG_VALUE → 异常逃逸出请求回调 → 整个进程退出(畸形百分号编码反而处理正确)。CI 与本地回归都跑这个服务。

修复:编码形态拦截补 00/2500、解码后再做一次 NUL 校验(双保险)、readFile try/catch。未使用 uncaughtException 兜底(那是掩盖)。行为不变:92 条合法路径 status / content-type / 字节级一致。

验证:node tools/verify-dev-server.mjs → 20 checks / EXIT=0,含 3 条「修复前必失败」的反例断言(进程存活、突发后仍 200、stderr 无崩溃栈)。

5. 消除动态求值 sink(运行期 + 构建期)

问题:site/app.js 与 tools/precompute.mjs 都把 defineEmits([...]) 的数组字面量交给函数构造器求值。实测:

输入 defineEmits([1, globalThis.__pwned = "executed-arbitrary-js"])
旧实现 -> 真的执行了

即:任何能改 frameworks/*.vue* 的人(投毒 PR / 内部威胁)在组件详情页被打开时即可执行同源 JS;构建期同源版本在构建机上执行,而 CI 往往带 secrets。

修复:改成只解析字符串字面量的 parseStringLiteralArray——非字符串元素 / 未识别转义 / 裸换行 / 结构不符一律整体拒绝返回 null(跳过抽取、不报错、绝不执行)。构建期实现独立成 tools/lib/parse-string-literal-array.mjs。

等价性验证:node tools/verify-emits-parse.mjs → 17 checks / EXIT=0,158 个 frameworks/*.vue 双实现抽取结果不一致 0(54 处字面量 / 96 条事件名),且新实现对 6 类对抗输入全部拒绝、不执行。

6. 新增验证脚本与变更记录

  • tools/verify-dev-server.mjs(20 checks)、tools/verify-emits-parse.mjs(17 checks)——均为零依赖、exit 1 表示失败、含「修复前会失败」的反例断言。
  • CHANGELOG.md 追加 [Unreleased] → Security · 生产部署链审计修复(2026-09-19)。
  • ⚠️ 规划修正已在 CHANGELOG 标注:PLAN.md §8「不改 site/dev-server.js(已工作良好)」的立论被实测推翻,按 AGENTS.md 第五节「以实测为准」处理。

回归证据

连跑 9 次(子 agent):每次 passRate 100% | pages 79 (all-pass 79) | 1017/1017 | N/A 34 | rc=0
主 agent 独立复跑 1 次(独立端口 13411):passRate 100% | 1017/1017 | N/A 34 | EXIT=0
零依赖铁律:zero-dep OK(EXIT=0)

三、已部署(2026-09-19)

按 AGENTS §九 的标准流程部署(备份 → 暂存目录核对 → 替换 → docker compose build && up -d)。共执行两次:

  1. 修复落地:把审计修复 + 工作树当前状态推上线(消除 288 文件漂移)。
  2. 重新部署:npm run build:site 两步构建重生成 data.js / data.json / site/sources / site/details 后,再跑一次打包流程保持线上与仓库一致(实测线上 /site/data.js 102,555B、/site/data.json 1,251,099B,与仓库一致;data.json 仍为完整版,符合铁律 5)。

回滚点:

  • /opt/kole-ui.pre-audit-20260919-0609 —— 审计前的原始状态(含被泄漏的 specs/、旧 nginx.conf.bak)
  • /opt/kole-ui.prev-20260919-0614 —— 第一次修复部署后的状态
  • /root/kole-ui-backup-20260919-0609.tgz(877 KB)、/root/kole-ui-backup-20260919-0614.tgz(882 KB)
项目 修复前(实测) 修复后(实测)
/.design_library/kole-ui/specs/组件1.txt 200 / 8623B(规范原文可下载) 404
/.design_library/kole-ui/agent-reports/*.json 200 404
嵌套 SKILL.md / README.md 200 404
/AGENTS.md、/package.json、/Dockerfile、/nginx.conf 404 404(一直未泄漏)
/sitemap.xml 404 200
tests/report.json(首页通过率角标数据源) 404 200
根路径 302 Location: http://192.168.5.7/site/(丢端口,落点无服务) Location: /site/(相对,保留端口)
响应头 无安全头,Server: nginx/1.31.3 CSP + nosniff + Referrer-Policy + Permissions-Policy + X-Frame-Options,Server: nginx
/healthz 两条 Content-Type 1 条,且安全头未丢
/site/dev-server.js 旧版(无白名单/无敏感路径过滤) 加固版(isPublicPath + KOLE_PORT)
组件文件 09-06 快照,与仓库漂移 288 个文件 与工作树一致
sitemap 的 396 条 URL 396/396 → 200 396/396 → 200(无回归)
容器 healthy healthy

对 Agent 承诺的资源(.design_library/kole-ui/{metadata.json,components/index.json,css.json,colors_and_type.css})仍正常 200 —— 排除的只是规范原文、agent-reports、preview、ui_kits 这些不该外发的部分。

访问方式:按你的决定保持 127.0.0.1:3311 回环。经实测确认,服务器上没有任何 frp 隧道 / 反代 / 域名指向该服务,所以从任何其它机器都打不开是必然的——唯一的访问路径是 SSH 隧道:

ssh -o ExitOnForwardFailure=yes -N -L 13311:127.0.0.1:3311 root@192.168.5.7
# 浏览器打开 http://127.0.0.1:13311/site/  (本机 13311 是为了不占本地 dev-server 的 3311)

已实测:经隧道对线上部署跑完整文档站冒烟(真实浏览器)全部通过——路由、中英切换、主题面板、详情页渲染均正常。 若想让局域网内其它设备也能直接打开 http://192.168.5.7:3311/,需把 compose 绑定改为 0.0.0.0:3311 并注意 Docker 会绕过 ufw —— 这属于显式暴露变更,等你说一句我就改。


四、待你拍板(D1–D10)

2026-09-19 决定记录(依据你的指示「暂时不考虑线上公用,但是还是得部署,修改原本的设计和计划」):

项 结论 落地位置
D1 是否部署 ✅ 已部署(按流程:备份 → 暂存核对 → 替换 → 重建;6/6 断言 + 396/396 URL 通过) 本文第三节、CHANGELOG Deploy 段
D2 部署方式 ✅ 已改为可复现:新增 tools/pack-deploy.mjs(.dockerignore 为唯一真源 + 硬断言),流程写进 AGENTS §九 tools/pack-deploy.mjs、AGENTS.md §九、ROADMAP S5-P13
D5 端口暴露 ✅ 保持回环、不对外公用;AGENTS §九 明确「打不开是正常的,用 SSH 隧道」 AGENTS.md §九
D7 PLAN §8 冻结条款 ✅ 已按实测修正(§7 与 §8 两处都标注 ⚠️ 规划修正) PLAN.md、CHANGELOG.md
D9 测试基线口径 ✅ 已同步为 1017 通过 / 0 失败 / 34 N/A / 共 1051(标注口径来源) TESTING.md、AGENTS.md §八、ROADMAP 规划修正记录
D10 是否还有其它会话 ✅ 你已确认无;本次审计中观察到的是当时并行写入,后续已复核无冲突 本文第五节 E1–E4

仍未决:D3(tests/report.json 是否随镜像发布)、D4(正式域名)、D6(容器运行期加固)、D8(契约对外承诺口径)。这四项已登记为 ROADMAP 任务包 S5-P14/P15/P16,等你有空再定。

D1|是否现在重建并重新部署线上容器?【最高优先】

  • 选项 A(建议):只同步「本次修复的文件集」重建,不动组件文件。范围小、可回滚、立即堵住规范原文泄漏与掉端口问题。
  • 选项 B:把当前工作树整体同步(顺带消除 288 文件的漂移)。代价:会把仓库里 286 个未提交的组件改动一起上线(这些改动本地回归 100%,但等于一次完整发布)。
  • 选项 C:暂不部署,只保留仓库修复。

我的建议:先做 A(堵漏),B 作为一次独立「发布」动作、等工作树改动提交审阅后再做。

选 A 时的执行序列(等你一句话我就执行):

# 1) 备份现状(在 192.168.5.7 上)
cd /opt/kole-ui && tar -czf /root/kole-ui-$(date +%Y%m%d-%H%M).tgz .

# 2) 只更新 5 个文件:nginx.conf / Dockerfile / .dockerignore / site/dev-server.js / site/app.js
#    (加上新增的 tools/lib、tools/verify-*.mjs 可选)

# 3) 重建并重启
docker compose build && docker compose up -d

# 4) 验收断言(四条必须全中)
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3311/.design_library/kole-ui/specs/组件1.txt   # 期望 404
curl -sI http://127.0.0.1:3311/ | grep -i '^Location'                                                          # 期望 /site/
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3311/sitemap.xml                                     # 期望 200
curl -sI http://127.0.0.1:3311/healthz | grep -ci '^content-type'                                              # 期望 1

D2|部署方式:怎么根治「手工拷贝导致漂移」

这就是这次事故的根因:/opt/kole-ui 是手工拷贝的目录,无 git、无 .dockerignore,所以 ① 排除规则失效导致规范泄漏;② 曾经修好的 absolute_redirect off; 被下一次拷贝覆盖(它只存在于 nginx.conf.bak-20260916,从未进过仓库 git 历史)。

  • 选项 A(建议):改用 rsync/脚本按固定排除清单同步 + 重建,排除清单与 .dockerignore 保持同一份真源。
  • 选项 B:服务器上 git clone 仓库 + docker compose build,用 git 版本作为唯一真源。
  • 选项 C:保持现状手工拷贝(不推荐,同样的回退会再来一次)。

D3|tests/report.json 要不要随镜像发布

  • 现状:当前 .dockerignore 保留它 → 重建后首页「通过率」角标会在线上显示(子 agent 一度排除,被我方流程外的改动改回保留,并在文件里写了理由)。
  • 选项 A(建议):保留 —— 首页那个「79 测试页 + 通过率」卡片本来就在页面上,显示数字才算兑现。
  • 选项 B:排除 —— 镜像更干净,但卡片静默不显示(当前线上就是 404 → 静默隐藏)。
  • 若选 B,删掉 .dockerignore 中相关保留注释对应的行即可回滚。

D4|sitemap 与包的正式域名(SITE_URL_BASE)

sitemap.xml 内容目前是 https://YOUR-ACCOUNT.github.io/kole-ui/…(占位域名),package.json 的 repository / homepage 同样是占位。

  • 需要你给:正式域名(或明确「不做 SEO,接受占位」)。
  • build-site.ps1 已支持通过环境变量 SITE_URL_BASE 覆盖;tools/release.mjs 已有占位符闸门会拦住正式发布。
  • 顺带一提:tools/verify-site-routing.mjs 目前断言 sitemap 里必须含占位 URL——如果换了真域名,这个断言要同步改(否则校验会失败)。

D5|端口暴露策略

  • 现状:绑定 127.0.0.1:3311,只有本机/SSH 隧道能访问(我从局域网直连 192.168.5.7:3311 超时,符合预期)。
  • 选项 A(建议):保持回环,并在 README/交接文档写明「必须隧道访问」,避免有人以为服务挂了。
  • 选项 B:挂 1Panel 反代 + 域名(要不要加 Basic Auth / IP 白名单?)。
  • 影响面:选 B 会让规范原文泄漏面从「本机」变成「公网」,必须先把 D1 做完。

D6|容器运行期加固(需要一次重启窗口)

read_only: true + tmpfs、cap_drop: [ALL]、security_opt: no-new-privileges、基础镜像 digest 固定。 好处明确,但改 compose 后必须重建容器,且 read_only 配错会让 nginx 起不来(需要临时可写 /var/cache/nginx、/var/run)。要不要做、什么时候做,你定。

D7|PLAN.md §8 冻结条款的修正

PLAN.md §8 写着 ❌ 不改 site/dev-server.js(已工作良好),§7 有对应验收条目。实测推翻了「已工作良好」,我已按 AGENTS.md 路径修了代码并在 CHANGELOG 标注 ⚠️ 规划修正,但没有改 PLAN.md 本身(它在冻结清单里)。 需要你确认:是否把该行改成「dev-server 的畸形路径处理属必修项」。

D8|契约 JSON 的对外承诺怎么统一

  • 本地:79 份契约 + index.json。
  • 线上:只有 button/card/input/modal/select/table.json + index.json(7 个文件),tag.json、calendar.json 等 → 404。
  • 文档三处互相矛盾:site/llms.txt 写「契约覆盖全部 79 个组件」;AGENTS.md 写「契约 JSON 79」;而 index.json 自己的 description 写「其中 6 个核心组件另含契约 JSON」。

选项:A 全量发布 79 份(镜像略大,但对 Agent 消费的承诺为真);B 只发布核心 6 份,并把 llms.txt/AGENTS.md 的表述统一改为「6 份核心契约 + 79 组件索引」;C 保持现状(不推荐,等于承诺失实)。

D9|测试基线数字的口径同步

TESTING.md 记的是 1009 / 35,HEAD 提交信息也写「回归1009-1009」,而当前工作树实测是 1017 通过 / 34 N/A。差异来自工作树里已有的未提交改动,不是本次修复造成。建议在合并这批改动后统一同步(我没改 TESTING.md,它不在允许清单内)。

D10|是否还有别的会话/自动化在动这个仓库

见第五节 E1–E3。如果有,建议先停掉再合并,否则两边互相覆盖。这是本次最需要你确认的运维问题。


五、必须让你知情的过程异常

E1|.dockerignore 在我方子 agent 交付后被第三方进程改写。 子 agent A 交付的哈希是 3b34ffa6(其中排除了 tests/report.json),磁盘现状是 843cb650(保留 report.json 并写了理由注释)。功能上现状更优(首页通过率卡片能显示),但 A 报告的 47 条断言不覆盖现状版本——所以我用当前文件独立重做了真实构建复验(第二节 1,全过)。这也意味着我已无法声称「这个文件的每处改动都来自本次派活」。

E2|tools/precompute.mjs(05:56:41)与 tools/lib/parse-string-literal-array.mjs(05:56:45)不在任何子 agent 的允许清单内,却被写入了;且 05:56:29 有一次我没派出的构建运行。 那次运行重写了 494 个文件(site/sources/** 395、site/details/* 79、site/data.js、site/data.json、site/changelog.json),但 sitemap.xml 与 site/tokens/* 未变——即只跑了第二步 node tools/precompute.mjs,没跑 build-site.ps1。 我复核了内容与后果:

  • tools/precompute.mjs 的 node --check 通过、tools/lib/...mjs 可加载、与 site/app.js 在 158 个 .vue 上抽取结果一致、对恶意输入不执行;
  • 重建后的 site/data.js 哈希 5fdffe24… 与构建前、与线上部署三方完全一致 → 这次构建是语义无损的,同时独立证明了新的安全解析器不改变构建输出(不依赖我方验证脚本的口径);
  • 漂移比对从我审计时的「288 个文件不同」变为「289」,差额恰好是本次修复的 site/app.js,其余无新增漂移。 结论:改动立意是对的(构建期确实有同一个求值 sink,打穿的是构建机而非浏览器),但来源不明、未走派活流程。若这是你的另一个会话在做同一件事,请注意两边会互相覆盖(见 D10)。

E3|共享模块头部引用的 tools/verify-precompute-emits.mjs 并不存在。 且注释里描述的「源码逐行等价 + 5000 例种子随机等价」比实际存在的检查更强。我已把注释改为描述真实存在的门(tools/verify-emits-parse.mjs:158 文件结果等价 + 对抗语料),改动后复跑该脚本仍 17 checks / EXIT=0。 若你确实想要那道更强的门(源码等价 + 随机 fuzz),需要另派一个任务实现。

E4|服务器 /tmp 残留 kole-di-final*(非本次产物)。 我清理了自己创建的全部临时目录与 scratch 镜像;这三项来源不明,未删以免干扰可能仍在运行的会话。生产容器 kole-ui-showcase 全程未动(Up 2 days (healthy))。


六、审计中被验证为「无问题」的部分(免得重复怀疑)

检查项 结果
路径穿越(../、..%2f、%2e%2e、....//、反斜杠、.git/config) 全部 403
搜索框 XSS(<img src=x onerror=…>) 0 个注入节点、0 次执行
路由滥用 无效 slug / 越界路径全部回落首页,无异常
代码高亮渲染 先全量转义再还原 token,无注入
硬编码凭据 未发现(命中的都是 design-token 变量名)
零运行时依赖铁律 成立(dependencies 为空)
sitemap 里的 396 条 URL 线上 396/396 → 200(静态页无死链;sitemap 文件本身 404 另见第二节 3)
容器健康 healthy,/healthz 200

附录:本次审计与修复用到的复验命令

# 全量回归(需先起服务;服务端口可用 KOLE_PORT 覆盖)
KOLE_PORT=13411 node site/dev-server.js &
REG_BASE=http://127.0.0.1:13411 node tools/run-regression.mjs

# 两处缺陷的定点验证
node tools/verify-dev-server.mjs      # 20 checks,含 3 条反例断言
node tools/verify-emits-parse.mjs     # 17 checks,158 文件双实现等价

# 铁律
node -e "const p=require('./package.json');const d=Object.keys(p.dependencies||{});if(d.length){console.error('FAIL');process.exit(1)}console.log('zero-dep OK')"

# 部署配置复验(在 192.168.5.7 上,用一次性容器,不碰生产)
docker run --rm -v /tmp/<你的>/default.conf:/etc/nginx/conf.d/default.conf:ro nginx:alpine nginx -t

证据留档:C:\Users\12914\Desktop\组件规范第一套\.zcode\deployed-manifest.txt(线上 1394 文件哈希清单)、.zcode\drift-check.mjs(漂移比对脚本)、.zcode\sitemap-check.txt(396 条 URL 清单)。