Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ All notable changes are documented here. The format follows Keep a Changelog, an

- Complete Simplified Chinese documentation with bidirectional navigation and automated coverage/link checks.

### Security

- Reject workspace paths that escape through intermediate symlinks, unsafe workspace names, destination ancestors, or file-Oracle symlinks.
- Reject report run-ID traversal and report-root/output symlink escapes; stage report files before publication, cap all generated artifacts at 64 MiB, and apply the same limit when loading JSON reports.
- Preflight workspace byte and entry budgets before destination mutation, preserve Git path bytes on Unix, and stream source fingerprinting.
- Frame raw platform path bytes in source fingerprints and persist Git status as a SHA-256 digest; integrity hashes are version-specific rather than cross-version identifiers.
- Redact sensitive environment values of any non-empty length, `HOME`/`USERPROFILE` paths, and recognized or configured CLI option values consistently across displays and persisted evidence, including configured attached short forms such as `-pVALUE`.
- Require `report.redact_home = true` in configuration v1 so the documented home-path privacy boundary cannot be disabled accidentally.
- Revalidate saved report-v1 structure before rendering, reject unknown fields without echoing parser details, and reapply command-derived redaction to legacy report metadata during regeneration.
- Refuse single-string opaque shell scripts and fail closed on regeneration of saved Shell-wrapper reports when historical output cannot be redacted safely.
- Use bounded multi-pattern literal redaction and render control characters visibly in terminal, Markdown, and valid XML evidence.
- Bound `deep_path_length` to 240 bytes so generated workspace names remain one portable component; configurations above that limit now fail validation.

## [0.1.0] - 2026-07-27

### Added
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@

- 完整的简体中文文档、双向导航,以及自动化覆盖范围/链接检查。

### 安全

- 拒绝经中间符号链接、不安全工作区名称、目标祖先或文件 Oracle 符号链接逃逸的工作区路径。
- 拒绝报告运行 ID 穿越及报告根目录/输出文件符号链接逃逸,在发布前暂存报告文件,把所有生成产物限制为 64 MiB,并在读取 JSON 报告时应用相同上限。
- 在修改目标前预检工作区字节和条目预算,在 Unix 上保真处理 Git 路径字节,并以流式方式计算源码指纹。
- 在源码指纹中对平台原始路径字节进行分帧,并以 SHA-256 摘要持久化 Git 状态;完整性哈希与版本相关,不是跨版本标识符。
- 在显示和持久化证据中一致脱敏任意非空长度的敏感环境值、`HOME`/`USERPROFILE` 路径,以及已识别或已配置的 CLI 选项值,包括 `-pVALUE` 这类已配置短选项紧贴形式。
- 配置 v1 要求 `report.redact_home = true`,防止意外关闭文档承诺的主目录路径隐私边界。
- 渲染前重新校验已保存 report-v1 的结构,在不回显解析器细节的情况下拒绝未知字段,并在重新生成时对旧报告元数据再次应用由命令值驱动的脱敏。
- 当只存在于 CLI 的值无法从证据中安全脱敏时,拒绝单字符串不透明 Shell 脚本,并在重新生成已保存的 Shell-wrapper 报告时 fail-closed。
- 使用有界多模式字面量脱敏,并在终端、Markdown 和有效 XML 证据中把控制字符显示为可见形式。
- 将 `deep_path_length` 限制为 240 字节,使生成的工作区名称保持为单一可移植组件;超过该限制的既有配置现在会校验失败。

## [0.1.0] - 2026-07-27

### 新增
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ default = []
test-fixtures = []

[dependencies]
aho-corasick = "1.1"
anyhow = "1.0"
clap = { version = "=4.5.23", features = ["derive"] }
ctrlc = "3.4"
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Scenarios:
0 skipped
0 inconclusive/infrastructure

