Gunnar

nordisk/gunnar

deny.toml

# cargo-deny configuration for gunnar — the "0 known CVE" gate.
#
# The deploy pipeline (`<ops>/deploy.sh` `door cve`) runs, on gunnar's own Cargo.lock:
#     cargo deny --no-default-features check advisories
#     cargo deny --no-default-features check bans
# and aborts the deploy if either goes red, exactly like a KVM boot door.
#
# Every `ignore` below is a triaged, dated decision — NOT a blanket mute.
# `yanked = "deny"` and the `bans` duplicate check are deliberately strict so a
# NEW yanked crate or a NEW advisory fails the door by name. Proven red-then-green
# 2026-08-31 (see `<ops>/deploy.sh` door history).

[advisories]
version = 2
# Fail the door on any crate the registry has yanked. A yanked crate is a warning,
# not a CVE, but the gate treats it as a hard stop so it cannot rot in silently.
yanked = "deny"
ignore = [
    # ---------------------------------------------------------------------------
    # RUSTSEC-2023-0071 — Marvin Attack, RSA private-key timing sidechannel.
    # Crate: rsa 0.10.0-rc.18. No safe upstream version exists.
    # Triaged 2026-08-31 (lane CVE-GATE):
    #   rsa enters ONLY through russh's default `rsa` feature (and the transitive
    #   ssh-key/rsa) — a capability of the SSH stack, not something gunnar calls.
    #   gunnar's SSH server (gunnar-ssh) mints and uses an ed25519 host key
    #   EXCLUSIVELY: gunnar-core::host_key::mint_ed25519 / Algorithm::Ed25519,
    #   with no code path that generates or signs with an RSA host key. The
    #   network-facing server therefore performs no RSA private-key operation.
    #   Verifying a client's RSA signature is not a private-key op and leaks no
    #   secret. The only path that could exercise an RSA private-key op is a
    #   gunnar CLIENT dialling with an operator-supplied ~/.ssh/id_rsa (client
    #   key search order: id_ed25519, id_ecdsa, id_rsa) — and its timing is
    #   observable only by the trusted remote it dials, not by an attacker.
    #   => The Marvin timing sidechannel is not reachable on gunnar's attack
    #      surface. Fully removing rsa from the tree is possible (russh
    #      default-features = false, drop `rsa`) but requires the same change in
    #      sibling nornir-git and pageant; tracked as a project decision.
    { id = "RUSTSEC-2023-0071", reason = "rsa is unreachable: gunnar's SSH server host key is ed25519-only (gunnar-core mint_ed25519); no RSA private-key op on any network-facing path. Triaged 2026-08-31. Full drop needs russh feature change in nornir-git+pageant (decision)." },

    # ---------------------------------------------------------------------------
    # RUSTSEC-2026-0222, RUSTSEC-2026-0269, RUSTSEC-2026-0316 (wasmtime 45.0.3):
    #   ignores REMOVED 2026-10-06 (round 4 triage). The lock carries wasmtime
    #   49.0.2 only (nornir-build-thing moved off ^45), which no advisory names;
    #   cargo-deny warned "advisory was not encountered" for all three. A stale
    #   ignore would mute the next wasmtime advisory of the same id for nothing.

    # ---------------------------------------------------------------------------
    # RUSTSEC-2026-0173 — proc-macro-error2 is unmaintained (not a vulnerability).
    # Crate: proc-macro-error2 2.0.1. No maintained release exists.
    # Triaged 2026-10-06 (round 4):
    #   it enters ONLY as a PROC-MACRO of i18n-embed-fl, which `age` 0.11.5 needs
    #   unconditionally (no feature turns it off) for its CLI's localized messages;
    #   gunnar-crypt takes age with default-features = false, features ssh+armor
    #   (encrypted repositories). A proc-macro runs in the compiler and links into
    #   no binary: nothing of it is on any shipped gunnar's runtime path. The fix
    #   is upstream (age/i18n-embed-fl moving off it); re-check at every age bump.
    { id = "RUSTSEC-2026-0173", reason = "proc-macro-error2 2.0.1 is unmaintained (no vulnerability): a compile-time proc-macro of i18n-embed-fl, which age 0.11.5 needs unconditionally (gunnar-crypt: age ssh+armor). Links into no binary. No maintained release. Triaged 2026-10-06; re-check at every age bump." },

    # ---------------------------------------------------------------------------
    # RUSTSEC-2026-0374 — basic-toml is unmaintained (not a vulnerability).
    # Crate: basic-toml 0.1.10. No safe upgrade exists.
    # Triaged 2026-10-28 (lane OX-CVE):
    #   it enters ONLY through i18n-config 0.4.8 (the newest; 0.4.7+ moved from
    #   toml to basic-toml), which the PROC-MACROS i18n-embed-impl and
    #   i18n-embed-fl read i18n.toml with at compile time; `age` 0.11.5 needs them
    #   unconditionally (gunnar-crypt: age ssh+armor), the same path as
    #   RUSTSEC-2026-0173 above. It parses this build's own i18n.toml, never
    #   input from a user or the network, and links into no binary. It cannot be
    #   replaced from here (a third-party proc-macro's dependency); the fix is
    #   upstream (i18n-config moving to toml). Re-check at every age bump.
    { id = "RUSTSEC-2026-0374", reason = "basic-toml 0.1.10 is unmaintained (no vulnerability): only a compile-time dependency of i18n-config 0.4.8 (newest), read by the proc-macros i18n-embed-impl/i18n-embed-fl that age 0.11.5 needs unconditionally (gunnar-crypt). Parses the build's own i18n.toml, links into no binary. No maintained release. Triaged 2026-10-28; re-check at every age bump." },
]

[bans]
# Duplicate versions are a warning (the tree legitimately carries a few during
# the russh/ssh-key rc churn), but they are surfaced so they cannot pile up
# unseen. Flip to "deny" once the tree is consolidated.
multiple-versions = "warn"
wildcards = "allow"
# No crate is outright banned yet; add entries here to hard-fail a known-bad dep.
deny = []

[sources]
unknown-registry = "deny"
unknown-git = "allow"
allow-registry = ["https://github.com/rust-lang/crates.io-index"]

[licenses]
# Licenses are gated elsewhere; keep this permissive so `check advisories`/`bans`
# never trip on a license surprise. Left intentionally broad.
version = 2
allow = [
    "MIT", "Apache-2.0", "Apache-2.0 WITH LLVM-exception", "BSD-2-Clause",
    "BSD-3-Clause", "ISC", "Unicode-3.0", "Unicode-DFS-2016", "Zlib",
    "MPL-2.0", "CC0-1.0", "0BSD", "BSL-1.0",
]
confidence-threshold = 0.8