ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

codex-security Native 原语层解析:Rust 实现 Node 缺失的 OS 操作与跨平台分发验证

codex-security Native 原语层解析:Rust 实现 Node 缺失的 OS 操作与跨平台分发验证 应用安全漏洞扫描AI 应用【免费下载链接】codex-securityOpenAIs Codex Security CLI and TypeScript SDK for finding, validating, and fixing security vulnerabilities. npm: https://www.npmjs.com/package/openai/codex-security项目地址https://gitcode.com/gh_mirrors/co/codex-security点击查看免费下载导读本篇文章围绕 OpenAI Codex Security 项目中 plugins/codex-security/native/README.md 所描述的 Native OS primitives 模块展开它是整个 codex-security 插件在 Unix 与 Windows 上安全处理文件系统路径、账户解析与文件锁的底层基石。文章将深入讲解这组 Node-API 8 原生绑定的设计动机、九个 Unix 核心函数与 Windows 句柄模型、字节级路径语义、EINTR 重试约定以及从本地构建、行为证明proof到分发门槛检查与 CI 打包的完整工程化流程。读完本文你将掌握这套跨平台原生层的调用契约、验证方式和发布链路并能直接复现其构建与测试命令。背景为什么 Node 需要一套原生 OS 原语Node.js 的标准库并没有暴露全部操作系统能力。在 codex-security 中resolve-security-md这个辅助工具负责解析仓库内所有SECURITY.md安全策略文件并拼接为扫描上下文见 resolve-security-md.ts它需要完成 Unix 上的原生账户查找~user形式的 home 目录展开以及 Windows 上不受 Node 高层 API 限制的路径、文件和目录操作。这些需求正是 native/README.md 中定义的绑定层的来源These bindings supply OS operations that Node does not expose. Theresolve-security-mdhelper uses native account lookup on Unix and native path, file, and directory operations on Windows.从实现结构看整个原生层是一个 Rust crate、两个平台后端Cargo.toml 声明了 cratecodex-security-native以cdylib形式编译依赖napi 3.12.2启用napi8feature与napi-derive 3.6.3Unix 侧额外使用libc 0.2.189Windows 侧使用windows-sys 0.61.2src/lib.rs 仅通过#[cfg(unix)]/#[cfg(windows)]分别引入 src/unix.rs 与 src/windows.rs发布配置profile.release开启strip true、lto true、codegen-units 1保证产物精简且优化充分。Unix 绑定九个 Node-API 8 函数的设计契约README 明确说明“The nine Node-API 8 functions are typed inbinding.mts”这九个函数的完整签名定义在 binding.mts 的UnixBinding接口中函数签名底层实现openAt(directory, name, flags, mode) { value, errno }libc::openatO_CLOEXEC自动重试 EINTRduplicate(descriptor) { value, errno }fcntl(F_DUPFD_CLOEXEC)复制描述符并置 close-on-execmakeDirectoryAt(directory, name, mode) { value, errno }libc::mkdiratrenameAt(oldDir, oldName, newDir, newName) { value, errno }libc::renameatunlinkAt(directory, name) { value, errno }libc::unlinkatstatAt(directory, name) { errno, mode, device, inode }fstatatAT_SYMLINK_NOFOLLOWreadLinkAt(directory, name) { errno, value }readlinkat缓冲区不足时倍增扩容fileLock(descriptor, unlock, nonblocking) { value, errno }flock(LOCK_EX / LOCK_UN / LOCK_NB)自动重试 EINTRuserHome(username) { errno, value }getpwnam_rERANGE时倍增缓冲区重试路径保持为不解释的 POSIX 字节README 强调“Paths remain byte buffers”路径始终保持为字节缓冲区。在 unix.rs 中所有名称参数都以napi::bindgen_prelude::Buffer接收再通过CString::new校验并转换——唯一拒绝的情形是路径中包含 NUL 字节Path contains a NUL byte。这意味着文件名不需要是合法 UTF-8可以携带任意不可解码字节例如 Linux 上的0xff等字节序列往返过程零解码零编码规避了 JavaScript 字符串在路径边界上的语义损耗proof.mts 的fixtureName刻意区分平台macOSAPFS 要求合法 UTF-8使用prefix-é而 Linux 直接构造Buffer.from([prefix.charCodeAt(0), byte])来演练不可解码文件名。statAt不跟随最终符号链接大整数用十进制字符串statAt底层使用fstatat(..., AT_SYMLINK_NOFOLLOW)因此对符号链接本身返回链接的modeS_IFLNK而不是目标文件的元数据——这是安全扫描场景中识别链接、拒绝越界遍历的关键。由于st_dev与st_ino在 64 位平台上可能超出 JavaScript 的精确整数范围Rust 侧将它们序列化为十进制字符串返回(stat.st_dev as u64).to_string()避免 JS 端Number舍入造成身份比较失真。Windows 侧的卷序列号与文件位置同样遵循“十进制字符串”约定见下文。openAt与duplicate强制 close-on-exec两个创建描述符的入口都保证描述符不会泄漏到后续exec的子进程中open_at在 flags 中强制并入O_CLOEXECduplicate使用F_DUPFD_CLOEXEC。这确保了扫描过程中派生的外部进程无法继承这些已锚定的文件描述符。EINTR 重试策略只有两个例外README 特别约定“openAtandfileLockretry EINTR, matching the current Python helpers. Other operations return their native errno.” 在 unix.rs 中通过retry_eintr闭包实现循环调用直到 errno 不是EINTR。其余操作mkdirat、renameat、unlinkat、statAt、readLinkAt直接返回原生 errno由调用方决定处理策略。与此同时Node 侧的 binding.mts 提供了readDescriptor对fs.readSync捕获EINTR异常后原地重试并且保留调用者之前已经读入的字节Retry the interrupted read, preserving the callers previously read bytes避免一次被信号中断的读取丢失已落盘的数据。userHome不经 Git、不依赖HOME环境变量的账户解析user_home通过getpwnam_r读取系统账户数据库返回pw_dir的原始字节当用户名不存在时返回errno与value: null。这一点对resolve-security-md至关重要它要展开~someone/...形式的扫描路径见 resolve-security-md.ts 中的expandHome必须查真实账户而不是 Git 配置。proof 中accountProof还会断言当前用户 home 与os.userInfo()一致容器内无账户条目时允许ERR_SYSTEM_ERROR/ENOENT、root存在、随机不存在的用户名返回null、包含 NUL 的用户名直接抛错。阻塞锁与主事件循环进程生命周期释放README 给出两条重要使用约束阻塞锁必须在主 JavaScript 事件循环之外运行fileLock的阻塞调用会挂起线程若在事件循环线程内执行会阻塞整个 Node 进程因此 proof.mts 中所有阻塞加锁场景都通过子进程nativeLockWorker完成持有锁的进程在关闭或退出时自动释放flock的语义保证进程死亡后内核释放锁proof 中专门验证了“持有者被 SIGKILL 后等待方能够拿到锁”peerDeathReleasesLock/nativeDeathReleasesLock。README 还提到“A Python signal handler can raise during a blocked call, so later routing must preserve cancellation through the worker lifecycle”即 Python 侧信号处理器可能在阻塞调用期间抛异常后续的路由逻辑必须把这种取消状态贯穿 worker 生命周期——这解释了为什么锁的争用、解锁、进程死亡交接都通过标准输入输出协议在子进程之间编排。Windows 绑定WindowsHandle与 UTF-16LE 边界Windows 侧是另一套完全不同的模型定义在 windows-binding.mts 与 src/windows.rs句柄所有权RustFile独占WindowsHandle内部持有OptionFileRust 标准库文件对象句柄不可继承且从不进入 Node 的 CRT 描述符表。释放途径有两个显式调用close()幂等self.file.take()后 drop垃圾回收兜底napi对象析构时同样 drop。因此 proof 使用--expose-gc参数显式触发 GC 来验证句柄生命周期。路径与名称无 NUL 终止符的 UTF-16LE 缓冲区Windows 侧所有路径参数与返回名称都是 UTF-16LE 编码的Buffer不携带 NUL 终止符并且完整保留孤立代理项lone surrogates——这是为了能原样表示 Windows 文件系统中任何合法文件名即使它不是合法 Unicode。wide_path只做两项校验字节长度必须是偶数整 code unit以及不含 NUL code unit。文件操作面与错误码README 列出的能力与 windows.rs 一一对应创建createWindowsDirectoryCreateDirectoryW与createWindowsDirectoriesfs::create_dir_all属性与重解析标签attributes()通过GetFileInformationByHandleEx(FileAttributeTagInfo)拿到FileAttributes与ReparseTag身份与名称identity()返回十进制字符串的卷序列号 128 位文件 ID 的原始缓冲区finalPath(flags)调用GetFinalPathNameByHandleW并自动扩容缓冲区读写与游标read/write通过 RustFile的Read/Writetrait 完成seek接受 64 位 BigInt 偏移与FILE_BEGIN/FILE_CURRENT/FILE_END三种 originsize用GetFileSizeExsetEndOfFile先取stream_position再set_len游标保留式截断flush对应sync_all精确句柄重命名与删除rename(destination, replace)构造FILE_RENAME_INFO含ReplaceIfExistssetDisposition(true)标记删除独占整文件锁lock(nonblocking)/unlock()映射到 Rust 的File::lock/try_lock/unlock。错误统一返回数字 Windows 错误码README 明确给出两个例子6ERROR_INVALID_HANDLE已关闭句柄与33ERROR_LOCK_VIOLATION非阻塞锁争用。open_windows_file还会拒绝FILE_FLAG_OVERLAPPED——因为挂起的重叠 I/O 可能在同步调用返回后仍持有原生缓冲区。四个在 Node 边界保留字符串的操作除文件句柄操作外windows.rs 还提供了四个字符串级操作README 对它们逐一描述函数行为windowsArguments返回完整 OS 参数向量含可执行文件与 Node 选项使用 Rust CRT 兼容解析器std::env::args_oswindowsEnvironment(name)读取单个宽环境变量区分“不存在”返回null与“空缓冲区”windowsAbsolutePath(path)基于原生当前目录与驱动器目录解析绝对路径std::path::absolute/GetFullPathNameW不要求目标已存在windowsDirectoryEntries(path)用std::fs::read_dir 缓存的DirEntry::file_type()枚举目录不逐个打开子项名称保持 UTF-16LE构造或迭代失败返回数字错误与空数组其中目录条目同时报告is_directory与is_symbolic_link两个标志目录符号链接与 junction 两者兼有通过entriesWithTypes暴露给resolve-security-md --listWindows 上的目录遍历入口见 resolve-security-md.ts。路径归一化与重解析点策略windows-files.mts将普通绝对路径解析与规范化委托给GetFullPathNameW和GetFinalPathNameByHandleW仅在根目录以下裁剪尾部分隔符它的小型 verbatim 路径归一化器在处理点段./..时保留盘符与 UNC 共享根包括字面尾随点与空格。stat(path, false)保留精确的符号链接/重解析点元数据使调用方可以独立于枚举器的链接标签来拒绝 junction 遍历。README 强调路径授权、祖先遍历与重解析点策略仍然是调用方而非原生层的责任。本地构建与行为证明一条命令链README 给出了从仓库根目录运行的完整开发流程需先安装固定的 Rust 工具链与现有 TypeScript 依赖pnpm --dir sdk/typescript install --frozen-lockfile pnpm --dir sdk/typescript run build:ci node plugins/codex-security/native/build.mjs node plugins/codex-security/native/proof.mjs cargo 1.97.1 fmt --check --manifest-path plugins/codex-security/native/Cargo.toml cargo 1.97.1 clippy --locked --manifest-path plugins/codex-security/native/Cargo.toml -- -D warnings注意 Rust 工具链被固定为1.97.1rust-toolchain.toml 与 Cargo.toml 的rust-version 1.97双重约束clippy 强制-D warnings。proof.mjs即 proof.mts不依赖 Python它通过loadBinding()加载当前平台产物覆盖 README 列出的全部场景目录替换rename 后锚定 FD 仍指向原 inode、字节路径含不可解码文件名、不可读文件的元数据读取mode 000 也能 stat、超长原始符号链接80 层component/拼接、描述符复制duplicate 后原 FD 可关闭、Node 描述符 I/O写、fsync、fstat、读回、账户查找、锁争用、解锁交接与进程死亡释放。proof 还会把结果以 JSON 形式输出node,platform,architecture,nodeApi: 8等便于 CI 断言。构建产物位于被忽略的target与dist目录。Linux 输出目录按 C 运行时细分linux-x64-gnu、linux-arm64-gnu、linux-x64-musl、linux-arm64-musl。目标目录的推导逻辑在 platform.mts通过process.report.getReport()的header.glibcVersionRuntime是否存在来区分 glibc 与 musl——纯 Node 实现、零子进程。分发门槛检查check.mjs的硬性指标任何产物上传前都必须运行node plugins/codex-security/native/check.mjscheck.mts 对三类平台各设硬性门槛平台门槛GNU Linux编译前重映射源码、Cargo registry 与编译器路径产物字节中不得包含私有构建路径标记/Users/、/home/dev-user等同时检查 UTF-8 与 UTF-16LE 两种编码readelf检查不得引入比GLIBC 2.28更新的符号版本musl Linux必须是当前架构的 ELF 镜像\x7fELF、EI_CLASS2、machine 字段校验依赖对应架构的libc.musl-*.so.1且不能有任何来自 glibc 的符号版本要求libgcc_s.so.1自身的GLIBC_2.0兼容导出被单独豁免macOSotool -l解析LC_BUILD_VERSION/LC_VERSION_MIN_MACOSX部署目标必须≤ 11.0Windows校验MZPE\0\0头与机器类型x640x8664/ arm640xaa64README 特别提醒从较新的 GNU Linux 工作站构建的产物可能通过行为 proof 却在分发检查中失败因为本机链接器引入的 glibc 版本可能高于 2.28。musl 没有 glibc 式符号版本下限因此其运行时兼容性还要依赖后续的 Node 加载 proof。CI 工作流三个平台流水线 产物合并README 描述了三条原生构建流水线native-unix在 digest-pinned 的 manylinux 2.28 镜像中构建 Linux 产物挂载固定 Rust 工具链与已拉取的 Cargo registry、离线编译并在编译期间封锁 Python 命令macOS 构建设置MACOSX_DEPLOYMENT_TARGET11.0CI 在 Node 20.0.0 与 22.13.0 两个版本上分别验证 x64 与 arm64 产物。native-musl使用原生 x64/arm64 Ubuntu worker digest-pinned 的 Rust 1.97.1 Alpine 编译器镜像禁用静态 CRT 链接让 Node 能够加载共享库ELF 与私有路径检查通过后每个未变更产物在 pinned 的 Node 20.0.0 Alpine 3.17 与 Node 22.13.0 Alpine 3.21 镜像中运行完整 proof对应 musl 1.2.3 与 1.2.5运行时容器只读挂载源码与产物无 Pythonproof 进程 PATH 为空。native-windows以 MSVC 静态 CRT 构建 x64 与 arm64检查 PE 架构与私有路径后在同一产物上以 Node 22.13.0 与 20.0.0、空 PATH 运行 proofproof 覆盖句柄生命周期与 GC、祖先替换、junction、精确句柄操作、原始 UTF-16 与长路径、数字错误码、整文件锁与释放阻塞锁在子进程中执行另有一次 Node 22 调用会使用 runner 的 Python 与既有msvcrt字节零锁做双向对比争用、解锁、关闭、进程死亡释放Python 仅是可选迁移 oraclenode --expose-gc plugins/codex-security/native/proof-windows.mjs python plugins/codex-security/scriptsWindows 构建还会额外编译测试专用的windows-wide-launcherRust 示例它以孤立代理项启动一个 Node proof 子进程参数、环境变量、工作目录均含子进程验证完整目录迭代、孤立代理项与替换字符文件的区分、规范化路径、有界读取、输出截断以及通过 typed adapter 的递归长路径Rust 侧一个禁止共享的文件 guard 保持打开子进程枚举其名称时显式数据读取必须报共享冲突而仅属性访问不受 Windows 文件共享阻止。该 launcher 会清理宽字符夹具且永不进入上传或打包的原生负载。创建文件/目录符号链接需要 Windows 开发者模式或符号链接特权缺失时 proof 仍运行其余断言含目录 junction并在 JSON 输出中将跳过的符号链接断言标为false。CI 两个架构都要求真实符号链接同时强制受限场景以覆盖两条路径。打包输入host 与 universal 两档分发README 的 “Package inputs” 章节给出插件独立构建流程pnpm --dir plugins/codex-security/mcp-app install --frozen-lockfile node plugins/codex-security/mcp-app/scripts/build_native.mjs node plugins/codex-security/mcp-app/scripts/build_mcp_app.mjs --output plugins/codex-security/mcp --native hostbuild_native.mjs复用 MCP app 的依赖编译 TypeScript 工具、拉取锁定版本的 Cargo 依赖并把宿主二进制与许可证声明写入native/dist--native host只打包当前平台与架构的文件到mcp/目录插件 launcher 的预期位置CI 在 Linux、macOS、Windows 上测试此构建无需 SDK插件与 npm 发布则使用默认的--native universal它要求native/prebuilt中齐备全部八个已验证二进制Linux gnu/musl × x64/arm64、macOS x64/arm64、Windows x64/arm64。native-artifacts工作流汇总三个平台流水线的八个已验证负载合并为native-universal-commit单一产物PR 校验任务共享node-ci组装的一份产物发布与独立校验运行各自组装。默认情况下独立 MCP builder 与 npm 包包含同一份完整mcp/native目录树运行时既不编译也不下载代码。GNU x64 任务还会对锁定的 Cargo 元数据运行notices.mjs收集各 crate 许可证与固定 Rust 标准库声明覆盖两个包面。由于 NAPI crates 的 registry 归档不附带许可证文件licenses/napi.txt保留了其固定的上游许可证文本升级这些依赖时需复查该覆盖文件。universal 构建、SDK 测试或 Docker 构建需要下载某个成功运行所产出的工件README 提示可在已推送分支上手动运行native-artifactsgh run download run-id --name native-universal-commit --dir plugins/codex-security/native/prebuilt被忽略的prebuilt目录必须包含全部八个平台目录与共享声明更改原生源码或构建工具链后需刷新它缺失负载会让构建失败即使宿主只加载其中一个已安装包的检查会以空PATH加载匹配的工件。与既有 Python 实现的迁移对齐整套原生层并非从零发明而是与仓库内已有的 Python 辅助脚本对齐并逐步迁移。README 提供了一条可选的双向对比命令node plugins/codex-security/native/proof.mjs python3 plugins/codex-security/scriptsproof.mts 中的pythonOracle直接 import 既有workbench_db.py的acquire_completion_file_lock/release_completion_file_lock/posix_file_lock在同样的锁文件上验证四个方向Node 持有、Python 探测争用期望EAGAIN/EWOULDBLOCKPython 持有、Node 等待并在解锁后获得以及进程死亡后的锁交接。README 明确标注这段协议只是“temporary interoperability oracle”临时互操作对照永不复用为迁移后的实现或发布工件。这也印证了原生层的目标在flock语义上与既有 Python 行为严格一致同时把扫描路径处理从 Python 迁移到 Rust Node 的字节安全模型上。总结从原语到发布的一体化工程质量回顾整条链路codex-security 的 Native OS primitives 模块体现了几个值得借鉴的工程决策边界最小化Node 不暴露的才进原生层路径以字节/UTF-16LE 原样传递杜绝编码转换引入的安全缝隙语义可证明九个 Unix 函数、Windows 句柄模型、锁的进程生命周期全部由不依赖 Python 的 proof 程序在真实文件系统上逐项断言分发可审计私有路径字节检查、glibc 2.28 / musl / macOS 11.0 / PE 架构四类硬门槛配合 digest-pinned 的 CI 镜像与空PATH运行保证“行为正确”与“分布兼容”双重达标迁移有对照以既有 Python 实现为 oracle 做双向验证为渐进替换提供可回退的安全网。对于希望在扫描类工具中引入安全文件系统原语的开发者native/README.md 及其配套的 binding.mts、src/unix.rs、src/windows.rs、check.mts 与 proof.mts 构成了一套完整、可直接复现的参考实现。赞分享应用安全漏洞扫描AI 应用【免费下载链接】codex-securityOpenAIs Codex Security CLI and TypeScript SDK for finding, validating, and fixing security vulnerabilities. npm: https://www.npmjs.com/package/openai/codex-security项目地址https://gitcode.com/gh_mirrors/co/codex-security点击查看免费下载相关推荐Android平台Rust开发AOSP集成与跨语言互操作Android平台Rust开发AOSP集成与跨语言互操作 本文详细介绍了在Android平台上进行Rust开发的全套技术方案包括开发环境搭建与配置、AIDL文档教程QMK Firmware GPIO 抽象层解析跨平台引脚控制宏、底层实现与 ATOMIC_BLOCK_FORCEON 原子操作QMK Firmware GPIO 抽象层解析跨平台引脚控制宏、底层实现与 ATOMIC_BLOCK_FORCEON 原子操作 在 QMK 键盘固件中GPI人工智能大模型Agent 记忆AI AgentRAG知识图谱dsh-pluginGBrain Native Writer Locks 深度解析跨平台进程互斥锁的原生 Node-API 实现与可复现构建GBrain Native Writer Locks 深度解析跨平台进程互斥锁的原生 Node API 实现与可复现构建 本文以 gbrain 仓库中 nat人工智能RAGAgent 记忆MCP 服务知识管理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表