Secret values persisted: no environment values are report fields
Secret values persisted: no recognized environment or CLI secret values are report fields
Source workspace unchanged: yes
```

Expand Down Expand Up @@ -90,7 +90,7 @@ AssumeZero always prints the final command it selected. Arguments after `--` ove
| AZ-S004 | `MINIMAL_PATH` | deep | Keeps the top-level command directory, system essentials, and explicit entries |
| AZ-S005 | `SPACE_WORKDIR` | ✓ | Uses a copied path containing multiple spaces |
| AZ-S006 | `UNICODE_WORKDIR` | ✓ | Uses a copied path containing Unicode |
| AZ-S007 | `DEEP_WORKDIR` | ✓ | Uses a safely bounded deep path |
| AZ-S007 | `DEEP_WORKDIR` | ✓ | Uses a safely bounded long path component |
| AZ-S008 | `REDIRECTED_TEMP` | ✓ | Redirects `TMP`, `TEMP`, and `TMPDIR` |
| AZ-S009 | `TIMEZONE_UTC` | deep | Sets process-level `TZ=UTC` on supported platforms; best effort |
| AZ-S010 | `LOCALE_C` | deep | Sets `LANG=C` and `LC_ALL=C` when the locale exists |
Expand Down Expand Up @@ -130,6 +130,7 @@ confirm_failures = 2
[workspace]
mode = "working-tree"
max_size_mib = 2048
max_entries = 100000
exclude = [".git", ".assumezero"]
include_untracked = []

Expand All @@ -146,6 +147,7 @@ max_total_seconds = 1800

[report]
formats = ["terminal", "json", "markdown"]
sensitive_options = [] # For ambiguous short secret flags, for example ["-p"]
```

Unknown fields fail validation. Output-text, regular-expression, required-file, and forbidden-file oracle conditions are documented in [PRODUCT_SPEC.md](docs/PRODUCT_SPEC.md). The machine-readable format is [config-v1.schema.json](schemas/config-v1.schema.json).
Expand Down Expand Up @@ -173,7 +175,7 @@ Verified fixture transcripts for hidden environment variables, hidden child tool

## Privacy

AssumeZero itself does not upload files, call external APIs, send telemetry, inspect the contents of the real home directory, or persist environment-variable values. Sensitive environment values are used only in memory to redact command output before it is written. Reports contain names, presence/classification metadata, and redacted output summaries.
AssumeZero itself does not upload files, call external APIs, send telemetry, inspect the contents of the real home directory, or persist recognized secret values. Sensitive environment values and values of recognized long CLI options such as `--token`, `--password`, and `--api-key` are used only in memory to redact command/output evidence before it is written. Configure ambiguous short or custom options in `report.sensitive_options`; a configured single-character short option covers `-p value`, `-p=value`, and `-pVALUE`. Reports contain names, presence/classification metadata, and redacted output summaries.

User-provided preparation and tested commands can still access the network and other resources available to the current user. Redaction is defense in depth; pattern matching can have both false positives and false negatives.

Expand All @@ -183,7 +185,7 @@ User-provided preparation and tested commands can still access the network and o

The tested command never has the source project as its working directory, but it retains the current user's operating-system privileges. It can deliberately reach outside its copied workspace. Do not use AssumeZero to run untrusted code. Shell parsing is disabled unless `--shell` is explicitly selected, in which case a warning is shown.

External symlinks are refused by default without reading their targets. Process-tree termination on timeout or interruption is best effort and cannot be guaranteed on every platform.
External symlinks are refused by default after resolving their targets for containment, without reading target file contents. Process-tree termination on timeout or interruption is best effort and cannot be guaranteed on every platform.

## Platform support

Expand Down
10 changes: 6 additions & 4 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Scenarios:
0 skipped
0 inconclusive/infrastructure

Secret values persisted: no environment values are report fields
Secret values persisted: no recognized environment or CLI secret values are report fields
Source workspace unchanged: yes
```

Expand Down Expand Up @@ -90,7 +90,7 @@ AssumeZero 总会打印最终选中的命令。`--` 后的参数优先于配置
| AZ-S004 | `MINIMAL_PATH` | deep | 保留顶层命令目录、系统必需目录和显式保留条目 |
| AZ-S005 | `SPACE_WORKDIR` | ✓ | 使用包含多个空格的副本路径 |
| AZ-S006 | `UNICODE_WORKDIR` | ✓ | 使用包含 Unicode 的副本路径 |
| AZ-S007 | `DEEP_WORKDIR` | ✓ | 使用安全、有界的深层路径 |
| AZ-S007 | `DEEP_WORKDIR` | ✓ | 使用安全、有界的长路径组件 |
| AZ-S008 | `REDIRECTED_TEMP` | ✓ | 重定向 `TMP`、`TEMP` 和 `TMPDIR` |
| AZ-S009 | `TIMEZONE_UTC` | deep | 在支持的平台上设置进程级 `TZ=UTC`;best effort |
| AZ-S010 | `LOCALE_C` | deep | 在 C/POSIX Locale 可用时设置 `LANG=C` 和 `LC_ALL=C` |
Expand Down Expand Up @@ -130,6 +130,7 @@ confirm_failures = 2
[workspace]
mode = "working-tree"
max_size_mib = 2048
max_entries = 100000
exclude = [".git", ".assumezero"]
include_untracked = []

Expand All @@ -146,6 +147,7 @@ max_total_seconds = 1800

[report]
formats = ["terminal", "json", "markdown"]
sensitive_options = [] # 含秘密值的歧义短选项,例如 ["-p"]
```

