← 返回目录

第 2 卷

第 2 / 4 卷日期:

复制代码

1constpolicy = {mode:'read-only',workspaceRoot: process.cwd() }
2constconfined = ctx.sandbox.confine(['bash','-c','echo hi > /tmp/out.txt'], po
3constresult =spawnSync(confined.argv[0], confined.argv.slice(1)) *// spawnSync

包装后的 argv 是这个样子:

复制代码 1sandbox-exec -p <profile> -- bash -c <命令></profile>

从左到右认识一下这几个部分:

sandbox-exec:这一层的 runner,在 macOS 上正是沙箱机制 Seatbelt 的命令行工具。

-p:告诉它:策略以字符串的形式直接跟在后面。

<profile></profile>:那段策略本身。下面马上把它单独打出来看。

--:分隔符,前面归沙箱,后面才是真正要跑的命令。

bash -c <命令>:你原本要跑的那条命令,一个字没动,只是被挪到了后面。

返回值有四样东⻄,每样都有用(先认个词:runner,平台上真正去包装、启动命令的那个程序,Linux 是 bwrap、macOS 是 sandbox-exec,后面细讲):

包装后的 argv——拿它去启动进程,替换掉你原本要跑的那条命 argv 令

跑 npm run start:ch07,脚本会连着跑五组对照。这里挑三组讲:①② 分别看“拦得住吗”和“放宽到哪儿”,⑤ 看“不套会怎样”,剩下 ③④ 的结论顺着这两组就能推出来,脚本里

都跑得到,就不占篇幅了。

先看第 ① 组:read-only 模式下往家目录写文件(家目录在工作区外,属于“不该被写”的地方):

复制代码 1--- ① read-only:往工作区外写 ---

2退出码: 1 | stderr: bash: /Users/.../_dsh_should_not_exist.txt: Operatio

被拒了。凭什么拒?答案就在上面那行 argv 里,跟在 -p 后面的 <profile></profile> 就是那段策略。把它单独打出来、去掉 JSON 转义,是这样:

复制代码 1(version 1) (allow default) (deny file-write*) (allow file-write* (literal "/dev/

这三句话就是 read-only 的全部语义:默认允许,只拒绝写,且只放行 /dev/null 这一个字面路径。留这么一个“下水道”,是让命令能照常往里丢弃输出,不至于因为没地方写而报

错。

第 ② 组换成 workspace-write,profile 末尾多出一段白名单,额外放行三个可写位置(同样去掉了 JSON 转义;$WORKSPACE、$TMPDIR 是脚本把真实长路径换成的别名):

三个位置各是什么:

$WORKSPACE 是工作区根目录,你要改的代码在这儿;

$TMPDIR 是这次调用拿到的私有临时目录;

/private/tmp:注意,你写的是 /tmp,但 Seatbelt(就是前面那个 sandbox-exec)

匹配的是解析后的路径,而 /tmp 在 macOS 上只是 /private/tmp 的符号链接。

最后这条值得停一下:正因如此,每个根目录都要先解析出实际路径、再整理字面写法;否则白名单写了一堆,实际一个也没匹配上。

注:中间 ③④ 两组正如上面所说的能推出来:③ 是 workspace-write 下往工作区外写、照样被拒,白名单只放行上面这三处;④ 是 read-only 下只读命令、不被拦,只拒

写、不拒读。

最后看第 ⑤ 组,不套沙箱的对照组,和 ① 同一个命令,退出码 0,文件真的写出了:

复制代码 1--- ⑤ 同一个命令,不套沙箱 ---

2退出码: 0(对比 ① 的 1:没被拦住,文件真的写出了)

五组看下来就一句:拦住这次写入的,不是什么隔离环境,就是命令前面被加上的那半截—— sandbox-exec -p <profile> --</profile>。你原本那条命令一个字没改,只是前面

多了这一串,写入就被管住了。

沙箱策略怎么定:每次各算各的

全站分卷索引 · 4 卷
  1. 1第 1 卷
  2. 2第 2 卷
  3. 3第 3 卷
  4. 4第 4 卷