Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/_ci-security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,14 @@ jobs:
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
- name: Dependency policy
run: cargo-deny check
- name: Runtime dependency policy
run: cargo-deny --exclude buzz-mesh-smoke check
- name: Mesh-LLM smoke dependency policy
run: cargo-deny --manifest-path crates/buzz-mesh-smoke/Cargo.toml check --config deny-mesh.toml
- name: Verify desktop dependency exceptions
run: python3 scripts/check-desktop-dependency-exceptions.py
- name: Desktop dependency policy
run: cargo-deny --manifest-path desktop/src-tauri/Cargo.toml check --config deny-desktop.toml


results:
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ jobs:
- 'scripts/test-postgres-test-discovery.sh'
- 'scripts/test-postgres-test-wrapper.sh'
- 'deny.toml'
- 'deny-mesh.toml'
- 'deny-desktop.toml'
- 'scripts/check-desktop-dependency-exceptions.py'
- '.github/workflows/ci.yml'
- '.github/workflows/_ci-*.yml'
- 'docker-compose.yml'
Expand Down Expand Up @@ -255,10 +258,10 @@ jobs:
security-domain:
name: Security Domain
needs: [changes]
if: github.event_name == 'push' || needs.changes.outputs.rust == 'true'
if: github.event_name == 'push' || (needs.changes.outputs.rust == 'true' || needs.changes.outputs.desktop-rust == 'true')
uses: ./.github/workflows/_ci-security.yml
with:
rust: ${{ needs.changes.outputs.rust == 'true' }}
rust: ${{ (needs.changes.outputs.rust == 'true' || needs.changes.outputs.desktop-rust == 'true') }}

rust-lint:
name: Rust Lint
Expand Down Expand Up @@ -430,7 +433,7 @@ jobs:

security:
name: Security
if: always() && (needs.changes.result != 'success' || ((github.event_name == 'push' || needs.changes.outputs.rust == 'true')))
if: always() && (needs.changes.result != 'success' || ((github.event_name == 'push' || needs.changes.outputs.rust == 'true' || needs.changes.outputs.desktop-rust == 'true')))
needs: [changes, security-domain]
runs-on: ubuntu-latest
timeout-minutes: 5
Expand Down
34 changes: 17 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 18 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,24 @@ controllers.

### Dependency Management

We use `cargo audit` in CI to scan for known vulnerabilities in dependencies.
CI checks runtime, Mesh-LLM smoke, and desktop dependencies with separate
`cargo-deny` policies. The desktop policy checks every feature, including the
optional `mesh-llm` graph that release builds enable. Its ZIP advisory
exceptions cover only `zip` below 9 under `mesh-llm`. The guard script verifies
that the default desktop graph has no `zip` below 9, that `mesh-llm` is its
only consumer, and that `mesh-llm` never calls the affected extract APIs. It also
verifies that the `ringbuf` exception remains an unused dependency of the pinned
NetEQ source.

Run the same checks locally from the repository root:

```bash
cargo-deny --exclude buzz-mesh-smoke check
cargo-deny --manifest-path crates/buzz-mesh-smoke/Cargo.toml check --config deny-mesh.toml
python3 scripts/check-desktop-dependency-exceptions.py
cargo-deny --manifest-path desktop/src-tauri/Cargo.toml check --config deny-desktop.toml
```

`#![deny(unsafe_code)]` is enforced across all crates — no unsafe Rust.

---
Expand Down
139 changes: 139 additions & 0 deletions deny-desktop.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Desktop policy. Every feature is checked, including the Mesh-LLM graph that
# release builds enable.
[graph]
all-features = true

