You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The PAM path had no automated coverage until the e2e image was repaired; keep it that way and add #[cfg(feature = "pam")] tests that drive authenticate_caller against pam_permit/pam_deny stacks in the Docker images.
~120 of 162 integration tests return silently when not root and are reported ok; the only root run (Docker matrix) is if: github.event_name == 'push', so on a pull request about 75 % of the suite does not execute. Run the matrix on PRs (each job is under 4 min), make skips visible (SHADOW_TEST_REQUIRE_ROOT=1 in the containers turns a skip into a failure), and un-gate the pwck/grpck/chage tests that never needed root.
The Docker matrix runs without --features pam; root+PAM is never unit-tested.
Tests call uumain() in-process and assert on nothing but exit codes: zero stdout/stderr assertions across the suite, so "bit-for-bit identical output" is unverified. Spawn CARGO_BIN_EXE_shadow-rs instead, which also stops harden_process, setuid(0), Landlock and umask changes from leaking between tests in one process.
Tautological tests: test_chage.rs and test_chpasswd.rs never invoke their tool (they set a shadow field and read it back); test_pwck accepts 0 || 2; test_chsh passes because PAM fails first; test_chfn discards its fixture; test_passwd discards eight return codes and races four threads on a PID-keyed lock. Rewrite against spawned binaries once chage/chpasswd/chfn/chsh have a --prefix.
No test that the multicall binary registers every tool (one table now drives dispatch and --list; a test that spawns shadow-rs <tool> --help for all fourteen names closes it).
Fuzz targets are not built in CI, have no subid target, and assert only "no panic"; add parse→Display→parse and no-separator invariants. The corpus is tracked but gitignored — decide.
CI
No permissions: blocks; actions pinned by tag, not digest (helpers:pinGitHubActionDigests in Renovate).
no-ai-traces.sh: the regex is not word-boundaried (llm matches smallmap, anthropic matches philanthropic), so it will block legitimate PRs touching CONTRIBUTING.md's tooling policy paragraph, and it misses gpt-4o, cursor, aider, co-authored-by: *[bot]. ${{ github.base_ref }} is interpolated into run:; pass it through env:.
audit.yml duplicates the deny job; keep one scheduled advisory check.
Two hook systems (hooks/ in Docker vs .pre-commit-config.yaml on the host with a different feature set); pre-push hook runs the full multi-distro suite and gets bypassed #231 already tracks the pre-push cost. Keep one, and encode the CI commands in make check/make test so README, CONTRIBUTING, hooks and ci.yml stop repeating them.
Build and dependencies
clap/derive is enabled and unused; it alone pulls clap_derive + syn 3 next to syn 2 and is why deny.toml skips the duplicate.
Makefile installs all fourteen tools into $(PREFIX)/sbin; GNU puts passwd chage chfn chsh newgrp in bin (non-root users rarely have sbin in PATH). make test runs without pam while install ships it.
shadow-rs.spec (version 0.2.0, %files lists a binary make install no longer produces) and debian/ are stale and would not build; tests/gnu-compat.sh compares full output for two commands only. Fix or delete.
rust:latest, rust:alpine, fedora:latest and get.nexte.st/latest are unpinned (nextest is never used); toolchain drift lands on main with no PR signal.
deny.toml allows MPL-2.0, ISC, BSD-2-Clause, CC0, Zlib that nothing uses — tighten to the graph; document that self_cell is accepted under its Apache option; unknown-registry/unknown-git → deny.
Fourteen [[test]] binaries each linking all fourteen crates, with fn run, read_shadow, setup_prefix … duplicated up to 14×; the uutils layout (tests/tests.rs + mod by_util + a real common) is the fix.
Findings on the test suite, CI and build system.
Coverage that does not exist
#[cfg(feature = "pam")]tests that driveauthenticate_calleragainstpam_permit/pam_denystacks in the Docker images.returnsilently when not root and are reportedok; the only root run (Docker matrix) isif: github.event_name == 'push', so on a pull request about 75 % of the suite does not execute. Run the matrix on PRs (each job is under 4 min), make skips visible (SHADOW_TEST_REQUIRE_ROOT=1in the containers turns a skip into a failure), and un-gate thepwck/grpck/chagetests that never needed root.--features pam; root+PAM is never unit-tested.uumain()in-process and assert on nothing but exit codes: zero stdout/stderr assertions across the suite, so "bit-for-bit identical output" is unverified. SpawnCARGO_BIN_EXE_shadow-rsinstead, which also stopsharden_process,setuid(0), Landlock and umask changes from leaking between tests in one process.test_chage.rsandtest_chpasswd.rsnever invoke their tool (they set a shadow field and read it back);test_pwckaccepts0 || 2;test_chshpasses because PAM fails first;test_chfndiscards its fixture;test_passwddiscards eight return codes and races four threads on a PID-keyed lock. Rewrite against spawned binaries oncechage/chpasswd/chfn/chshhave a--prefix.--list; a test that spawnsshadow-rs <tool> --helpfor all fourteen names closes it).subidtarget, and assert only "no panic"; add parse→Display→parse and no-separator invariants. The corpus is tracked but gitignored — decide.CI
permissions:blocks; actions pinned by tag, not digest (helpers:pinGitHubActionDigestsin Renovate).cargo check --workspacewithout--all-targets/--all-features;completionsand (until shadow-core: keep ownership on rewrite, refuse field injection, never abort on '..' #238)selinuxwere compiled by nobody. Add an--all-featuresclippy pass.no-ai-traces.sh: the regex is not word-boundaried (llmmatchessmallmap,anthropicmatchesphilanthropic), so it will block legitimate PRs touchingCONTRIBUTING.md's tooling policy paragraph, and it missesgpt-4o,cursor,aider,co-authored-by: *[bot].${{ github.base_ref }}is interpolated intorun:; pass it throughenv:.audit.ymlduplicates thedenyjob; keep one scheduled advisory check.hooks/in Docker vs.pre-commit-config.yamlon the host with a different feature set); pre-push hook runs the full multi-distro suite and gets bypassed #231 already tracks the pre-push cost. Keep one, and encode the CI commands inmake check/make testso README, CONTRIBUTING, hooks andci.ymlstop repeating them.Build and dependencies
clap/deriveis enabled and unused; it alone pullsclap_derive+syn 3next tosyn 2and is whydeny.tomlskips the duplicate.Makefileinstalls all fourteen tools into$(PREFIX)/sbin; GNU putspasswd chage chfn chsh newgrpinbin(non-root users rarely havesbininPATH).make testruns withoutpamwhileinstallships it.shadow-rs.spec(version 0.2.0,%fileslists a binarymake installno longer produces) anddebian/are stale and would not build;tests/gnu-compat.shcompares full output for two commands only. Fix or delete.rust:latest,rust:alpine,fedora:latestandget.nexte.st/latestare unpinned (nextest is never used); toolchain drift lands on main with no PR signal.deny.tomlallowsMPL-2.0,ISC,BSD-2-Clause,CC0,Zlibthat nothing uses — tighten to the graph; document thatself_cellis accepted under its Apache option;unknown-registry/unknown-git→deny.[[test]]binaries each linking all fourteen crates, withfn run,read_shadow,setup_prefix… duplicated up to 14×; the uutils layout (tests/tests.rs+mod by_util+ a realcommon) is the fix.