1.2.1 (tag v1.2.1, 2026-10-09) is the current release; 1.2.0 (tag v1.2.0, 2026-10-08: images and chart on GHCR, no GitHub release — its publish job failed), 1.1.0 (tag v1.1.0, 2026-10-05) and 1.0.0 (tag v1.0.0, 2026-10-04, the first) came before. Security fixes land on main and are released as the next 1.2.x; only the latest 1.x release is supported. Earlier 0.1.0 development snapshots are not.
Assets include database and SMTP credentials, API token secrets, subscriber identity and preferences, list configuration, audit history, and service availability. Actors include anonymous clients, members, moderators, list/domain owners, server owners, the MTA, operators, reverse proxies, and hostile mail/web clients.
Primary boundaries are public HTTP to Axum, authenticated adapters to repositories, process to PostgreSQL, MTA to the future LMTP listener, and operator configuration to the process. Phase 2 mail processing is not yet an implemented boundary.
| Threat | Example | Present mitigation | Residual/operational requirement |
|---|---|---|---|
| Spoofing | Forged bearer/basic identity | SHA-256 token digest, constant-time comparison, expiry/revocation, peer CIDR checks | Terminate TLS; do not trust unsanitized forwarding headers |
| Tampering | Unaudited member/config write | Scope/resource checks and atomic business-write plus audit transaction | Protect DB credentials and backups; review migration privileges |
| Repudiation | Privileged action denied later | Structured append-oriented audit actor/target/diff model | Centralize/retain logs outside an attacker-controlled host |
| Information disclosure | DSN/password in output or image context | Config redaction, .dockerignore, no shell in runtime image |
Never commit .env; use url_file or protected environment files |
| Denial of service | Repeated invalid auth or oversized input | Peer-keyed pre-auth limits and input bounds | Reverse-proxy limits and resource monitoring remain required |
| Elevation of privilege | Scoped token crosses list/domain | Resource-bound authorization checks | Regression tests must cover every new write endpoint |
API requests pass through two in-memory stages. Before credential parsing, security.rate_limit.api_pre_auth limits the actual TCP socket peer IP; X-Forwarded-For is deliberately ignored. After successful credential validation, security.rate_limit.api limits the stable account/token identity. If api_pre_auth is omitted, it inherits api. Values use a positive COUNT/WINDOW form with s, min, hour, or day windows and are validated when configuration loads. Rejections return HTTP 429 with an integer Retry-After header and occur before token usage timestamps, handlers, business writes, or audit writes at that stage.
These maps are node-local: replicas do not coordinate counters, and restarting a process clears its buckets. Deployments requiring a global limit must add a shared limiter or an edge/reverse-proxy policy; this process-local control must not be treated as multi-node abuse protection.
The Compose application uses a static musl binary in scratch, numeric UID/GID 1000, a read-only root filesystem, all capabilities dropped, no-new-privileges, and a bounded tmpfs. The healthcheck executes listmngr status; no shell or curl is installed. PostgreSQL credentials are supplied at runtime and must match the application URL.
The systemd unit creates /var/lib/listmngr with StateDirectory, fixes its working directory, clears capabilities, and restricts syscalls, address families, devices, kernel interfaces, home access, and host filesystem writes. Operators must validate the unit on their target Linux/systemd version before rollout and relax protections only with documented runtime evidence.
Base images, GitHub Actions, and CI tools are pinned. Cargo.lock, cargo deny, and cargo audit are blocking controls. Pinning reduces unintended drift but does not replace periodic reviewed upgrades.
Two advisories are recorded exceptions, each with its reason in deny.toml, and CI runs cargo audit --ignore RUSTSEC-2023-0071 --ignore RUSTSEC-2026-0387 with the same two ids; every other advisory blocks.
- RUSTSEC-2023-0071 (
rsa, Marvin timing attack on RSA private-key operations) has no fixed release. The onlyrsause in this tree iswebauthn_rpverifying RS256 passkey signatures with the credential's public key; listmngr holds no RSA private key. Remove the exception when a fixedrsais available. - RUSTSEC-2026-0387 (
crypto-bigint0.5, timing variability in operations meant to be constant-time) is reached only throughwebauthn_rp0.3.0, the latest release, which pinsp256/p3840.13 and soelliptic-curve0.13. Both uses verify ECDSA signatures with a public key (passkey assertions; OpenID Connect ES256 tokens, which share thep2560.13 line); no secret scalar is handled, so timing leaks nothing. Remove the exception whenwebauthn_rpmoves to the 0.14 stack.
With mta.enabled, the Helm chart runs Postfix in the application pod as
root with eight capabilities (CHOWN, DAC_OVERRIDE, FOWNER, FSETID,
KILL, NET_BIND_SERVICE, SETGID, SETUID), a writable root and an
emptyDir spool — the same set Compose grants it, because Postfix's master
binds port 25, switches to its own users and writes its queue. The
application container keeps UID 1000, a read-only root and no
capabilities; the two share only the state volume, read-only on the
Postfix side, and loopback (LMTP in, the plaintext relay out), which
never leaves the pod. allowPrivilegeEscalation stays false and the pod's
seccomp profile applies to both.
- Do not put production secrets in
.env.example, Compose YAML, command lines, issue reports, logs, or screenshots. .envis ignored from Docker context and must remain untracked.- Prefer
database.url_fileor/etc/listmngr/listmngr.envwith root ownership and mode0600. - Rotate any credential suspected of exposure; redact it before attaching diagnostics.
- CLI user creation/password changes use a hidden prompt,
--password-stdin, or Unix--password-fd;--password VALUEis rejected to prevent process-list/history disclosure. Password input is bounded to 1,024 bytes and malformed/oversized streams fail closed. - Runtime CLI errors use typed, stable categories and correlation UUIDs instead of raw error chains or string-based classification. Secret-file I/O errors never include the underlying OS/decoder error or file contents.
- The unauthenticated
statusprobe targets the configured local HTTP listener, bypasses environment proxies, does not follow redirects, and times out per request. Readiness is observed from the running service, not inferred from a separate successful database connection.
Please use the repository's private security advisory form:
https://github.com/p10node/listmngr/security/advisories/new
Do not file public exploit details. Include the affected version/commit, minimal reproduction, impact, environment, and any suggested remediation. Do not include live credentials or personal subscriber data. Response timing depends on maintainer availability; no fixed SLA is promised while the project is unreleased. The machine-readable policy is at ../security.txt.