# cargo-deny config for the Hoard workspace. # # We run this in CI (.github/workflows/ci.yml → cargo-deny job) and locally # via `cargo deny check`. The defaults are picked to be strict enough to # catch new GPL-incompatible deps and known advisories, but not so strict # that minor-version churn breaks the build. [graph] all-features = true [advisories] version = 2 # We block on unmaintained crates and security advisories by default. If a # specific RUSTSEC ID is unavoidable for now, add it to `ignore` with a link # to the tracking issue explaining the exposure. yanked = "deny" ignore = [ # proc-macro-error: unmaintained, used by older proc-macro crates we # don't control (transitive via tauri-macros or similar). "RUSTSEC-2024-0370", # number_prefix: unmaintained, transitive via indicatif used by the CLI # progress bars. No safe upgrade; the replacement crate isn't drop-in. "RUSTSEC-2025-0119", # unic-* family: open-i18n/rust-unic is unmaintained as a group. Pulled # transitively by urlpattern → tauri-utils. Tauri's tracking issue: # https://github.com/rustsec/advisory-db/issues/2414. No safe upgrade. "RUSTSEC-2025-0075", # unic-char-range "RUSTSEC-2025-0080", # unic-common "RUSTSEC-2025-0081", # unic-char-property "RUSTSEC-2025-0098", # unic-ucd-version "RUSTSEC-2025-0100", # unic-ucd-ident # quick-xml < 0.41: quadratic duplicate-attribute check + unbounded # namespace allocation (both DoS-class). Transitive only: 0.39.x via # plist ← tauri-utils, 0.37.x via tauri-winrt-notification ← # notify-rust (Windows toasts). Each pins its own minor, so there's no # in-line upgrade until upstream bumps. The XML those paths parse is # local (bundle plists, toast templates we build ourselves), never # attacker-controlled. "RUSTSEC-2026-0194", "RUSTSEC-2026-0195", # tokio-tar PAX header smuggling (extended-header path/size confusion). # Threat model + compensating controls in restore.rs: # - Self-hosted archives come from the trusted hoard-server over an # authenticated session. # - Cloud archives are user-uploaded, but every extracted entry path goes # through `sanitize()` (rejects `..`, absolute and non-normal # components), so a smuggled path can't escape the destination, and # per-file SHA-256 is checked against the manifest where one exists. # - A per-restore decompression cap (H-06) bounds total bytes written, so # a size-smuggling / bomb archive aborts instead of filling the disk. # Migration to the maintained `astral-tokio-tar` fork (near drop-in) or sync # `tar` on a blocking task is tracked for when the restore path is reworked. "RUSTSEC-2025-0111", # Three unmaintained crates that arrived with Slint, none of them a # vulnerability, and all reachable only from the graphical installer. # `resvg` is what Slint draws an SVG icon with, and it brings `usvg`, # which brings `rustybuzz` for shaping and `ttf-parser` for the font # tables. Both only ever read the fonts the installer bundles into its own # binary, never a file from the user or the network. `paste` is further # away still: `i-slint-compiler` pulls `image` for the decoders, and # `image` pulls `exr` and `ravif`, which use it. That compiler is reached # through `slint-build` and `slint-macros`, so `paste` runs at build time # and ships in nothing. Each crate pins its own minor inside Slint, so # there is no in-line upgrade. Revisit when Slint's renderer moves off # resvg, or when maintained forks land. "RUSTSEC-2024-0436", # paste "RUSTSEC-2026-0192", # ttf-parser "RUSTSEC-2026-0206", # rustybuzz # glib 0.18: `VariantStrIter`'s iterator impls are unsound. It is GTK 3's # glib, the one Tauri, wry and tray-icon build on for Linux, and the fix is # 0.20, which only comes with their move to GTK 4. Nothing in Hoard # iterates a GVariant string array. "RUSTSEC-2024-0429", # unsafe-libyaml: unmaintained, no known hole. It is serde_yaml's parser, # and serde_yaml reads the Ludusavi manifest (hoard-manifest, a file from a # known upstream, never one a user uploads) and hoard-admin's YAML. # Revisit with a YAML parser in safe Rust. "RUSTSEC-2024-0449", # zbus_names, zvariant, zvariant_derive: no longer maintained as crates of # their own because zbus 6 folded them in. They come with zbus 5, the D-Bus # of hoardd, the CLI and the desktop's notification, single-instance and # portal plugins, and go when those move to zbus 6. "RUSTSEC-2026-0370", # zvariant "RUSTSEC-2026-0371", # zbus_names "RUSTSEC-2026-0372", # zvariant_derive # zip 2.x: extracting keeps SUID bits, follows planted symlinks and applies # the archive's permissions. All three live in `ZipArchive::extract` and # `ZipStreamReader::extract`, which Hoard never calls: the agent only lists # the entries of a save that is a zip, the server only writes exports. The # fix is zip 9, a major apart. "RUSTSEC-2026-0367", "RUSTSEC-2026-0368", "RUSTSEC-2026-0369", ] [licenses] version = 2 # Hoard itself is AGPL-3.0; copyleft-compatible permissive licenses are fine # for transitive deps. Anything missing from this list trips the build so we # notice new ecosystem-wide license shifts (e.g. a crate flipping to BUSL). allow = [ "MIT", "Apache-2.0", "Apache-2.0 WITH LLVM-exception", "BSD-2-Clause", "BSD-3-Clause", "BSL-1.0", "CC0-1.0", "ISC", "MPL-2.0", "Unicode-3.0", "Unicode-DFS-2016", "Zlib", "OpenSSL", "AGPL-3.0", # Slint, which the graphical installer draws its window with, is offered as # `GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR # LicenseRef-Slint-Software-3.0`. We take the GPL arm: section 13 of the # AGPL-3.0 we ship under grants permission to combine a covered work with a # GPLv3 work and convey the result, so the copyleft arm costs nothing and # asks nothing the project does not already do. The royalty-free arm would # be worse for us, not better: it exists for people who want to stay # proprietary, and it charges an attribution badge for the privilege. # Allowing one arm of an OR expression satisfies it, so the two LicenseRef # terms need no entry of their own. "GPL-3.0-only", ] confidence-threshold = 0.8 # `ring` ships under a custom license that doesn't have an SPDX identifier; # upstream has acknowledged this and the contents are equivalent to # BSD-3-Clause + OpenSSL. We accept it explicitly here. # # `libfuzzer-sys` is `(MIT OR Apache-2.0) AND NCSA`, and the `AND` means the # NCSA half cannot be satisfied by picking another arm. NCSA is the University # of Illinois licence, permissive and GPL-compatible, but we scope it to this # crate rather than opening it globally: it reaches us only as a leaf of the # image decoders Slint's compiler pulls in, and nothing else should be # arriving under it unnoticed. # # `webpki-roots` ships Mozilla's CA bundle, so it is data rather than code and # carries CDLA-Permissive-2.0 accordingly. Same reasoning: scoped, because a # data licence turning up on an actual code crate is exactly the surprise this # list exists to catch. Two versions are in the tree at once, and the exception # is keyed by name, so it covers both. exceptions = [ { allow = ["OpenSSL"], crate = "ring" }, { allow = ["NCSA"], crate = "libfuzzer-sys" }, { allow = ["CDLA-Permissive-2.0"], crate = "webpki-roots" }, ] [[licenses.clarify]] crate = "ring" expression = "ISC AND MIT AND OpenSSL" license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] [bans] # Multiple versions of the same crate bloat the build and usually indicate a # transitive dep that hasn't caught up. We allow it with a warning; CI will # not fail solely on duplicates so we can ship while waiting on upstream. multiple-versions = "warn" # `wildcards = "deny"` catches public crates that pin to `version = "*"`, # but flags our own intra-workspace path deps (which have no version field # and are therefore parsed as `*`). `allow-wildcard-paths = true` exempts # path-only deps, which resolve from the workspace and never from the registry, # so the wildcard has no security implication. wildcards = "deny" allow-wildcard-paths = true deny = [] skip = [] skip-tree = [] [sources] unknown-registry = "deny" unknown-git = "deny" allow-registry = ["https://github.com/rust-lang/crates.io-index"] allow-git = []