2026-03-23 14:11:19 +01:00
|
|
|
<!-- spell-checker:ignore reimplementing setuid subuid subgid gshadow -->
|
|
|
|
|
|
|
|
|
|
# Contributing to shadow-rs
|
|
|
|
|
|
|
|
|
|
Thanks for wanting to contribute to shadow-rs! This document explains
|
|
|
|
|
everything you need to know.
|
|
|
|
|
|
|
|
|
|
Before you start, also check:
|
|
|
|
|
|
2026-04-03 14:45:17 +02:00
|
|
|
- [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md)
|
2026-03-23 14:11:19 +01:00
|
|
|
- [SECURITY.md](./SECURITY.md) for vulnerability reporting
|
|
|
|
|
|
|
|
|
|
> [!WARNING]
|
|
|
|
|
> shadow-rs is original code and **cannot contain any code from GNU
|
|
|
|
|
> shadow-utils** or other GPL-licensed implementations. This means that
|
|
|
|
|
> **we cannot accept any changes based on the
|
|
|
|
|
> [shadow-maint/shadow](https://github.com/shadow-maint/shadow) source
|
|
|
|
|
> code** (GPL-2.0+). To make sure that cannot happen, **you must not read
|
|
|
|
|
> or link to the GNU source code**. This includes paraphrasing or
|
|
|
|
|
> translating their logic, and feeding it into an LLM for translation.
|
|
|
|
|
|
|
|
|
|
## Safe Reference Sources
|
|
|
|
|
|
|
|
|
|
When implementing a tool, reference ONLY these sources:
|
|
|
|
|
|
|
|
|
|
| Source | License | Use |
|
|
|
|
|
|--------|---------|-----|
|
|
|
|
|
| [POSIX specification](https://pubs.opengroup.org/onlinepubs/9799919799/) | Open standard | Behavioral spec |
|
|
|
|
|
| Man pages (man7.org) | Documentation | Command options, file formats |
|
|
|
|
|
| [FreeBSD src](https://cgit.freebsd.org/src/) | BSD-2-Clause | Implementation patterns |
|
|
|
|
|
| [OpenBSD src](https://cvsweb.openbsd.org/src/) | ISC | Implementation patterns |
|
|
|
|
|
| [musl libc](https://musl.libc.org/) | MIT | pwd/grp/shadow C API |
|
|
|
|
|
| [sudo-rs](https://github.com/trifectatechfoundation/sudo-rs) | Apache-2.0 / MIT | PAM, privilege-dropping |
|
|
|
|
|
|
|
|
|
|
**Process**: Read the POSIX spec and man page for behavioral requirements,
|
|
|
|
|
then write an original implementation. Never search for or view the C source.
|
|
|
|
|
|
|
|
|
|
## Getting Started
|
|
|
|
|
|
|
|
|
|
### Requirements
|
|
|
|
|
|
|
|
|
|
- Docker and Docker Compose (all builds/tests run in containers)
|
|
|
|
|
- Git
|
|
|
|
|
|
|
|
|
|
### Setup
|
|
|
|
|
|
|
|
|
|
```shell
|
2026-04-22 15:31:42 +02:00
|
|
|
git clone https://github.com/uutils/shadow-rs
|
2026-03-23 14:11:19 +01:00
|
|
|
cd shadow-rs
|
|
|
|
|
docker compose build
|
|
|
|
|
./hooks/install.sh # install pre-commit and pre-push hooks
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Building and Testing
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
docker compose run --rm debian cargo build # build
|
|
|
|
|
docker compose run --rm debian cargo test --workspace # test on Debian
|
|
|
|
|
docker compose run --rm alpine cargo test --workspace # test on Alpine (musl)
|
|
|
|
|
docker compose run --rm fedora cargo test --workspace # test on Fedora (SELinux)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Linting
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
docker compose run --rm debian cargo clippy --workspace --all-targets -- -D warnings
|
|
|
|
|
docker compose run --rm debian cargo fmt --all --check
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Design Goals
|
|
|
|
|
|
|
|
|
|
- **Drop-in replacement**: same flags, same exit codes, same output format as
|
|
|
|
|
GNU shadow-utils. Differences with GNU are treated as bugs.
|
|
|
|
|
- **uutils compatible**: tools use `uucore` (`UResult<()>`, `#[uucore::main]`,
|
|
|
|
|
`show_error!`) so they can be merged into the uutils ecosystem.
|
|
|
|
|
- **Memory safe**: no `.unwrap()`, no `panic!`, no `std::process::exit` in
|
|
|
|
|
library code.
|
|
|
|
|
- **Well-tested**: unit tests, proptest, integration tests, fuzz targets.
|
|
|
|
|
|
|
|
|
|
## Our Rust Style
|
|
|
|
|
|
|
|
|
|
### Don't `panic!`
|
|
|
|
|
|
|
|
|
|
Never use `.unwrap()` or `panic!`. Use `unreachable!` only with a justifying
|
|
|
|
|
comment. Return errors via `UResult<()>`.
|
|
|
|
|
|
|
|
|
|
### Don't `exit`
|
|
|
|
|
|
|
|
|
|
Utilities must be embeddable. Return `UResult<()>` from `uumain`. The
|
|
|
|
|
`uucore::bin!()` macro handles `process::exit` in the generated `main()`.
|
|
|
|
|
|
|
|
|
|
### `unsafe`
|
|
|
|
|
|
2026-04-03 15:16:41 +02:00
|
|
|
Denied at the workspace level (`unsafe_code = "deny"`). Only two FFI boundary
|
2026-04-03 15:27:58 +02:00
|
|
|
modules are exempted: `shadow_core::pam` (PAM C library) and `shadow_core::crypt`
|
2026-04-03 15:16:41 +02:00
|
|
|
(POSIX crypt(3)). Every `unsafe` block must have a `// SAFETY:` comment.
|
2026-03-23 14:11:19 +01:00
|
|
|
|
|
|
|
|
### `str`, `OsStr` & `Path`
|
|
|
|
|
|
|
|
|
|
Use `OsStr` and `Path` for filesystem paths. Only convert to `String`/`str`
|
|
|
|
|
when you know the data is always valid UTF-8 (usernames, for example).
|
|
|
|
|
|
|
|
|
|
### Comments
|
|
|
|
|
|
|
|
|
|
Comments explain _why_, not _what_. If you need to describe what code does,
|
|
|
|
|
improve the naming instead.
|
|
|
|
|
|
|
|
|
|
## Commits
|
|
|
|
|
|
|
|
|
|
- Small, atomic commits — one logical change per commit
|
|
|
|
|
- Tool-prefixed messages: `passwd: fix buffer handling`
|
|
|
|
|
- Test commits: `tests/passwd: add aging test`
|
|
|
|
|
- Do not move code in the same commit as a behavior change
|
|
|
|
|
|
|
|
|
|
## Pull Requests
|
|
|
|
|
|
|
|
|
|
- One issue per PR, reference it: `Fixes #N`
|
|
|
|
|
- Title prefixed with tool name, under 70 characters
|
|
|
|
|
- Branch naming: `fix/N-short-description` or `feat/N-short-description`
|
|
|
|
|
- CI must pass (clippy, fmt, tests on all 3 distros)
|
|
|
|
|
- Keep PRs small and self-contained
|
|
|
|
|
|
|
|
|
|
## AI-Assisted Development
|
|
|
|
|
|
|
|
|
|
AI tools (Copilot, Claude, etc.) are allowed and treated like any other
|
|
|
|
|
development tool.
|
|
|
|
|
|
|
|
|
|
- All code must be understood, reviewed, and tested by human contributors
|
|
|
|
|
- **Clean-room rule is absolute** — never feed GPL source into an LLM
|
|
|
|
|
- Reference only: POSIX specs, man pages, BSD-licensed implementations
|
|
|
|
|
- If a PR is substantially AI-generated, note it (transparency, not a blocker)
|
|
|
|
|
- Quality is the gate — contributions are judged on correctness and test
|
|
|
|
|
coverage, not authorship method
|
|
|
|
|
|
|
|
|
|
## Licensing
|
|
|
|
|
|
|
|
|
|
shadow-rs is distributed under the terms of the [MIT License](LICENSE).
|
|
|
|
|
|
|
|
|
|
Acceptable dependency licenses: MIT, Apache-2.0, ISC, BSD-2-Clause,
|
|
|
|
|
BSD-3-Clause, CC0-1.0, Unicode-3.0, Zlib, MPL-2.0.
|
|
|
|
|
|
|
|
|
|
**No GPL or LGPL dependencies, ever.**
|