Skip to content

Harden workspace and reporting security boundaries - #6

Merged
y4ho0 merged 3 commits into
mainfrom
codex/workspace-security-hardening
Aug 12, 2026
Merged

Harden workspace and reporting security boundaries#6
y4ho0 merged 3 commits into
mainfrom
codex/workspace-security-hardening

Conversation

@y4ho0

@y4ho0 y4ho0 commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • close workspace-copy escapes through intermediate and final symlinks, unsafe workspace names, replaced workspace roots, and file-oracle paths
  • preflight byte, entry, and path-storage budgets; preserve Unix Git path bytes; stream source fingerprinting instead of reading whole files into memory
  • centralize environment and CLI secret redaction across dry-run, prepare, verbose output, terminal output, JSON, Markdown, JUnit, and saved-report regeneration
  • harden saved-report paths, canonical run IDs, Schema v1 loading, symlink handling, staged writes, control-character rendering, and report-size limits
  • keep 15 English and Simplified Chinese documentation pairs, examples, and schemas aligned

Security findings addressed

Filesystem and workspace boundaries

  • git-clean include_untracked could follow an intermediate directory symlink outside the repository
  • workspace names and report run IDs were not constrained to one portable normal path component
  • prepare commands could replace the copied workspace root before later execution
  • file oracles and saved-report regeneration could cross symlink or path boundaries
  • copy budgets were enforced after mutation instead of through a complete preflight
  • non-UTF-8 Git paths and whole-file fingerprint reads created integrity and resource-limit gaps

Sensitive-data handling

  • CLI-only tokens, passwords, API keys, configured short-option values, and short sensitive environment values could reach output or persisted reports
  • secrets could re-enter through project paths, configuration errors, Oracle details, historical reports, or opaque Shell commands
  • saved report strings could inject terminal control sequences or invalid XML characters
  • repeated literal replacement could expand output or consume excessive CPU

Saved-report trust boundary

  • report paths could escape through unsafe run IDs or symlinked .assumezero directories
  • loaded reports did not strictly enforce the report-v1 contract
  • generated Markdown and JUnit artifacts did not share the JSON size boundary
  • historical opaque Shell reports could not be safely re-redacted

Design

  • canonicalize repository and workspace roots and apply path-component containment rather than string-prefix checks
  • reject absolute paths, parent traversal, nested names, drive-prefixed paths, UNC-like paths, external symlinks, broken symlinks, and replaced workspace roots
  • preflight eligible files and metadata before workspace mutation, then recheck sources while copying
  • use one centralized redaction policy with leftmost-longest Aho-Corasick matching
  • bound literal redaction rules to 2,048 entries and 256 KiB; fail closed when the budget is exceeded
  • redact both HOME and USERPROFILE; report.redact_home = false is rejected
  • reject single-string opaque Shell commands and saved Shell wrappers when their secrets cannot be reconstructed safely
  • validate exact Schema v1 saved-report structures without echoing untrusted values
  • enforce a 64 MiB limit for loaded JSON and every generated JSON, Markdown, or JUnit artifact

Verification

Local verification on macOS/aarch64:

  • cargo fmt --all -- --check
  • cargo check --locked --all-targets --all-features
  • cargo clippy --locked --all-targets --all-features -- -D warnings
  • cargo test --locked --all-features — 76 unit, 20 end-to-end, and 1 release-consistency test passed
  • cargo +1.80.0 check --locked --all-targets --all-features
  • cargo build --locked --release
  • ./scripts/validate-schemas.sh
  • python3 scripts/check-docs.py — 15 bilingual document pairs and all local links passed
  • cargo audit --no-fetch --no-yanked
  • credential, private-key, local-user-path, and persisted-report scans

Final GitHub verification for commit 86bddccae77011cd4ce3b3214c30a2e4edec8974:

  • CI matrix: Windows, Ubuntu, macOS, Rust 1.80, JSON Schema, documentation localization, and install smoke test passed
  • Dependency audit: passed

An initial Windows rerun exposed a Unix-only path in a regression-test fixture. The fixture was made platform-native without changing production behavior, and the complete Windows test and Release build then passed.

Compatibility and scope

  • no dependency versions were upgraded
  • aho-corasick 1.1.4, already present in the lockfile transitively, is now declared as a direct dependency for bounded multi-pattern redaction
  • loading malformed, extended, or unsupported saved report-v1 structures now fails closed
  • report.redact_home = false and single-string opaque Shell commands are now rejected
  • existing English CLI commands, option names, schemas, and report field names remain the stable runtime interface
  • complete Simplified Chinese documentation is included; CLI and report-renderer localization is outside this security PR
  • no Dependabot PRs were merged
  • no changes were pushed to main
  • this PR is ready for review; no merge, tag, release, or crates.io publication has been performed

Residual risks

  • canonicalization and metadata checks narrow but cannot eliminate same-user TOCTOU races
  • raw command arguments remain visible to the operating system while a child process runs
  • transformed, fragmented, or undeclared secrets can evade exact-value redaction
  • report-v1 has no trusted Shell provenance, so saved sh -c and Windows /D /S /C wrappers are rejected conservatively
  • the 64 MiB artifact limit is checked after rendering, so a malicious but structurally valid report can still cause bounded temporary memory amplification
  • Unicode bidirectional formatting characters are not normalized

中文摘要

本 PR 完成一次以安全边界为优先的 Rust 工程加固:

  • 修复工作区复制经中间或最终符号链接访问仓库外文件的问题
  • 对工作区名称、报告 Run ID、文件 Oracle 和报告路径实施可移植的路径组件校验
  • 在复制前完成文件数量、总字节数和路径存储预算预检
  • 防止 prepare 命令替换工作区根目录后在外部路径执行后续命令
  • 集中脱敏环境变量、CLI Token、密码、API Key、短选项、HOME/USERPROFILE 及历史报告内容
  • 对脱敏规则数量和总长度设置上限,超限时 fail-closed
  • 拒绝无法可靠恢复秘密边界的单字符串 Shell 命令和历史 Shell wrapper
  • 严格验证 report-v1、canonical ULID、报告目录和所有生成格式的 64 MiB 上限
  • 将终端控制字符转换为可见文本,并保证 JUnit XML 1.0 合法
  • 保持 15 对英文与简体中文文档、示例和 Schema 一致

最终验证结果:

  • 76 个单元测试、20 个端到端测试和 1 个发布一致性测试全部通过
  • Format、Check、Clippy 零警告、Rust 1.80 和 Release build 全部通过
  • Windows、Ubuntu、macOS、Schema、中文文档、安装和依赖审计全部通过
  • 未合并 Dependabot PR,未推送 main,未执行合并或发布

剩余风险主要是同用户 TOCTOU 竞争窗口、操作系统运行期间可见的原始参数、经过变换或分片的秘密,以及 report-v1 缺少可信 Shell 来源信息。

@y4ho0
y4ho0 marked this pull request as ready for review August 12, 2026 18:38
@y4ho0
y4ho0 merged commit 004c8d0 into main Aug 12, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant