Files
shadow/docs/CLEAN-ROOM-AUDIT-2026-05-04.md
T
Pierre Warnier d31c689b3f cli: identify shadow-rs as part of uutils (#161) (#162)
* 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.
2026-06-08 13:34:08 +02:00

6.1 KiB
Raw Blame History

Clean-room compliance audit — 2026-05-04

This document records the results of an automated clean-room audit run against src/uu/**/*.rs and src/shadow-core/**/*.rs. It is preserved as evidence of the project's clean-room posture against GNU shadow-utils (GPL-2.0+).

Why an audit at all

The project's clean-room policy (CONTRIBUTING.md) forbids reading shadow-maint/shadow source. That guarantees no commit ever derives from upstream — but it cannot, by itself, prove the absence of accidental similarity in user-facing strings (error messages, clap flag-help, --help text). This audit is the evidence side of the policy: independent verification that no string in the tree is a verbatim copy of upstream wording.

Protocol

The audit was performed by an LLM with the shadow-maint/shadow source code in its training corpus, run from a clean-room driver outside the repository's working tree. The protocol guarantees no upstream content crosses back into the repository:

  1. Output schema — the LLM's response was constrained to a strict JSON Schema admitting only {file, line, verdict, confidence, category} per finding plus a length-capped notes field. The schema literally has no field where a GNU-shadow string could land.
  2. Verdicts — five labels: verbatim, near-paraphrase, idiomatic, independent, abstain. Confidence in {high, medium, low}.
  3. Sandbox — the tool ran with --ephemeral --sandbox workspace-write and was instructed not to clone, fetch, or webfetch shadow-maint/shadow or any GNU shadow-utils mirror.
  4. Output channel — only the schema-validated JSON message was captured. The progress log was scanned for shadow-maint, github.com/shadow, apt-get source, git clone, wget, curl — no command invocations targeted upstream.
  5. Provenance — the only "shadow" URLs that appeared in the log were the prompt's own warning text and our own https://github.com/uutils/shadow-rs constant.

The model's task was to classify every user-facing string literal in the listed files (clap .help/.about/.long_about/.override_usage/.after_help, uucore::show_error!, eprintln!/println!, writeln!(io::stderr/stdout), panic!/unreachable!) without ever quoting upstream. If unable to classify without quoting, it was instructed to emit abstain.

Results

Verdict Count Share
verbatim 0 0.0%
near-paraphrase 134 27.6%
idiomatic 218 44.9%
independent 62 12.8%
abstain 72 14.8%
total examined 486

Headline: 0 verbatim matches.

Triage of the 134 near-paraphrases

  • 16 high-confidence all fall under the behavioral-compatibility carve-out — output strings that scripts grep for and where the project explicitly commits to drop-in compat with GNU shadow (CONTRIBUTING.md Design Goals: "Drop-in replacement: same flags, same exit codes, same output format as GNU shadow-utils"). Distribution:

    • chage -l aging-info column headings — src/uu/chage/src/chage.rs lines 442, 466480 (the function comment itself reads "Print the aging information in the GNU chage -l format").
    • pwck diagnostic output — src/uu/pwck/src/pwck.rs lines 385, 478, 504, 510, 519.
    • grpck diagnostic output — src/uu/grpck/src/grpck.rs lines 209, 251, 261.

    Under the merger doctrine, copyright does not attach to expression dictated by external function (here: drop-in compat). These were retained as-is — divergence would be a regression, not a fix.

  • 118 medium-confidence were clap flag-help and .about(...) strings across all 14 tools. The model could not commit to "high" because flag-help wording is close to upstream man pages and verifying that would require quoting. All 118 were rewritten in this pass to remove residual GNU-shadow-style phrasing, working only from our own source (the existing flag name, the actual behavior in the code).

Triage of the 72 abstains

The model declined to classify these — typically because they are domain-specific format strings that overlap with similar implementations generally, and the model was unwilling to commit either way without quoting. They live mostly in src/shadow-core/src/{validate,crypt,error}.rs and the show_error! paths of the user tools. None of them is a verbatim risk; all use composable phrasing typical of error-handling idiom.

Methodology integrity checks

  • The schema-validated output passed Draft-2020-12 JSON-Schema validation with zero errors.
  • All 30 file paths referenced in findings exist in the working tree.
  • Every line number is within the bounds of the file it references — no hallucinated locations.
  • The audit was run with model_reasoning_effort=high to avoid shallow-model false negatives.

Bottom line

Question Answer
Verbatim copies of GNU shadow strings in tree? 0 in 486 strings examined
Real paraphrase-rewrite surface? 118 strings, all rewritten in this commit
Compatibility carve-out strings? 16 retained intentionally
GPL strings leaked through audit? None (schema-constrained, log-verified)

The project's clean-room posture is intact. The 118 rewritten strings remove the only material residual-similarity surface that did not fall under the drop-in-compat carve-out.

Re-running this audit

The audit is reproducible. The protocol parameters are:

  • Strict JSON Schema for the LLM's response (no field can carry upstream content).
  • Sandbox/ephemeral mode; explicit prompt forbidding clone/fetch/webfetch of shadow-maint or any GNU shadow source package.
  • Post-run scan of the tool's command log for upstream URLs / source-fetch commands.
  • Schema-validate the final output before consuming it.

Future audits should be filed as docs/CLEAN-ROOM-AUDIT-YYYY-MM-DD.md.