The dev images built cargo-deny from source via `cargo install cargo-deny`.
That compiled cargo-deny on every image build (multi-minute) and surfaced
cargo-deny's own `profile.dev.package.{insta,similar}` warnings, which look
like our config but are not.
Install the pinned (0.19.8), checksum-verified prebuilt binary instead. The
static musl build runs on glibc too, so all three images share one asset.
Result: no source compile, no spurious warnings, and a reproducible,
sha256-verified install (vs the previous unpinned `cargo install`). Each
build self-checks with `cargo-deny --version`.
Version/checksum are ARGs so they can be bumped (and tracked by Renovate).
Address review feedback on #173:
- Don't sleep after the final build attempt (no retry follows it).
- Forward the workflow-wide CARGO_NET_RETRY into the container with a
bare '-e CARGO_NET_RETRY' instead of re-hardcoding the value, keeping
a single source of truth.
The Test (debian/alpine/fedora) matrix failed four times in a row on
2026-06-10, each on a transient network error (Docker Hub base-image
pull i/o timeout; crates.io download broken pipe) unrelated to the code.
- fail-fast: false so one distro's flake no longer cancels the others.
- Retry 'docker compose build <target>' (the Docker Hub pull) with backoff.
- Pass CARGO_NET_RETRY into the container and set it for runner-native
jobs so cargo retries crate downloads; real test failures still fail
fast (no step-level retry around the test run).
No new third-party actions — a shell retry loop keeps the supply-chain
surface unchanged.
Closes#172.
* cli: identify shadow-rs as part of uutils in --help and --version
Per #161, every tool now reports `(uutils shadow-rs) <ver>` in its
--version output and links to the project in --help via a shared
shadow_core::cli module.
While auditing the same uu_app() blocks for residual similarity to
GNU shadow-utils man-page phrasing, the medium-confidence findings
from a clean-room similarity check were also rewritten: 118 clap
.help() and .about() strings across all 14 tools now use phrasing
derived from the in-tree behavior of each flag, not from upstream
documentation.
Output strings that scripts grep for (chage -l column headings,
pwck/grpck diagnostic lines) were left intentionally untouched, since
drop-in compatibility is part of the project's contract.
- shadow-core: new cli module with VERSION and AFTER_HELP constants
- 14 tools: wire .version() and .after_help() in every uu_app()
- 14 tools: rewrite 118 flag-help and .about() strings
- shadow-rs multicall: handle --help/--version/-h/-V with project ID
- fmt + clippy + tests pass on debian, alpine, fedora (574 passed, 0 failed)
Closes#161
* docs: record clean-room audit results (2026-05-04)
Adds an evidence document for the clean-room similarity audit that
prompted the flag-help rewrite in the previous commit. Headline:
0 verbatim matches in 486 user-facing strings.
The audit protocol is reproducible and described in the doc, including
the schema-constrained output and post-run command-log scan that ensure
no upstream string crosses back into the workspace.
* cli: correct help/version text flagged in review
- multicall --version/--help now use the shared (uutils shadow-rs)
identifier instead of (uutils), matching the tool crates; clarify the
symlink invocation line in the help banner.
- --root help for the SysRoot tools (userdel, useradd, usermod, groupadd,
groupdel, groupmod, pwck, grpck) no longer claims a chroot(2): these
only resolve system files under a path prefix. Reword to match, and
rename the metavar CHROOT_DIR -> ROOT_DIR.
- userdel --force: documented as accepted-for-compatibility (no effect)
rather than promising behavior the code does not implement.
- groupadd -K: scope the help to the GID-range keys actually honored.
- passwd --repository: note only the local files backend is supported.
- passwd --stdin: reads password input generally (auth may consume the
current password too), not just the new password.
- chpasswd -m: state that MD5 is rejected, not supported.
- usermod --append: restore the note that -a is only effective with -G.
- clean-room audit doc: cite CONTRIBUTING.md (the in-tree policy) instead
of the gitignored CLAUDE.md.
The Security audit workflow runs `cargo audit --file ./Cargo.lock`, but
Cargo.lock was gitignored, so the file was absent in CI and every run
failed with "Couldn't load ./Cargo.lock" — including the daily scheduled
run on main. No advisory scan was ever performed.
Commit the lockfile and drop it from .gitignore. shadow-rs ships binaries
(the setuid-root tools), and committing the lockfile is the convention for
binary crates: it gives reproducible builds and a stable input for the
audit. The fuzz crate's lockfile stays ignored as throwaway tooling.
Closes#167.
std::fs::create_dir(path) calls mkdir(path, 0o777) and the actual mode
is 0o777 & ~umask. With an attacker-controlled umask (inherited across
setuid since the kernel does not reset it), the directory exists with
permissive bits between mkdir and the subsequent set_permissions call.
Fix uses DirBuilder::mode(...).create(...) so the mode is set in the
syscall itself, wrapped in UmaskGuard::zero() to neutralize umask
interference. Same pattern applied to skel.rs for subdirectory and
file copies (file copies switch from fs::copy to OpenOptions::mode +
io::copy for the same reason).
Regression test asserts a process running with umask 0 still produces
a 0o700 home directory.
Reported by @collinfunk in uutils/coreutils#11828.
Fixes#157.
#154: Reject setuid multicall invocations where argv[0] doesn't match
AT_EXECFN from the ELF auxiliary vector. Prevents an attacker from
spoofing argv[0] to route the multicall binary to a different tool
in setuid context. Only enforced when euid != uid.
#155: Add daily cargo-audit workflow matching uutils/coreutils pattern.
Also triggers on Cargo.toml/Cargo.lock changes.
Fixes#154. Fixes#155.
First release under the uutils organization.
Highlights:
- Repo transferred to uutils/shadow-rs
- uucore upgraded to 0.8
- nix crate fully replaced by rustix
- Default install now uses standalone per-tool binaries with
least-privilege setuid layout
- println!/eprintln! replaced with non-panicking writes
- Unwind tables suppressed in release builds
- NSS-backed user lookup via getpwuid_r
- 35+ security findings addressed across 6 review rounds