未知字段会导致校验失败。输出文本、正则表达式、必需文件和禁止文件等 Oracle 条件见[产品规格](docs/zh-CN/PRODUCT_SPEC.md)。机器可读配置格式见 [config-v1.schema.json](schemas/config-v1.schema.json);字段名保持英文,作为稳定接口的一部分。
Expand Down Expand Up @@ -173,7 +175,7 @@ assumezero explain <run-id>

## 隐私

AssumeZero 本身不会上传文件、调用外部 API、发送遥测、检查真实主目录的内容或持久化环境变量值。敏感环境值只在内存中用于输出脱敏,随后才写入报告。报告只包含名称、存在性/分类元数据和脱敏后的输出摘要。
AssumeZero 本身不会上传文件、调用外部 API、发送遥测、检查真实主目录的内容或持久化已识别的秘密值。敏感环境值以及 `--token`、`--password`、`--api-key` 等已识别长 CLI 选项的值只在内存中用于命令/输出证据脱敏,随后才写入报告。存在歧义的短选项或自定义选项需在 `report.sensitive_options` 中声明;已声明的单字符短选项会覆盖 `-p value`、`-p=value` 和 `-pVALUE`。报告只包含名称、存在性/分类元数据和脱敏后的输出摘要。

用户提供的准备命令和被测命令仍可访问当前用户可访问的网络及其他资源。脱敏是纵深防御,模式匹配可能出现误报或漏报。

Expand All @@ -183,7 +185,7 @@ AssumeZero 本身不会上传文件、调用外部 API、发送遥测、检查

被测命令不会以源项目作为工作目录,但仍保留当前用户的操作系统权限,因此可以故意访问副本之外的资源。不要用 AssumeZero 运行不受信任代码。除非显式选择 `--shell`,否则禁用 Shell 解析;启用时会显示警告。

默认拒绝指向外部的符号链接,并且不会读取目标。超时或中断后的进程树终止是 best effort,无法在所有平台上保证。
默认拒绝指向外部的符号链接;工具会解析目标以判断边界,但不会读取目标文件内容。超时或中断后的进程树终止是 best effort,无法在所有平台上保证。

## 平台支持

Expand Down
2 changes: 1 addition & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ AssumeZero is a Rust library plus a thin CLI binary. The main modules are:
10. Fingerprint and compare source state.
11. Redact in memory and write report schema v1.

The process runner receives an executable and argument vector; it does not concatenate a shell string. Explicit `--shell` converts the user script into platform shell arguments only after displaying the security warning.
The process runner receives an executable and argument vector; it does not concatenate a shell string by default. Explicit `--shell` can convert multiple structured tokens into platform shell arguments after displaying the security warning. A single opaque script token is refused because its CLI-only secrets cannot be redacted reliably from output evidence.

## Cross-platform design

Expand Down
6 changes: 5 additions & 1 deletion docs/JSON_SCHEMA.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ workspace_integrity

`started_at` and `finished_at` are Unix epoch-second strings in schema v1. Durations are integer milliseconds.

Environment-variable values are not fields anywhere in the schema. `restored_names` contains variable names or redacted normalized `PATH` entries. Captured output is a bounded, redacted summary and carries an `output_truncated` flag.
`run_id` is a canonical 26-character ULID and a portable single path component. Repository and workspace fingerprints are opaque, version-dependent integrity evidence intended for before/after comparison within the same run; do not compare values across AssumeZero versions. `git_status_before` and `git_status_after` may contain legacy porcelain text from older v1 reports or a `sha256:` digest from hardened builds; new reports persist only the digest so repository path names do not re-enter evidence.

Before `explain` or report regeneration, AssumeZero revalidates the saved v1 structure. It rejects unsupported versions, invalid required values, and unknown fields in every fixed-shape object, including nested run, scenario, and finding structures. Parser details and offending values are suppressed because saved reports are untrusted and may contain secrets.

Environment-variable values are not fields anywhere in the schema. Recognized or configured sensitive CLI option values are replaced before the `command` field is constructed. `restored_names` contains variable names or redacted normalized `PATH` entries. Captured output and Oracle details are bounded where applicable and redacted before Report construction; captured output carries an `output_truncated` flag.

Consumers must:

Expand Down
11 changes: 10 additions & 1 deletion docs/LIMITATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,28 @@
Version 0.1.0 intentionally supports only finite, non-interactive commands.