[advisories]
ignore = [
# zip 8.6.0 < 9: extraction keeps setuid/setgid bits (0367), creates
# symlinks that escape the destination (0368) and applies permissions through
# symlinks (0369). Only path: the optional mesh-llm feature → mesh-llm v0.76.2
# → mesh-llm-host-runtime / mesh-llm-plugin-manager / mesh-llm-system → zip 8.
# No mesh-llm release uses zip 9 yet. Its readers copy entries through
# enclosed_name() and never call the affected extract APIs;
# check-desktop-dependency-exceptions.py verifies that, and that the default
# graph has no zip below 9. Remove on the mesh-llm bump that moves to zip 9.
{ id = "RUSTSEC-2026-0367", reason = "zip 8.6.0 only via the optional mesh-llm feature; manual entry copy, never extracts; guard-verified" },
{ id = "RUSTSEC-2026-0368", reason = "zip 8.6.0 only via the optional mesh-llm feature; manual entry copy, never extracts; guard-verified" },
{ id = "RUSTSEC-2026-0369", reason = "zip 8.6.0 only via the optional mesh-llm feature; manual entry copy, never extracts; guard-verified" },
# These desktop transitive dependencies have maintenance notices, not
# vulnerability reports. Remove when opus, user-idle, and GTK migrate.
{ id = "RUSTSEC-2026-0150", reason = "audiopus_sys via opus; unmaintained with no safe upstream upgrade" },
{ id = "RUSTSEC-2020-0168", reason = "mach via user-idle; unmaintained with no safe upstream upgrade" },
{ id = "RUSTSEC-2024-0370", reason = "proc-macro-error via GTK glib-macros; unmaintained with no safe upstream upgrade" },
# neteq 0.8.3 declares ringbuf but never uses it: its buffer is VecDeque.
# CI verifies the pinned source has no reference and no other graph parent.
# Remove when neteq drops its unused dependency or upgrades to ringbuf 0.5.2.
{ id = "RUSTSEC-2026-0293", reason = "unused dependency of pinned neteq 0.8.3; check-desktop-dependency-exceptions.py verifies it stays unreachable" },
# 0.26.11 contains no roots: it re-exports webpki-roots 1.x. Both lockfiles
# resolve that parent to 1.0.10, which removes the distrusted emSign root.
# The advisory also flags this compatibility wrapper. The bans below keep
# every other affected version forbidden while this false positive remains.
{ id = "RUSTSEC-2026-0381", reason = "0.26.11 only re-exports the patched 1.0.10 roots; bans enforce patched versions for all other releases" },
# instant 0.1.13 — unmaintained crate. Transitive dep: nostr → instant.
# Will be resolved when nostr crate updates its dependencies.
{ id = "RUSTSEC-2024-0384", reason = "transitive dep via nostr; no upstream fix available" },
# paste 1.0.15 — unmaintained. Transitive dep: mesh-llm → iroh → netlink-* → paste.
# No safe upgrade available; tracked for upstream (iroh/netlink) replacement.
{ id = "RUSTSEC-2024-0436", reason = "transitive dep via mesh-llm → iroh → netlink; no upstream fix available" },
# quick-xml < 0.41: quadratic runtime on duplicate-attribute check (0194) and
# unbounded namespace-declaration allocation in NsReader (0195). Both DoS-class,
# requiring attacker-controlled XML. Our two locked versions only parse trusted
# input: 0.38.4 (rust-s3/aws-creds — responses from our own S3/MinIO endpoint)
# and 0.39.4 (mesh-llm → iroh → netdev → plist — local macOS system plists).
# Patched release (>= 0.41.0) is unreachable until rust-s3 and plist/netdev bump;
# remove these when upstream catches up.
{ id = "RUSTSEC-2026-0194", reason = "transitive via rust-s3 and mesh-llm→plist; trusted-input XML only; no upstream fix available yet" },
{ id = "RUSTSEC-2026-0195", reason = "transitive via rust-s3 and mesh-llm→plist; trusted-input XML only; no upstream fix available yet" },
# unsafe-libyaml 0.2.11 — unmaintained (repository archived 2024-03-25), not a
# vulnerability report. Transitive dep: serde_yaml → unsafe-libyaml. The serde_yaml_ng
# fork uses the same crate, so removing it means migrating YAML parsing in
# buzz-agent, buzz-persona, buzz-relay, buzz-workflow and desktop.
{ id = "RUSTSEC-2024-0449", reason = "transitive via serde_yaml; unmaintained, not vulnerable; removal needs a YAML parser migration" },
# zbus 5 D-Bus crates (zvariant, zbus_names, zvariant_derive) — unmaintained,
# not vulnerable. Only path: buzz-mesh-smoke → mesh-llm → keyring 4 →
# zbus-secret-service-keyring-store / secret-service 5 → zbus 5.
# Remove when mesh-llm's keyring stack moves off zbus 5.
{ id = "RUSTSEC-2026-0370", reason = "zvariant via buzz-mesh-smoke → mesh-llm → keyring 4 → secret-service 5 → zbus 5; unmaintained, not vulnerable" },
{ id = "RUSTSEC-2026-0371", reason = "zbus_names via buzz-mesh-smoke → mesh-llm → keyring 4 → secret-service 5 → zbus 5; unmaintained, not vulnerable" },
{ id = "RUSTSEC-2026-0372", reason = "zvariant_derive via buzz-mesh-smoke → mesh-llm → keyring 4 → secret-service 5 → zbus 5; unmaintained, not vulnerable" },
]

[licenses]
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",
"OpenSSL",
"CC0-1.0",
"CDLA-Permissive-2.0",
"MITNFA",
"MPL-2.0",
"BSL-1.0",
"Unlicense",
# minicbor's permissive, OSI-approved license. Used for strict App Attest
# assertion CBOR parsing and also transitively by appattest.
"BlueOak-1.0.0",
# bzip2/libbzip2's permissive BSD-like license. New via desktop zip/bzip2
# transitive deps; compatible with Apache-2.0 distribution.
"bzip2-1.0.6",
]
confidence-threshold = 0.8

# mesh-llm workspace crates (pinned git dep) omit a per-crate `license` field in
# their manifests, so cargo-deny reports them as unlicensed. The mesh-llm repo is
# licensed "MIT OR Apache-2.0" (workspace Cargo.toml + top-level LICENSE = Apache-2.0);
# clarify each pulled-in member to that expression. Remove once mesh sets the field
# upstream (filed).
[[licenses.clarify]]
crate = "mesh-llm-config"
expression = "MIT OR Apache-2.0"
license-files = []

[[licenses.clarify]]
crate = "mesh-llm-gpu-bench"
expression = "MIT OR Apache-2.0"
license-files = []

[[licenses.clarify]]
crate = "mesh-llm-host-runtime"
expression = "MIT OR Apache-2.0"
license-files = []

[[licenses.clarify]]
crate = "mesh-llm-plugin"
expression = "MIT OR Apache-2.0"
license-files = []

[[licenses.clarify]]
crate = "mesh-llm-system"
expression = "MIT OR Apache-2.0"
license-files = []

[[licenses.clarify]]
crate = "mesh-mixture-of-agents"
expression = "MIT OR Apache-2.0"
license-files = []

[[licenses.clarify]]
crate = "buzz-desktop"
expression = "Apache-2.0"
license-files = []

[licenses.private]
ignore = true

[bans]
# RUSTSEC-2026-0381: allow only the 0.26.11 re-export wrapper or patched roots.
deny = [
{ name = "webpki-roots", version = "<0.26.11" },
{ name = "webpki-roots", version = ">0.26.11, <1.0.10" },
]
multiple-versions = "warn"
wildcards = "allow"
Loading
Loading