【Codex with ChatGPT 源码学习⑪】从 read_image 到 asset import:多模态资产如何安全进入 Agent 工作区?四项真实实验发现
系列定位:通过源码和可复现实验,研究开源项目
XiaoDuoYa/codex-with-chatgpt中 ChatGPT 与 Codex 的协作边界。本章从文本代码审查转向图片读取、媒体导入、文件系统权限与资产证据链。
上游仓库:XiaoDuoYa/codex-with-chatgpt
固定源码 Commit:535a07dbb7aee887924c53767d985c09d405319d(不是后续变化的主分支)。
实验范围:Windows、Node.js 24.21.0;使用隔离临时目录直接调用原项目已构建的dist模块。没有执行生产 MCP/OAuth、Cloudflare、ChatGPT 实际生成下载或真实浏览器媒体导入端到端测试。
阅读对象:MCP/Agent 开发者、Node.js 工程师、安全与可靠性研究者。预计阅读 25~35 分钟。
01. 为什么 AI 能生成图片,还不等于 Agent 能管理好图片?
假设用户提出一个完整的工程任务:
为产品制作一张系统架构图,保存到仓库的
assets/,然后请另一名 Agent 审查它是不是正确的原始生成资产。
表面上只有“生成 → 保存 → 审查”三步;实际却至少存在七个问题:
- ChatGPT 生成的是原始图片还是浏览器显示层截图?
- 哪个组件负责下载,哪个组件拥有本地写权限?
- 文件叫
.png,就可以证明它是真正的 PNG 吗? - 已经计算 SHA-256,是否足以证明文件来自指定生成任务?
- 某张图能够导入,是否保证 MCP 客户端还能读取?
- 工作区有符号链接、Junction 或硬链接时,路径规则如何生效?
- 如果来源在检查与复制之间发生变化,最终导入的内容还是检查过的内容吗?
我们阅读了固定版本源码,并在独立测试目录中直接调用真实模块,得到四个特别重要的观察:
| 编号 | 已经观测到的现象 | 性质 |
|---|---|---|
| M-01 | 只有 8 B PNG 签名的文件可被图片读取模块接受;12 B MP4 头可成功导入 | 签名检查不等于完整解码 |
| M-02 | 20 MiB 的带 PNG 头测试文件导入成功,后续 read_image 返回 FILE_TOO_LARGE | 跨接口能力不一致 |
| M-03 | 来源文件在校验后、复制前被替换,目标内容不再符合原校验结论 | 受控验证–使用时间窗口已复现 |
| M-04 | .env.png 直接读取被拒;指向同一文件内容的硬链接 alias.png 可以读取 | 路径名称分类局限已观察 |
重要边界:这些是固定源码版本与特定实验条件下的实际结果;不是“任意用户能够远程读取真实凭据”的证明,也不是完整安全审计结论。
02. 一张图看懂 C2C 的媒体访问架构
先厘清职责:ChatGPT 能处理图像,不等于 MCP Bridge 能从 ChatGPT 浏览器直接取得本地文件;C2C 的 read_image 与 CLI asset import 也不承担同一种职责。
图中的生成/下载是 Skill 定义的 Agent 工作流。本章并没有声称实际跑通浏览器的资产下载链路。核心依据来自固定源码:
skill/SKILL.md——规定下载原始资产,不能用截图冒充生成结果。src/mcp/server.ts——注册read_image等只读 MCP 工具。src/media/import.ts——本地媒体导入实现。src/cli/index.ts——CLIasset import入口。
这一设计最值得学习的地方是读取与写入分离:ChatGPT 通过 MCP 获取受限的图片访问;把下载文件写入项目是本地 Codex 执行层的任务。不能把 CLI 写入能力等同为 MCP 已提供远程任意写文件 API。
03. 源码地图:先固定研究入口
| 文件 | 核心职责 | 上游固定链接 |
|---|---|---|
src/mcp/server.ts | read_image 工具、权限和 MCP 消息内容 | 查看源码 |
src/workspace/media.ts | readWorkspaceImage()、图片扩展名、签名、SVG、Base64 | 查看源码 |
src/workspace/manager.ts | Workspace.resolve()、真实路径与边界 | 查看源码 |
src/workspace/ignore.ts | 敏感文件名和忽略规则 | 查看源码 |
src/media/import.ts | 来源校验、目标路径、复制与哈希 | 查看源码 |
src/cli/index.ts | asset import 参数和调用入口 | 查看源码 |
skill/SKILL.md | ChatGPT 生成、下载、Codex 导入的流程规则 | 查看源码 |
后面的所有“源码事实”仅针对该 Commit。所有“实验实测”则来自与该基线对应的本地已构建模块;未对构建产物的字节内容作单独的可重现构建证明。
04. read_image 如何返回图片?
看 src/mcp/server.ts 中 read_image 的注册处理。典型请求只包含工作区相对文件路径:
{
"path": "assets/architecture.png"
}
MCP 调用层先执行 workspace.read 权限控制,再使用 readWorkspaceImage(workspace, path) 获取读取结果。响应包含两种不同的信息:
- 文本或结构化元数据:相对路径、MIME 类型、原文件大小。
- MCP
image内容块:Base64 媒体字节与 MIME 类型。
因此,read_image 不等于 read_file:后者面向代码和文本,具备二进制拒绝、按行读取等行为;前者直接读取图片字节并封装成 MCP 媒体内容。
注意两层权限:MCP workspace.read Scope 是调用者授权;Workspace.resolve() 是具体文件访问约束。直接从 Node.js 导入模块做测试,能验证后者,却不能证明 OAuth/Scope 端到端配置一定有效。
05. readWorkspaceImage():类型白名单与 10 MiB 边界
固定版本的 src/workspace/media.ts 支持:
| 后缀 | MIME | 签名/内容检查 |
|---|---|---|
.png | image/png | 8 B PNG 签名 |
.jpg / .jpeg | image/jpeg | JPEG 起始标记 |
.gif | image/gif | GIF87a / GIF89a |
.webp | image/webp | RIFF + WEBP |
.svg | image/svg+xml | SVG 文本起始结构和一组危险模式正则过滤 |
文件读取上限为 10 MiB。实现先确认文件路径、类型和大小,再读取文件 Buffer;对类型特征检查通过后,返回 Base64。
有两个技术上需要明确区分的事实:
一,文件后缀不是内容证据。 名为 fake.png、内容为普通文本的文件会被拒绝。
二,签名检查不是完整解码。 有正确 PNG 起始签名,并不意味着后续块、长度、CRC、像素数据都存在且合法。SVG 的文本正则过滤也不等于完整 XML/SVG 解析与安全渲染。
5.1 9 项真实模块验证
在 Windows 独立临时目录中调用真实 readWorkspaceImage():
| 测试 | 实际结果 | 如何解读 |
|---|---|---|
| 70 B PNG Fixture | OK,Base64 长 96 | 图片字节返回正常 |
| 76 B SVG Fixture | OK,Base64 长 104 | 该 SVG 模式被接受 |
| 伪 PNG 文本 | BINARY_FILE | 类型检查生效 |
| 仅 8 B PNG 头 | OK | 没有完整解码校验 |
onload SVG | BINARY_FILE | 此类事件属性被拒 |
.env.png | ACCESS_DENIED_SENSITIVE_FILE | 名称策略生效 |
../outside.png | PATH_OUTSIDE_WORKSPACE | 越界被拒 |
.pdf | BINARY_FILE | 不在图片白名单 |
| 超 10 MiB | FILE_TOO_LARGE | 大小限制生效 |
总计 9/9 用例符合预期。这里包含“8 B 文件被接受”这一边界观察,因此 9/9 是实验断言通过,不是九项安全检查全部通过。
06. asset import:谁拥有媒体写权限?
Skill 对原始生成资产的工作流大致规定:
- 在 ChatGPT 界面获得图片或视频的生成结果。
- 使用实际的 Download 控件获取原始文件。
- 在 Codex 本地执行环境调用
c2c asset import。 - 导入到新的工作区相对路径,不覆盖已有文件。
- 在任务执行记录和后续审查中引用导入后的路径。
命令形态示意:
node .\dist\cli\index.js asset import `
-w "独立工作区目录" `
--from "已下载文件的本地路径" `
--to "assets/generated/image.png" `
--json
这里 --from 指已经在本地的下载文件,--to 指工作区内新目标。由于真实浏览器下载链路没有在本章复测,不能把下载成功视为本章已经验证的事实。
最值得保留的设计原则是:下载证据不能用浏览器截图替代。 浏览器截图只能证明页面当时如何显示,不一定包含原始图片分辨率、编码、透明通道或媒体元数据。
07. 逐段拆解 importMediaAsset()
固定版本 src/media/import.ts 的重要操作顺序如下:
主要保护点:
- 来源必须是普通文件,大小在 1 字节至 512 MiB 范围。
- 目标必须在 Workspace 允许的相对路径内,并符合敏感文件规则。
- 根据目标后缀识别媒体类型;允许图片与 MP4/MOV/WebM 等视频文件。
- SVG 有独立 10 MiB 校验限制,并通过文本规则排除部分主动内容。
- 二进制媒体主要检查文件头部特征,并不调用完整图片/视频解码器。
COPYFILE_EXCL防止已有目标被直接覆盖;复制之后对目标内容计算 SHA-256。
但这套流程仍然留有两个不能混为一谈的概念:
类型合规只描述当前执行了哪些检查;来源可信需要证明它确实对应某个 ChatGPT 生成任务。当前导入模块不会因为扩展名和签名有效,就自动建立“来自 ChatGPT”的真实性证明。
7.1 导入模块的 12 项实测
| 场景 | 结果 | 说明 |
|---|---|---|
| 正常 PNG | OK,哈希一致 | 字节复制正确 |
| 同一目标再次导入 | 拒绝 | 非覆盖保护生效 |
| 伪 PNG | 拒绝 | 类型特征不匹配 |
| 8 B PNG 签名 | OK | 未做完整解码 |
| 正常 SVG | OK | 该文本结构通过 |
包含 onload 的 SVG | 拒绝 | 此模式被识别 |
12 B MP4 ftyp 头 | OK | 不等于完整视频可解码 |
.pdf 目标 | 拒绝 | 类型不支持 |
.env.png 目标 | 拒绝 | 敏感路径保护 |
目标 ../ 越界 | 拒绝 | Workspace 边界 |
| 空来源 | 拒绝 | 文件大小规则 |
| 来源文件无扩展名 | OK | 来源后缀不是必要条件 |
12/12 符合当前版本预期;再次强调,“不完整 PNG / MP4 被允许”是本章的可靠性边界发现,而不是良好媒体安全性的证明。
08. M-01:文件头通过,为什么图片依然可能无效?
在读者实验中,测试文件可以仅包含:
PNG: 89 50 4E 47 0D 0A 1A 0A (8 bytes)
MP4: 00 00 00 18 66 74 79 70 ... (ftyp header)
这两个样本都缺少完整格式必需的其他数据。实验结果却分别允许作为图片读取/媒体导入的候选。
这说明当前代码验证的是:类型特征与后缀是否相称,并没有证明:
- PNG 的结构、块校验和像素是否完整;
- MP4 的盒结构、时长、轨道和媒体数据是否可解码;
- SVG 是否可以在受限制的渲染环境中安全显示。
对于未来 Agent 系统,至少应将 signatureValid 与 fullyDecoded 分成两个独立字段。未经完整解码检查时,fullyDecoded 应标记为“未验证”,而不是凭签名推断为真。
09. M-02:20 MiB 图片能导入,却不能通过 MCP 读取
这里出现了明确的跨组件契约差异。
| 接口 | 文件范围 | 大小界限 |
|---|---|---|
importMediaAsset() 普通图片/视频 | 导入原文件 | 最高 512 MiB |
importMediaAsset() SVG | 文本安全检查 | 10 MiB |
readWorkspaceImage() 支持的图片 | 读取并 Base64 返回 | 10 MiB |
隔离实验构造了一个有 PNG 签名、总长度 20,971,520 B(20 MiB) 的测试文件。实测:
{
"importedBytes": 20971520,
"importedMime": "image/png",
"targetSha256Valid": true,
"sourceMatchesTarget": true,
"readImageResult": "FILE_TOO_LARGE",
"oversizedSvgRejected": true,
"result": "PASS"
}
请注意:这个 20 MiB Fixture 并非完整解码过的有效 PNG;它只是帮助我们确定签名、大小和接口边界。
工程启发:Imported、ReadableByMCP、Decodable 和 Reviewable 是四个不同状态。导入成功后,应立即判断后续审查接口是否能够访问;必要时在可信的本地执行环境中生成受控缩略图或转换格式,但不得把缩略图冒充原始生成资产。
还有一个资源成本:10 MiB 图片编码为 Base64,文本长度大约扩展到 13.33 MiB(不含 MCP/JSON 包装开销)。因而限制不仅关乎文件大小,也关乎传输与内存成本。
10. M-03:检查通过后,实际复制了另一个版本
这是本章最重要的数据一致性发现。
观察源码顺序:来源 stat、头部签名检查、实际 copyFile()、目标 SHA-256 发生在不同时间点。来源如果在“通过检查”与“开始复制”之间发生变化,会怎样?
我们做了一个受控故障注入:在独立测试进程中,临时包裹 fs.promises.copyFile();当导入函数已完成来源签名验证、即将执行复制时,用同样长度、但不再包含 PNG 签名的测试数据替换来源。
结果:
| 指标 | 实测 |
|---|---|
injectionExecuted | true |
| 初始大小 / 导入返回大小 | 都是 72 B |
copiedModifiedContent | true |
importedHashMatchesTarget | true |
originalHashMatchesTarget | false |
signatureValidAfterCopy | false |
result | VALIDATION_COPY_GAP_OBSERVED |
这里不是 SHA-256 算法不可靠。它准确地记录了复制后的目标字节,却不能证明那些字节与此前通过验证的来源是同一份版本。
这是基于控制测试进程中复制调用的确定性注入;没有证明实际远程用户可触发该竞争,更不能宣称已有恶意利用发生。
更可靠的设计应先把来源保存到权限可控、稳定的暂存副本,再对同一暂存版本做签名/解码/哈希校验,最后安全地提交目标。若来源哈希和已验证内容版本不一致,必须重新验证或拒绝。
11. 路径安全为什么不能只依赖 ../ 过滤?
源码中 Workspace.resolve() 并不是简单字符串前缀判断。它会解析 Workspace 根路径,然后对请求路径的最深现存祖先进行 realpathSync.native() 处理,再判断规范化后的目标是否仍位于工作区内部。源码参考 src/workspace/manager.ts。
相对路径越界只是其中一种问题。Windows 下还需考虑 Junction、软链接等路径别名:
Workspace/
assets/ 真实目录(内部)
internal-link/ Junction → Workspace/assets/
external-link/ Junction → Workspace 外部目录
隔离的 Windows 静态链接测试结果:
| 场景 | 结果 | 判定 |
|---|---|---|
| 正常导入 | OK | PASS |
| Junction 指向工作区内 | OK | PASS |
| Junction 指向工作区外 | PATH_OUTSIDE_WORKSPACE | PASS |
../ 越界 | PATH_OUTSIDE_WORKSPACE | PASS |
| 文件软链接指向敏感路径 | 未创建成功 | SKIP |
| 文件软链接指向外部 | 未创建成功 | SKIP |
4 PASS / 2 SKIP。这验证了已存在的静态 Junction 边界,不代表在检查后其他进程替换目标父目录时仍然安全。并发目录替换没有在本章执行。
12. M-04:硬链接为什么能改变敏感文件的名称分类?
这里需要区分符号链接和硬链接。
符号链接或 Junction 包含一个指向其他路径的关系,可以通过真实路径解析处理;硬链接则意味着不同名字可以引用同一份文件对象,并没有唯一的“原始路径名”。
实验创建的是虚构的测试图片数据,不是真实 .env:
Workspace/
.env.png ← 敏感规则按名称拒绝
alias.png ← 硬链接,指向同一文件内容
Windows 实测结果:
{
"hardlinkCreated": true,
"directSensitiveRead": "ACCESS_DENIED_SENSITIVE_FILE",
"aliasResolvedPath": "alias.png",
"sameBytes": true,
"sameSha256": true,
"aliasReadResult": "OK",
"aliasMimeType": "image/png",
"result": "OBSERVED"
}
为什么会这样?因为 Workspace.resolve() 最终执行的是当前路径名称的敏感分类。它不能从普通硬链接的名称推导出这个文件对象还被另一个敏感名称引用。
因此,当前策略在该条件下的确表现为:.env.png 拒绝,alias.png 允许。
但这不等于已证明绕过操作系统 ACL、取得了真实凭据,或者任意 MCP 远程调用者都能创建这种硬链接。现实威胁模型还必须验证身份权限、目录 ACL、谁能创建链接、谁能读原文件,以及 MCP Scope 和工作区连接范围。
正确的工程定位是:文件名敏感规则属于辅助防线,不能单独承担文件内容机密性的全部保证。 真正的秘密应与普通可共享工作区进行文件系统权限和存储位置隔离。
12.1 硬链接如何形成路径名称与文件对象的分离?
这张图解释的是名称规则与底层对象身份不等价,而不是示范远程利用。若要形成真实安全影响,还需要具备创建硬链接及读取目标内容所需的操作系统权限。
13. 从单项防护转向完整信任边界
把本章的安全与可靠性问题放到同一张图中:
对 Agent 系统,至少存在四类“证明”:
| 要证明什么 | 需要的证据 | 单独的 SHA-256 能否证明 |
|---|---|---|
| 媒体来自哪个生成任务 | 下载任务关联、来源可核对信息 | 不能 |
| 图片/视频是否完整 | 类型签名 + 适当的格式解码校验 | 不能 |
| 导入的是否是验证过的内容 | 同一受控副本的哈希及稳定提交 | 只能部分辅助 |
| Reviewer 看的是哪个版本 | 读取时的内容指纹与对象版本 | 需要配合时序证据 |
这里所有“改进”是独立架构建议;不要将它们误写成 C2C 已有的综合媒体信任机制。
14. 读者最容易误解的三个“PASS”
误解 A:9/9 PASS,所以图片输入完全安全。
实际含义是九个测试用例符合事先断言,包含“仅 PNG 文件头也能读取成功”这个观察。它恰恰揭示格式验证并不深入。
误解 B:导入 SHA-256 正确,所以原始生成资产没有被替换。
哈希只描述计算时的目标字节。M-03 显示,目标哈希可以完全正确,但此前的类型检查已针对另一份来源版本。
误解 C:硬链接测试成功,就证实远程任意文件读取。
实验只证明了在测试者有权限主动创建硬链接时,两个名字命中的路径分类不同。没有测试远程攻击者权限,也没有验证真实秘密泄露。
有价值的公开技术文章必须同时保留已证实行为、可适用范围和未验证事项。
15. Media Asset Evidence Contract v0.1:可信交接的接口草案
从实际模块观察可以进一步提出一个用于 Planner、Codex、Browser Adapter 和 Reviewer 协作的证据契约。
{
"schemaVersion": "0.1",
"assetId": "asset-example-001",
"workspaceId": "workspace-example",
"source": {
"kind": "browser-download",
"originVerified": false,
"sha256": null,
"downloadTaskId": null
},
"validation": {
"signatureValid": true,
"fullyDecoded": false,
"validationPolicy": "signature-only-v1",
"validatedSha256": null
},
"import": {
"destination": "assets/generated/example.png",
"sizeBytes": 20971520,
"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"sourceMatchesTarget": null
},
"review": {
"mcpReadable": false,
"reason": "FILE_TOO_LARGE",
"reviewedSha256": null
}
}
这是教学性设计提案,并非原 C2C 已实现的数据类型或响应。示例哈希为格式占位值而非某个真实文件的哈希。originVerified=false 表示未建立来源证明;fullyDecoded=false 表示未完成完整解码验证,不代表已检测出恶意内容。
配套 ZIP 中有 JSON Schema 和使用真实导入模块创建示例证据的脚本。建议的最小一致性条件:
- 来源媒体在受控暂存位置形成稳定内容版本,并记录字节哈希。
- 类型/格式验证明确标识检查深度,必要时使用实际解码器。
- 已验证的版本哈希与最终目标版本哈希能够比较,并在不一致时拒绝或重新验证。
- 提交过程坚持非覆盖,约束目标目录权限,记录实际文件版本。
- 可读性不从导入成功推导;需要结合 MCP Reader 的真实结果。
- Reviewer 每次访问保存相应内容指纹,避免审查依据在文件被替换后无从追溯。
16. 完整动手实践:下载资源、配置环境、逐项复现
本节是文章的实际操作指南。公开配套资源提供完整 .mjs 脚本、公共辅助库、测试、Schema、使用说明、故障排查和 SHA-256 文件校验清单。
实验资源下载:【下载资源】。请优先阅读包内
README.md和安全边界与清理.md。
16.1 准备固定版本的 C2C 源码
系统要求:Node.js 20+、Git,能够按照项目固定版本 package.json 构建;本文有直接实测证据的是 Windows 环境,Linux/macOS 为参考命令而非本章实测结论。
在新的学习目录克隆,避免对当前业务仓库强制重置:
git clone https://github.com/XiaoDuoYa/codex-with-chatgpt.git c2c-ch11-source
cd c2c-ch11-source
git checkout --detach 535a07dbb7aee887924c53767d985c09d405319d
corepack pnpm install --frozen-lockfile
corepack pnpm build
此时应存在:
c2c-ch11-source/
dist/workspace/manager.js
dist/workspace/media.js
dist/media/import.js
这些实验不需要 ChatGPT/Codex 账号登录、不需要配置 Cloudflare Tunnel、不需要安装 Skill 或启动 MCP Bridge。
16.2 解压 ZIP 并执行预检
在 ZIP 解压后的 c2c-ch11-public-lab 目录打开 PowerShell:
$repo = 'C:\learning\c2c-ch11-source' # 换成你已构建的源码目录
node .\scripts\00-preflight.mjs --repo "$repo"
node --test .\tests\helpers.test.mjs
预检将核对源码 Commit、Node 版本和 dist 模块。基础自测试只验证本实验包的参数、测试 Fixture 和安全清理保护,不等于上游集成实验全部通过。
16.3 实验 A:直接读取图片(9 项)
node .\scripts\01-read-image.mjs --repo "$repo"
重点看 header-only-png、sensitive-path、path-traversal 和 over-limit。header-only-png=OK 是已知格式验证边界;不要把不完整字节输出当作可显示图片。
16.4 实验 B:媒体导入(12 项)
node .\scripts\02-asset-import.mjs --repo "$repo"
重点看非覆盖、签名、目标类型和导入后 SHA-256。这个脚本通过真实导入模块复制虚构样本,未调用真实 ChatGPT 下载功能。
16.5 实验 C:20 MiB 读写上限差异
node .\scripts\03-size-boundary.mjs --repo "$repo"
该实验创建 20 MiB 带 PNG 头的临时文件;目标是验证大小限制,并不测试实际图片显示。
16.6 实验 D:校验后的来源变化
node .\scripts\04-source-mutation.mjs --repo "$repo"
期望观察 VALIDATION_COPY_GAP_OBSERVED。实验只在自己创建的 Node 进程中临时拦截 copyFile(),并在 finally 恢复。它不是可用于生产环境的“安全补丁”或攻击工具。
16.7 实验 E:Junction、符号链接和硬链接
node .\scripts\05-links.mjs --repo "$repo"
Windows 若缺少创建文件符号链接的权限,相关用例可能显示 SKIP;请保留真实结果,不要为了跑满测试而降低系统权限保护。硬链接实验只使用虚构 PNG 头,不涉及真实秘密。
16.8 实验 F:媒体证据契约演示
node .\scripts\06-evidence-demo.mjs --repo "$repo"
它展示如何记录来源哈希、验证阶段、目标哈希及读回版本。注意输出为教学提案,并非上游 MCP 的现成 JSON Schema。
16.9 Linux/macOS 参考运行方式(未实测)
REPO="$HOME/c2c-ch11-source"
node scripts/00-preflight.mjs --repo "$REPO"
node --test tests/helpers.test.mjs
for x in scripts/0[1-6]-*.mjs; do node "$x" --repo "$REPO"; done
不同文件系统的链接权限、路径大小写规则和媒体文件系统语义可能不同,发布结果请明确标注平台。
16.10 实验结束后的清理与安全界限
所有脚本只在操作系统临时目录创建 c2c-ch11-* 工作区,正常执行结束后自动清理;原 C2C 克隆只用于模块导入,不修改项目配置、Git 历史或真实数据。
没有自动复制用户下载文件、不会访问账号凭据、不会发起浏览器控制、不会创建真实 .env。如果进程被强制终止,请手工确认临时目录内容与本次测试对应后再删除;不要批量清空系统 Temp。
17. 两类问题该如何提出工程整改?
这里区分已经验证的行为和仍需测试的增强:
| 关注点 | 当前证据 | 可行改进方向 | 尚未证明 |
|---|---|---|---|
| 文件签名不足 | 8 B PNG / 12 B MP4 | 实际解码验证、格式策略版本 | 所有非法媒体都能绕过 |
| 20 MiB 不可读取 | 接口差异实际复现 | 导入即标记可读性,受控缩略图通路 | 任意真实大图都可转换成功 |
| 校验/复制时间窗口 | 72 B 受控变更复现 | 同一稳定副本先验证后提交,版本哈希比对 | 真实攻击者具备竞争条件 |
| 硬链接名称分类 | 普通别名可读同一测试字节 | ACL、凭据隔离、最小权限、审查链接创建权限 | 远程 MCP 具备任意硬链接创建权限 |
| 静态 Junction | 外部路径被拒、2 项 SKIP | 目录权限隔离、稳定句柄及竞态测试 | 并发替换完全安全 |
| SVG 正则过滤 | onload 被拒、源码有一组过滤规则 | 结构化处理、安全渲染和多变体测试 | SVG 完整规范安全性 |
特别是校验与复制的时间窗口,不能只通过再多计算一次最终哈希解决;关键在于保证“验证对象”与“最终使用对象”是同一个稳定内容版本。
18. 本章测试边界与复现实验的正确读法
本文的实测来自固定 Commit 对应的本地已构建模块,隔离实验数据均为虚构内容。下列事项未完成验证:
- 真实 ChatGPT 图片/视频生成 → 浏览器 Download → Codex 导入的端到端链路;
- 远程 OAuth/MCP
workspace.readScope 的端到端授权; - PDF/音频的专用处理、OCR、视频理解或标准解码器综合安全验证;
- 完整 SVG 规范、XML 实体/编码组合及安全渲染;
- Windows 文件符号链接的两个跳过用例;
- 并发父目录/Junction 替换与跨进程竞争下的实际可利用性;
- Linux/macOS 同等环境复测;
- 当前项目后续主分支是否已修复或改变这些行为。
本次实测表格里的 PASS 仅表示测试执行与断言相符。OBSERVED 表示已观察到限制或不一致。任何真实安全影响都应进一步结合权限模型和攻击前提评估。
19. 总结:多模态 Agent 的可靠性,来自连续的证据链
回顾这个项目的技术路线:浏览器负责可见界面交互,Codex 负责本地执行,MCP 负责受控只读访问。这种职责划分在媒体资产流程中尤其清晰。
但职责分离只是开始。从 ChatGPT 生成结果到本地文件,仍然需要回答:原文件从哪里来?校验针对哪个版本?最终复制了什么?Reviewer 实际读到了哪一个版本?
第十一章最重要的四个结论是:
- 扩展名、签名与完整解码不同。 文件头只能证明有限的格式特征。
- 导入成功与可审查不同。 不同接口上限必须进入 Agent 的工作流状态。
- 哈希正确与校验一致不同。 哈希要绑定稳定的验证副本,才能形成有效的传递证据。
- 路径名称和文件对象身份不同。 链接、Junction、ACL 和敏感规则属于不同的安全控制层次。
下一章预告:【Codex with ChatGPT 源码学习⑫】测试体系与故障注入:怎样让 Agent 系统的问题变成可稳定复现、可回归验证的工程证据?
资料与致谢:源码研究基于 XiaoDuoYa/codex-with-chatgpt 的固定公开提交;实验脚本为独立教学资源,与上游项目的官方测试或安全公告无关。若在未来版本发现行为变化,请优先引用实际 Commit、运行输出和对应源码,而不是直接复用本文的结论。
系列导航:参见同一系列的「导读」及其他章节。
「系列导读」:ChatGPT 负责思考,Codex 负责干活?从源码读懂 Codex with ChatGPT(系列导读)
「上一章」:【Codex with ChatGPT 源码学习⑩】Workspace Search 与 Git Diff:从快速检索到可信代码审查——两项真实复现的证据完整性问题
「下一章」:【Codex with ChatGPT 源码学习⑫】188 项测试全部通过,为什么 Agent 仍然有四项 RED?测试体系、故障注入与可审计回归
本文基于Codex with ChatGPT固定源码,通过四项真实实验揭示多模态资产(图片/视频)进入Agent工作区时的关键安全边界:文件头校验不等于格式完整、导入成功不等于MCP可读、哈希一致不保证来源未篡改、路径过滤无法防御硬链接绕过。重点分析类型白名单、10 MiB读取限制、权限分离架构及媒体证据契约设计,强调需构建从暂存、验证、哈希绑定到审查的连续证据链。
387

被折叠的 条评论
为什么被折叠?



