- 应用安全
- 漏洞扫描
- AI 应用
【免费下载链接】codex-security
OpenAI's Codex Security CLI and TypeScript SDK for finding, validating, and fixing security vulnerabilities. npm: https://www.npmjs.com/package/@openai/codex-security
导读
artifact-storage.md是 Codex Security 插件(plugin)与独立制品产出型 Skill 必须遵守的制品(artifact)存储策略:它规定哪些文件可以用save_codex_security_artifact/read_codex_security_artifact写入,如何用scanId或targetPath声明制品归属权,如何在persistent(持久)与temporary(临时)两种存储之间正确选择,以及共享威胁模型缓存、已完成扫描密封结果等边界规则。读完本文,你将掌握在插件托管扫描与独立阶段中安全、合规地创建、读取、导入和引用扫描制品的完整操作契约,并能从源码层面理解其路径校验、原子写入与上下文绑定的实现原理。
适用范围与职责边界
策略开篇先划清了「谁适用、谁不适用」的边界,避免策略被错误套用到其他工作流:
- 适用:插件管理的扫描(plugin-managed scans)与独立产出制品的 Skill(standalone artifact-producing skills)。
- 不适用一:显式由 SDK 拥有的工作流(explicitly SDK-owned workflow)保留其 SDK 提供的目录、既有的制品写入与完成行为,应遵循其自身既有指令,而非本策略。
- 不适用二:受约束的 Deep worker 保留其既有的窄范围制品工具(narrow artifact tools)与只读执行画像(read-only execution profile)。
也就是说,本策略针对的是「由插件 MCP 管理、可写制品」的扫描路径,而 SDK 自有目录与 Deep worker 的受约束沙箱不属于本策略管辖。对应地,仓库中 compact-artifact-tools.ts 同时注册了面向主扫描的save_codex_security_artifact/read_codex_security_artifact,以及面向 Deep worker / reducer 的受限工具集(record_codex_security_scan_draft、get_codex_security_deep_reducer_inputs、record_codex_security_deep_reduction),后者正是「窄工具 + 只读画像」在源码层的体现。
扫描归属权:先取 scanId,再建制品
对于完整扫描(full scan),创建任何制品之前必须先从权威来源取得scanId。不同扫描模式使用不同的启动工具:
| 扫描模式 | 启动方式 | 说明 |
|---|---|---|
| Standard 标准扫描 | start_codex_security_standard_scan | 插件 MCP 的标准扫描入口 |
| Headless Diff(无扫描上下文的差异扫描) | start_codex_security_prompt_only_scan | 必须携带精确的 target、mode: "diff"、scope: "."与diffTarget |
| Deep 深度扫描 | 既有 Deep coordinator(协调器) | 沿用其既有协调流程 |
同时必须保留既有的 scan 与 handoff token(交接令牌)。如果所需 MCP 不可用,或所选基线(baseline)不受支持,应当上报阻塞原因(blocker),而不是回退到用 shell 手工编写 canonical 文件——这些规则优先于更早期的终端文件写入回退方案(terminal file-authoring fallbacks)。
在源码中,createScanArtifactContext(artifact-context.ts)通过 workbench 的get-scan --scan-id <scanId>命令解析扫描记录,校验状态是否为running(requireRunning为 true 时非 running 直接拒绝修改),并核对handoffClaimToken:当扫描设置了期望的 claim token 时,缺省或错误的 token 都会被拒绝("requires its current continuation claim")。这印证了「先有权威 scanId + 正确 claim,才能写制品」的硬性约束。
另一条硬性规则是:结构化输出必须继续走各自既有的工具——inventory(清单)、candidate discovery(候选发现)、validation(验证)、attack paths(攻击路径)、semantic drafts(语义草稿)、checkpoints(检查点)与 completion(完成)。canonical 结果与恢复检查点(recovery checkpoints)即使在扫描运行期间也是持久的;绝不使用补充文件工具(save tool)去替换它们或生成report.md。这一点在 artifact-storage.ts 的supplementalPath校验中落地:persistent存储只允许artifacts/、findings/、hardening/前缀、report_validation.md与独立集合下的threat_model.md,并且命中 reserved_artifact_paths.json(如artifacts/02_discovery/candidate_ledger.jsonl、artifacts/deep_discovery等)时会直接报错 "Use the existing scan tools for canonical artifacts, ledgers and checkpoints."。
补充文件:save / read 工具与两种存储
策略明确要求:Markdown 文档、可选的 finding 详细分析(write-ups)、加固文档/图表(hardening documents/diagrams)、验证证据(validation evidence)与保留的辅助输出(retained helper output),一律通过插件 MCP 的save_codex_security_artifact和read_codex_security_artifact读写;不得用 shell 重定向、apply_patch、Python 或其他普通文件写入工具来写这些保留文件。
声明归属权(owner)
每次调用必须且只能声明一种归属:
- 运行中的扫描:提供
scanId,以及(在要求时)当前的handoffClaimToken; - 独立阶段或扫描完成后的衍生文档:提供
targetPath,指向被授权的仓库或输入文档目录。这会创建一个目标绑定的制品集合(target-bound artifact collection),但不会启动扫描,也绝不能编造 scan ID。
supplementalContext(compact-artifact-tools.ts)严格执行 "Provide exactly one scanId or standalone targetPath",且handoffClaimToken在没有scanId时会被拒绝。独立路径会经standaloneArtifactContext(artifact-storage.ts)解析目标真实路径,用sha256(realpath)生成稳定的集合标识,并拒绝把制品集合建在目标仓库内部("Artifact storage must be outside the target repository."),同时以0o700权限创建目录。
显式选择存储:persistent 与 temporary
storage字段必须显式给出,二者语义截然不同:
| 维度 | persistent(持久) | temporary(临时) |
|---|---|---|
| 用途 | 保留的文档/证据,位于权威扫描目录或独立集合下 | 可丢弃的暂存与执行输出(staging / execution output) |
| 默认根目录 | $CODEX_SECURITY_STATE_DIR/scans;未设置 state 覆盖时为$CODEX_HOME/state/plugins/codex-security/scans(CODEX_HOME默认~/.codex) | 上下文相关的 OS 临时目录 |
| 覆盖规则 | 既有的CODEX_SECURITY_SCAN_ROOT覆盖仍然优先于插件创建的输出 | 不依赖持久集合 |
| 状态回退 | 若默认 workbench state 不可写,保留制品跟随其临时回退 state 的scans目录,可能被 OS 临时目录清理删除 | 独立 temporary 保存/读取不创建也不要求持久集合 |
| 生命周期 | 属于完成结果,可被 canonical 引用 | 不属于完成结果,不得被 canonical findings 或 coverage 引用,可以独立于保留文件消失 |
| 已有扫描 | 保留既有扫描的已保存路径(含更早的临时路径) | —— |
server.ts中通过CONFIGURED_SCAN_ROOT = process.env.CODEX_SECURITY_SCAN_ROOT?.trim()与CONFIGURED_WORKBENCH_STATE_DIR = process.env.CODEX_SECURITY_STATE_DIR?.trim()(server.ts)读取这两条环境变量,印证了文档中的优先级描述。
一个容易踩坑的细节:相对存储覆盖(relative storage overrides)从插件目录解析,与 MCP 启动目录无关,这与 workbench 的行为保持一致。也就是说,若调用中出现相对路径的存储覆盖,它以插件根目录为基准,而不是以启动 MCP 时的 cwd 为基准。
调用契约与参数约束
- 准备目录:省略
path、content、sourcePath,工具会准备并返回所选目录;必须使用返回的directory,绝不自行构造或猜测物理路径。 - 保存文件:必须提供
path,且content与sourcePath恰好二选一("provide path and exactly one of content or sourcePath")。此校验在saveCodexSecurityArtifact开头强制执行(artifact-storage.ts)。 - 返回值:物理
path、canonicalrelativePath、内容摘要(SHA-256)。扫描证据引用(scan evidence references)中应使用持久相对路径。 - 读取:使用与保存时相同的身份(scanId 或 targetPath)、
storage与相对path;二进制内容必须显式请求encoding: "base64"。读取 schema 中encoding枚举为utf8/base64,默认utf8(artifact-storage.ts)。
运行中扫描的三个典型调用示例(原文示例,可直接照用):
save_codex_security_artifact({ scanId, handoffClaimToken?, storage: "persistent", path: "artifacts/01_context/threat_model.md", content: "<exact Markdown>" }) save_codex_security_artifact({ scanId, handoffClaimToken?, storage: "temporary" }) save_codex_security_artifact({ scanId, handoffClaimToken?, storage: "persistent", path: "artifacts/02_discovery/validation_artifacts/<candidate_id>/poc.bin", sourcePath: "<returned temporary directory>/poc.bin" })路径命名规则
path必须是artifacts/、findings/或hardening/下的可移植相对文件名(portable relative filename);report_validation.md同样受支持;独立集合(standalone collections)额外支持threat_model.md。每个阶段应沿用既有的相对制品布局(各阶段的目录约定见 scan-artifacts.md)。
源码层面的components校验(artifact-storage.ts)还做了跨平台防路径穿越:拒绝空段、.、..、Windows 非法字符<>:"\|?*、控制字符、以点或空格结尾的段,以及con/prn/aux/nul/com1-9/lpt1-9等保留设备名——保证「可移植相对路径」在 Windows 与 Unix 上都能安全落盘。
边界规则同样重要:源码/配置文件的编辑(source/configuration edits)与外部发布请求体(external-publication request bodies)不属于扫描制品,保留各自既有的工具与授权;显式用户指令仍然优先;如果所需输出目标无法由受管存储表示,应说明该限制,而不是静默替换为其他目标或谎称已写入。
生成证据与遗留辅助工具:temporary 暂存 + persistent 导入
PoC 执行和构建会在沙箱中产生文件,策略对这类「生成式证据」给出严格的两段式流程:
- 先准备
temporary存储:只在返回的临时工作区(或其可丢弃的仓库副本)中运行经授权的 build/test/generation 命令;PoC 的源文件/输入文件用 save 工具以storage: "temporary"编写。 - 再按需导入
persistent:最终记录中需要的每个文件,用storage: "persistent"+sourcePath(必须位于同一个返回的临时目录内)导入;生成 inventory、归一化输出或其他文件的工具必须接收临时输出路径。
两条红线:
- 导入实际字节,而不是模型对二进制输出或日志的转录(Import the actual bytes, not a model transcription)。二进制文件以 base64 方式读取、以源文件路径原样搬运。
- 没有正当理由不得保留整棵依赖/构建树(Do not retain entire dependency/build trees without a reason)。
在实现上,storageContext(artifact-storage.ts)在tmpdir()下以codex-security-artifacts-<sha256(context.root)>命名临时根;saveCodexSecurityArtifact的sourcePath导入分支要求storage === "persistent",并强制源文件必须解析到该上下文临时目录内部(relative()检查绝对路径或..逃逸即拒绝),随后读取实际字节写入目标。
对于独立/遗留的可变账本(legacy mutable ledgers,如 standalone 模式下的老式 ledger),应使用 save 工具替换其完整内容,或导入 helper 产出的临时文件,不得直接向保留的扫描目录追加。当前 Standard 与 Deep 扫描已不再引入遗留 inventory 或 ledger。
共享威胁模型缓存与已完成结果
共享威胁模型的读写规则
对于共享仓库威胁模型(shared repository threat model):
- 使用
targetPath: <repo_root>、storage: "persistent"、path: "threat_model.md"定位缓存集合; - 仅当威胁模型工作流允许缓存复用(cache reuse),且其精确的仓库/版本页脚(footer)匹配时,才读取该集合的缓存文件;
- 运行中的扫描应把选定的精确文本保存到
artifacts/01_context/threat_model.md,并使用该运行扫描的scanId; - 存储工具并不授权更新共享缓存——工作流中禁止读取或替换共享缓存的既有条件必须被保留。
每个共享威胁模型必须以恰好两行结尾:
Repository: <stable target identity from scan-contract.md> Version: <revision for an immutable Git tree; snapshot digest otherwise>其中<stable target identity>与修订/快照摘要的语义在 scan-contract.md 定义(git_worktree/directory_snapshot/git_diff/git_revision等目标种类与requiredSnapshotDigest/ revision 字段)。scan-artifacts.md同时规定:后续扫描阶段应以<context_dir>/threat_model.md为事实来源。
密封结果不可变
- 已完成/密封(completed/sealed)的扫描文件不能通过 save 工具编辑;后续的 write-up 或加固请求应使用独立目标集合(standalone target collection),并把返回的文件单独链接引用,保留原始结果及其引用不变。
- 临时清理不得删除保留文件或恢复检查点。
- save 工具在**与 finalization 相同的完成锁(completion lock)**下发布运行中扫描的文件;遇到已停止/密封扫描的拒绝(stopped/sealed-scan rejection)时要如实反馈,并保留既有输出。
对应地,createScanArtifactContext在requireRunning时对非 running 扫描直接抛出 "its artifacts cannot be modified",从机制上保证密封结果无法被改写;replaceArtifactText(artifact-io.ts)以同目录.tmp文件 +rename的方式原子替换,并在.lock文件上自旋等待(最多 500 次、每次 20ms),确保并发写入互斥,这也正是「完成锁」语义的底层支撑。
从源码看实现原理
1. 目录准备与根校验
所有制品操作都通过requireArtifactRoot(artifact-io.ts)校验:根必须是绝对路径、非符号链接、真实存在的常规目录,并realpath归一化。读写路径的每个组件都会经过validateArtifactComponents拒绝空串、.、..、斜杠与\0;文件读取时对每一级做lstat,拒绝符号链接与非常规文件,并二次确认realpath未逃逸绑定根("escaped its bound context")。这意味着即使模型传入恶意路径,也无法借助符号链接把读写引到扫描目录之外。
2. 上下文绑定与权限模型
ArtifactContext(artifact-io.ts)携带root、repoRoot、layout、scanId、handoffClaimToken、mode、targetContract等字段;createScanArtifactContext只从 workbench 的持久化扫描记录构造(host-bound),注释明确 "Never construct this object from model tool input."。注册工具时(compact-artifact-tools.ts),读工具标记readOnlyHint: true、idempotentHint: true,写工具标记readOnlyHint: false,且全部为modelOnlyMeta(仅模型可见),把「哪些工具可写、哪些只读」暴露给宿主。
3. 环境变量与根目录解析
server.ts在启动时读取CODEX_SECURITY_SCAN_ROOT与CODEX_SECURITY_STATE_DIR,resolve-scan-root等 workbench 命令无需数据库即可运行(WORKBENCH_COMMANDS_WITHOUT_DATABASE)。这与文档中「默认根$CODEX_SECURITY_STATE_DIR/scans→ 回退$CODEX_HOME/state/plugins/codex-security/scans→CODEX_SECURITY_SCAN_ROOT优先」的优先级描述一一对应。
测试验证:策略如何被证明
仓库测试从正反两面验证了上述契约:
- test_artifact_storage.mjs 验证:默认 scanDir 位于
state/scans/<target>下(持久)、temporary 目录位于tmpdir()下且不在scanDir 内;content精确字节往返(含尾部空格);二进制文件先写入 temporary 再以sourcePath导入 persistent,读取时encoding: "base64"得到一致字节;目录外文件(outside.txt)被拒绝导入。 - test_artifact_storage_regressions.mjs 针对
CODEX_SECURITY_SCAN_ROOT/CODEX_SECURITY_STATE_DIR/CODEX_HOME三者的覆盖优先级与回退行为做回归,验证了文档中根目录解析与临时回退 state 的规则。
这些测试与策略文档互相印证:先取权威 scanId、显式选 storage、用 save 工具写入、从返回的 temporary 目录导入、引用 persistent 相对路径、绝不触碰密封结果,是插件托管扫描与独立阶段产出可信、可追溯制品的完整闭环。对于更完整的路径布局(artifacts_dir、01_context、02_discovery、03_coverage、04_reconciliation、05_findings及最终report.md/hardening/等),可继续阅读 scan-artifacts.md 与 scan-contract.md。
- 应用安全
- 漏洞扫描
- AI 应用
【免费下载链接】codex-security
OpenAI's Codex Security CLI and TypeScript SDK for finding, validating, and fixing security vulnerabilities. npm: https://www.npmjs.com/package/@openai/codex-security
相关推荐
Codex Security 扫描契约(Sealed Scan Contract)权威解读:不可变扫描产物、Manifest 语义与目标快照规范
Codex Security 扫描契约(Sealed Scan Contract)权威解读:不可变扫描产物、Manifest 语义与目标快照规范 导读 本文以
应用安全漏洞扫描AI 应用Label Studio 数据管理详解:导入、导出与存储策略
Label Studio 数据管理详解:导入、导出与存储策略 Label Studio 作为一款多类型数据标注工具,其高效的数据管理能力是提升标注效率的核心。本
数据标注人工智能Azure Linux合规性证据管理:存储与检索策略
Azure Linux合规性证据管理:存储与检索策略 在当今云计算环境中,合规性证据管理已成为企业数据治理的关键环节。Azure Linux作为面向Azure
操作系统云原生容器
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考