Repository navigation
feat(deps): cargo-deny gate, unsafe guard, command.run env fix, edition 2024 - #159
Merged
Merged
Conversation
Avoids an xfail placeholder; the test goes RED against the old scheduled workflow and green when Task 4 deletes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
licenses, bans and sources were never checked anywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
unsound defaults to "workspace" in cargo-deny, hiding RUSTSEC-2026-0306 and RUSTSEC-2026-0205 that cargo-audit reports. serial_test 4 and an autocfg/faster-hex lock bump clear both. Fixture tests prove deny.toml loads (MIT accepted) and rejects GPL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Each remaining unsafe block carries #[expect(unsafe_code, reason)], so a removed block fails lint instead of leaving a stale allow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
One advisory ignore list (deny.toml). The weekly run still catches advisories published against an unchanged lockfile; stale RUSTSEC-2026-0118/0119 ignores go with the old file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Unprivileged steps whose env: sets PATH now find their program on it, so command.run can stop mutating the etch process environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
command.run applied env: with set_var before the step and remove_var after it: inherited values were deleted rather than restored, a failed step leaked them to later actions, sudo never received them, and Debug printed them. env: now goes to Exec.environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
One unsafe block holds every test env mutation; the guard restores on drop, including on panic. tests/test_env_guard_serial.py keeps guard users serialised and catches let _ = misuse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Every test env mutation now goes through EnvGuard. Four closure helpers that set PATH are inlined so the serial source check sees each guard's test, and two unserialised env tests gain #[serial]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
style_edition stays 2021 here so this commit carries no reformat; the 2024 style flip ships as its own PR. Drop-order sites were read and classified (see PR body). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
The privilege provider would carry env values on its command line, exposing them in ps and sudo logs. Refusing is the only option that neither leaks nor silently drops env:. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
The workspace is on edition 2024; MSRV is deliberately not declared (spec N1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
The edition-2024 let-chain rewrite grew execute from 297 to 298 lines, tripping the clippy::too_many_lines ratchet. Behaviour unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
A cargo tool run against a fixture manifest writes Cargo.lock beside it; the fixtures are policy controls, never built. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
66 mutants exceeded the 10-minute job; run 2 jobs under a 30-minute cap. Linux CI cannot compile the macOS-only chflags/flags paths, so their mutants are excluded (backlog row). Adds tests that kill the two CI-missed mutants: the --label filter and CustomPathBuf schema. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
The expectation re-derived the schema the way the impl does. Also name both mutation workflows in the macOS-exclusion backlog row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo
This was referenced Nov 1, 2026
github-actions Bot
pushed a commit
that referenced
this pull request
Nov 4, 2026
…_program (#165) resolve_program() returns a full absolute path (e.g. /usr/bin/sudo), not a bare command name. The original check if command.eq("doas") || command.eq("sudo") || command.eq("run0") { ... } was introduced in #159 without updating for the new full-path return value. The comparison always evaluates to false, so self.elevate() (which calls `sudo --validate` with inherited stdin to warm the credential cache) is never called for privileged steps. Trigger: a privileged: true non-streaming command.run step runs with expired sudo credentials. Because elevate() is not called first, sudo runs with stdin piped (not inherited, Command::output() default), cannot prompt, and fails with "sudo: a password is required" rather than prompting the user. Fix: replace the bare-name check with self.is_elevated(), which is the exact semantic that was intended (privileged=true && not root) and covers all three providers (sudo, doas, run0) correctly regardless of their resolved path. Regression test: resolve_program_returns_full_path_not_bare_name asserts the return value is an absolute path so this bug cannot silently re-appear. Claude-Session: https://claude.ai/code/session_01RMRWKLTCW3D6caXjkjvCiL Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
cargo-auditjob (id kept as the required-check name) now runscargo deny check advisories licenses bans sources; licenses, bans and sources were enforced nowhere before.deny.tomlsetsunmaintained = "all"andunsound = "all": theunsounddefault of"workspace"hid RUSTSEC-2026-0306 (faster-hex) and RUSTSEC-2026-0205 (scc), which only cargo-audit reported. Both are cleared by bumps (serial_test4, faster-hex 0.10.1), no new ignore. The weeklycargo-audit-scheduled.yml(with its drifted second ignore list) is replaced bycargo-deny-scheduled.yml.unsafe_code = "deny"in lib, app, jsonschemagen; every remainingunsafeblock carries#[expect(unsafe_code, reason)]stating its real invariant. Two backlog rows record the Lua plugin trust model and a duplicated macOSstathelper.command.runenv fixed.env:now goes to the child throughExec.environmentinstead ofset_varon the etch process. Before: inherited values were deleted rather than restored, a failed step leakedenv:to later actions, sudo never received it, andDebugprinted the values. An unprivileged program is resolved on the step's ownenv:PATH.privileged: truewithenv:is refused at plan time: the privilege provider would put the values on its command line (ps, sudo logs). No etch-config manifest combines them.test_helpers::EnvGuard(oneunsafeblock, restores on drop and on panic);tests/test_env_guard_serial.pyrequires every guard user to be#[serial]and catcheslet _ =misuse.rust-version;rustfmt.tomlpinsstyle_edition = "2021"so this PR carries no reformat (the flip is PR2).docs/cursor/specs/2026-10-31-rust-standards-edition-2024-spec.md; plan:docs/cursor/plans/2026-10-31-rust-standards-edition-2024-plan.md.Test Plan
mutation-prfit: 58 mutants,--jobs 2, 490 s locally — 43 caught, 0 missed, 15 unviable (macOS-only chflags.rs/flags.rs excluded, Backlog row)make test— 1185 Rust tests + 197 Python tests pass (Ubuntu 26.04, matches ubuntu-latest)cargo test -p etch-lib(threaded runner) — 1041 passcargo deny check advisories licenses bans sources— all okcargo clippy --target aarch64-apple-darwin --workspace --all-targets,-D warnings) — clean, with a positive controlmake fuzz-manifest FUZZ_TIMEOUT=10— 205668 runs, no crashcargo clippy --all-targets -D warningsexit 0;cargo test --workspace1219 passed, 0 failed (cargo-nextest not installed there)/usr/bin/env PATH=/tmp/p etchprobeprintsokEvidence (spec V items)
--configfails the MIT (config-loaded) test; a scratch--ignore RUSTSECfails the one-list test; a removed#[expect]failsmake lint;which()in place ofwhich_infails the same-step resolution test. Phase 3 added: env sort removal fails 20/20; a fallback to etch'sPATHfailsresolve_program_misses_when_step_path_lacks_program.unset(expectedorig); (b) failed-step case readx; (c) PATH override broke the next action: "Commandshnot found in path"; (d) same-step resolution passed.set_var/remove_var; 4gen; 1if let; 13 drop-order warnings at 12 sites, all in the spec's Problem 4 files.trace!join (String), rollback.rsif let Err(anyhow::Result), status.rs where/plan match (rhai/plan Result), copy.rsrender_str(Result), actions/mod.rseval_with_scope(Result<bool, Box>), exec.rsinfo!join (String), chmod.rs/chown.rs/load.rserror!(path Display), plugin/globals.rs json→lua (Resultmlua::Value), contexts/mod.rstrace!×2 (String). No lock guard, RefCell borrow or side-effecting Drop; no behaviour change; no action.main's lockfile, cargo-audit lists 0205/0306 as unsound and cargo-deny withunsound = "all"errors on exactly those two; on this branch's lockfile neither appears.mainbaseline, worst 0.99x.Code review
reviewed-sha: 1b5db1f
diff-hash: 120dda07ae632e7b472592fbdaffaa21223ac002944b4bf665b4139c9e76aeee
reviewers: 5/5
Three rounds, 5/5 reviewers each.
command.runenv:becamesudo env K=V … cmd, exposing values inps,/proc/<pid>/cmdlineand sudo logs (scored 75). Operator chose fail closed; fixed in f3296b9 (plan()refusesprivileged: truewithenv:; spec R19 amended, R21 added).record_stateextraction e9f9e59): one comment-accuracy finding on therecord_statedoc (it cited atoo_many_linesratchet the repo does not configure); comment-only fix in a849809. After round 3 the only non-docs change is 1589263, one.gitignoreline (tests/fixtures/**/Cargo.lock) removing tool residue; no code changed.mutation-prtimed out): the mutation-pr fit (timeout-minutes: 30,--jobs 2,.cargo/mutants.tomlexcluding the two macOS-only files) and two tests killing the CI-missed mutants. Two low findings fixed in 1b5db1f: the schema test now asserts an independent literal, and the backlog row names both mutation workflows.Required
Informational
Kept
RESOLVED: 4 fixed, 0 kept
Spec alignment
Findings
Reviewed
Verifications
shnot found in path, exit 1; (d) passed.🤖 Generated with Claude Code
https://claude.ai/code/session_01RMRWKKn1ZnoEgzb3hX8ewo