权限与围栏
这套系统里最要紧的部分。分三层看。
text
应用想干一件事
│
├─ ① 围栏(Node 权限模型) ── 内核层面拦,越权直接崩
├─ ② 授权表(能力 → 范围) ── 查这个应用有没有这项能力
└─ ③ UAC 审批(问用户) ── 没有就弹框问① 能力即字符串
text
fs.read:/sdcard/**三段:能力 + : + 范围。
授权记录是三元组:
text
应用 = demo
能力 = fs.read
范围 = /sdcard/**同一个应用、同一个能力、同一个范围 —— 只问一次。
② 能力清单
文件类
| 能力 | 说明 |
|---|---|
fs.read | 读文件内容 |
fs.list | 列目录 |
fs.stat | 取元数据(大小、mtime、类型) |
fs.watch | 监听文件变化 |
fs.write | 写文件 |
fs.mkdir | 建目录 |
fs.remove | 删除 |
fs.copy | 复制 / 移动 |
读能力家族互覆盖。 一条 fs.read:/data/** 同时覆盖 fs.list / fs.stat / fs.watch。 理由很实际:fs.list 本来就需要读目录,fs.stat 本来就需要读 inode, fs.watch 本来就需要读目录项 —— 让用户为同一件事点三次是设计失败。
进程 / 网络 / 系统
| 能力 | 风险 | 说明 |
|---|---|---|
proc.spawn | 高 | 起子进程。范围是可执行文件路径 |
net.fetch | 中 | 联网。范围是主机名 |
sys.info | 低 | 读系统信息,不含敏感内容 |
交互类
| 能力 | 风险 | 说明 |
|---|---|---|
dialog.open | 低 | 文件选择器 / 保存框 / 消息框 |
clipboard.write | 低 | 写剪贴板 |
clipboard.read | 中 | 读剪贴板 —— 里面可能刚复制过密码、验证码或私钥 |
③ UAC 的两种答案
这一点很重要,容易搞混:
| UAC 审批 | 对话框 | |
|---|---|---|
| 问什么 | 「允许这个应用读 /sdcard 吗?」 | 「你要打开哪个文件?」 |
| 答案 | 枚举:允许 / 拒绝 / 始终允许 | 数据:路径、文件名、按钮 id |
| 谁决定 | 用户 | 用户,但选的是内容 |
应用永远不能指定自己想去哪。 对话框的起始目录 startIn 有白名单 (沙箱 data → cwd → 家目录),也没有 defaultPath 这个参数。
用户选中一个路径后,系统会为那一个路径自动记一条授权 —— 这就是「点了一个文件」与「知道应用要拿它做什么」之间的分界。 高危路径(私钥之类)不在自动授权范围内,仍要走审批。
风险分级
| 等级 | 能力 | 处置 |
|---|---|---|
| 低 | sys.info fs.read fs.list fs.stat fs.watch dialog.open clipboard.write | 弹一次框,用户点「始终允许」后不再问 |
| 中 | fs.write fs.mkdir fs.remove fs.copy net.fetch clipboard.read | 每次都问 |
| 高 | proc.spawn | 需要先开限时管理员模式 |
dialog.open 定为低风险的理由写在了代码里:由用户在桌面上亲自选择,应用无法指定路径。
管理员模式
高危操作(起子进程)需要限时提权:
- 通过挑战口令开启,有效期 30 分钟,自动失效
- 桌面顶部常驻红条 —— 提权期间用户必须看得见
- 生效期间高危操作不再逐个弹框
围栏
Node 应用跑在 Node 权限模型(--experimental-permission)里:
bash
node --experimental-permission \
--allow-fs-read=./ \
--allow-fs-write=./data \
--allow-child-process \
app.js内核生成这些参数时会自动放行 @runx/sdk 与 @runx/host 所在的目录 —— 否则应用连自己的 SDK 都读不到。
@runx-node-safety 的策略文件里有一段专门的说明:
js
} else if (cap === 'dialog.open' || cap === 'clipboard.read' || cap === 'clipboard.write') {
notes.push(cap + ' 由审批层(broker)控制,Node 权限模型没有对应开关');
}因为这些能力走的是应用 → 网关 → 审批层的链路,根本不碰 Node 的文件 API。
一句话总结
应用能做什么,由它申请什么决定; 它能碰哪些路径,由用户批准什么决定; 它碰不到什么,由围栏在内核层面兜底。