- Workspace copies are isolation from direct relative source writes, not a security sandbox.
- Canonicalization, preflight, and source rechecks narrow filesystem race windows but do not provide capability-relative, atomic protection against a repository being modified concurrently during copy.
- `workspace.max_entries` also bounds full-source fingerprint collection; only `.git` and `.assumezero` are excluded from that count, and stored fingerprint path names have a 64 MiB aggregate limit.
- Absolute-path and deliberate out-of-workspace writes cannot be prevented.
- Timeout/interruption termination targets the direct child; complete descendant-tree termination is best effort.
- Disposable workspace cleanup is best effort after execution; a surviving descendant process or operating-system file lock can leave a temporary copy behind, especially on Windows.
- Only Unicode environment variables available through Rust's portable string API participate in v0.1.0 minimization.
- Very short exact secret values can cause broad false-positive masking. When `-p` is configured as sensitive, every longer `-p...` token is treated as an attached value.
- Literal redaction is limited to 2,048 rules and 256 KiB of rule text; exceeding either limit fails closed by suppressing affected free-text evidence.
- `EMPTY_HOME` can confirm dependence on home-level state but does not trace the specific file.
- Cache redirection covers a conservative known-variable list and cannot prove a cache was actually read.
- `TZ=UTC` is a process-level best-effort setting, not an operating-system timezone change.
- `LOCALE_C` is skipped if the locale cannot be discovered reliably.
- Deep paths are bounded and do not probe beyond operating-system limits.
- The `DEEP_WORKDIR` path-length probe uses one bounded component (maximum 240 ASCII bytes); it does not measure arbitrary directory nesting or probe beyond common component limits.
- A 1-minimal result is not a globally minimum or unique causal explanation.
- Minimization assumes sufficiently stable behavior and can stop with a `SUSPECTED` current-best result when budget is exhausted.
- Pairwise scenario reduction is documented for a future release and is not enabled in v0.1.0.
- Shell mode is explicitly trusted-input only.
- Log redaction can miss encoded, transformed, fragmented, or unfamiliar secrets.
- Raw secrets passed as command arguments remain visible to the operating system while the process runs. Single-string opaque shell scripts are refused; transformed values and undeclared short-option meanings in structured commands can still evade exact redaction.
- Regenerating an older report reapplies current built-in long-option redaction, but historical custom/short-option semantics cannot be reconstructed. Inspect historical `.assumezero/runs` content before regenerating or sharing it.
- Report v1 has no trusted Shell provenance, so `explain` and regeneration reject every saved `sh -c` or Windows `/D /S /C` wrapper, including direct commands that were not created with `--shell`.
- Human-readable renderers expose C0/C1 control characters as visible code points, but Unicode bidirectional formatting characters are not normalized in v0.1.0.
- The tool does not trace arbitrary filesystem access, syscalls, network faults, databases, services, or containers.
- No crates.io package, telemetry, account system, cloud backend, or Marketplace Action is provided.

Expand Down
4 changes: 3 additions & 1 deletion docs/PRODUCT_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ assumezero report <run-id> --format markdown|json|junit

Global options are `--verbose`, `--quiet`, `--no-color`, `--json`, and `--config <path>`.

`--shell` remains trusted-input only. v0.1.0 refuses a single opaque script token; use multiple structured tokens, direct argv execution, or recognized environment variables when evidence may contain sensitive values.

## Baseline

The default is two runs, each in a new project copy with the same command, source state, configuration, and original inherited environment. Every run must satisfy the oracle before attribution starts. Mixed accepted/rejected runs produce `BASELINE_UNSTABLE`; consistently rejected runs produce `BASELINE_FAILED`. `--strict-output` additionally requires identical redacted stdout/stderr summaries and exit codes.
Expand All @@ -57,7 +59,7 @@ Absolute and parent-traversing oracle file paths are rejected. Each run records

`git-clean` uses Git's tracked-file list plus relative paths explicitly named in `workspace.include_untracked`. It does not copy `.git`, ignored dependencies, or build output by default. It generally needs preparation commands.

The default maximum copied size is 2 GiB. External symlinks are refused unless the user explicitly accepts their risk.
The default copy limits are 2 GiB and 100,000 filesystem entries. Eligible sources, sizes, and entry counts are preflighted before the destination project directory is created; oversized copies fail before mutation. The same entry count also bounds source-fingerprint collection across the source tree (excluding `.git` and `.assumezero`, but not other workspace exclusions), and fingerprint path storage has a 64 MiB hard limit. Workspace names are exactly one normal path component. External symlinks and paths crossing symlink ancestors are refused unless the user explicitly accepts the limited final-link exception; nested traversal through symlinks is always refused.

## Minimization

Expand Down
Loading