Skip to content

Replace cargo audit with cargo deny for CLI dependency policy#96

Open
juangaitanv wants to merge 8 commits into
mainfrom
deps/cargo-deny
Open

Replace cargo audit with cargo deny for CLI dependency policy#96
juangaitanv wants to merge 8 commits into
mainfrom
deps/cargo-deny

Conversation

@juangaitanv
Copy link
Copy Markdown
Contributor

Why

cargo audit only checked RustSec advisories. For a binary we ship to customers via npm + pip, that left three supply-chain dimensions untested:

  • licenses — a GPL/AGPL crate could ship in the binary
  • sources — no lock to crates.io; a git or private-registry dep would pass silently
  • bans — duplicate-version bloat, banned crates

cargo deny covers all three plus advisories from the same RustSec DB — a strict superset of cargo audit. One supply-chain tool, no redundant RustSec fetch.

Changes

  • deny.toml (new) — version = 2 schema. advisories: deny vuln/unmaintained/unsound/yanked. licenses: permissive allowlist + option-ext's file-level MPL-2.0 (transitive via dirs). bans: warn on duplicate versions / wildcards. sources: lock to crates.io (unknown-registry/unknown-git = deny).
  • harnesscmd_audit/_cmd_audit_innercmd_deny/_cmd_deny_inner; cmd_ci, dispatch, header, and help updated. Local stays lenient (skips with install hint if cargo-deny absent), CI stays strict.
  • .github/workflows/test.yml — install cargo-deny instead of cargo-audit.
  • Cargo.toml — mark corgea publish = false.
  • AGENTS.md — document ./harness deny.

Note: plan assumption corrected

The plan assumed the root corgea crate had no license and that private = { ignore = true } alone would skip it. In reality:

  1. The repo's LICENSE file is GNU LGPL-2.1, which cargo-deny reads as the crate's license (LGPL-2.1-or-later) — so it was rejected, not "no license".
  2. private.ignore only skips crates marked publish = false, which corgea was not.

Fix: added publish = false to Cargo.toml. It's factually correct — corgea ships via maturin (npm + pip), never cargo published to crates.io — and it makes private.ignore fire so our own LGPL source is exempt from the dependency-license rules. (This does not weaken the policy for actual dependencies.)

Verification

  • cargo deny check → all four checks pass (advisories / licenses / sources / bans; duplicates are warn-only).
  • ./harness deny → green ✓.
  • ./harness ci → green ✓ (clippy strict, fmt, deny, tests + coverage, 35 passed).
  • Negative check: dropping MPL-2.0 from the allowlist → option-ext v0.2.0 rejected, licenses FAILED (exit 4). The policy bites.

Base is chunk1/harness (not main) — that branch is an open PR (#92) and carries the ./harness structure this change modifies, matching the stacked-chunk convention (cf. #93).

Zero-dep bash runner (check/fix/lint/test/audit/coverage/pre-commit/ci/
post-edit/setup-hooks/suppressions/install) wrapping cargo + git.

./harness ci is the single source of truth for the strict gate: clippy
-D warnings, fmt check, cargo audit, and cargo-llvm-cov tests with a
--fail-under-lines floor (13% baseline, will ratchet up as more code
ships with tests).

GitHub Actions test.yml runs the same gate so cloud CI matches local.

AGENTS.md documents the commands. CLAUDE.md and .claude/ are gitignored
so personal agent configs stay local.
Reformats per rustfmt and applies clippy lints (needless_return,
collapsible_if, eq_ignore_ascii_case, double_ended_iterator_last,
print_with_newline, contains_key, needless_late_init,
useless_format/vec, borrowed_box, etc.) so ./harness ci's strict
clippy gate passes.

Adds unit tests for utils::api::{is_jwt, auth_headers,
check_for_warnings} so the coverage gate isn't sitting on 0%.

Pure refactor + tests: no behavior change.
…able

Pre-commit now mirrors CI (strict clippy + fmt --check + tests) instead
of running autofix, which could rewrite the working tree behind the
commit. Drops the unimplemented `install` from harness docs.

Extracts `should_warn_deprecated` from `check_for_warnings` so the 299
deprecation contract is covered by tests; deleting the branch now
fails the suite.
Resolve conflicts from #94 (network-error retry) vs the harness branch:

- src/scan.rs: keep main's retry_on_network_error wrapping around all
  four uploads (file/scan-chunk/scan/git-config); apply branch rustfmt.
- src/utils/api.rs: union merge — keep both should_warn_deprecated +
  JWT/auth/deprecation tests (branch) and retry_on_network_error +
  backoff consts + retry tests (main).
- src/authorize.rs: .err().expect() -> .expect_err() so main's new
  bind-error test passes the branch's strict clippy gate (-D warnings).

Validated: cargo build, clippy -D warnings, fmt --check, 21 tests pass.
Replace all eprintln! diagnostics with log macros routed through
env_logger, and reroute the homegrown debug() wrapper to log::debug!.
User-facing stdout (println!/print!) and the terminal TUI are left
untouched.

- Add log + env_logger; init logger in main(), bridging CORGEA_DEBUG
  (env/config) and RUST_LOG to the level, message-only format so CLI
  errors/warnings read exactly as before.
- Map eprintln! by severity: error! for genuine failures, warn! for
  recoverable/advisory paths (retries, fallbacks, best-effort uploads,
  deprecation notices), info! for the "no issues found" completion.
- debug() now delegates to log::debug!, dropping the per-call
  Config::load() (filesystem read + TOML parse) it ran on every call.

Tally: 82 error!, 18 warn!, 1 info!; 109 print(ln)! preserved.
Cover the eprintln! -> log facade migration across three layers, std-only
with no new dependencies (cargo audit stays green):

- Layer A: extract default_log_level(i8) out of init_logging and unit-test
  the ==1 (not "nonzero") debug semantics; main.rs gains its first test mod.
- Layer B: behavioral tests on no-exit log paths — sarif/coverity parsers
  returning None (exercising the migrated error! lines) and
  create_zip_from_target excluding matched files (exercising the warn block).
- Layer C: new tests/logging_e2e.rs drives the real binary at the no-network
  empty-token seam (corgea scan -> error! -> exit 1) to verify default level,
  message-only format, RUST_LOG/CORGEA_DEBUG filtering + precedence, exit
  code, and stdout/stderr separation.

Coverage rises to ~23% lines (floor 13%). Also gitignore *.profraw (the
env_clear'd e2e subprocess leaks default-named profraw under coverage) and
document the Homebrew-Rust llvm-tools fallback for ./harness coverage.
cargo audit only checked RustSec advisories. For a binary we distribute to
customers, that left licenses, sources, and bans untested. cargo deny covers
all three plus advisories from the same RustSec DB — a strict superset — so
consolidate to one supply-chain tool.

- deny.toml: advisories (deny), licenses (permissive allowlist + option-ext
  MPL-2.0), sources (lock to crates.io), bans (warn on duplicates/wildcards).
- harness: cmd_audit/_cmd_audit_inner -> cmd_deny/_cmd_deny_inner; ci + dispatch
  + help/header updated. Local stays lenient, CI stays strict.
- test.yml: install cargo-deny instead of cargo-audit.
- Cargo.toml: mark corgea publish=false. It ships via maturin (npm + pip),
  never crates.io, and its own LICENSE is LGPL-2.1; publish=false makes it
  "private" so deny skips license checks on our own source.
- AGENTS.md: document ./harness deny.

Verified: cargo deny check passes (advisories/licenses/sources/bans), negative
check fails when MPL-2.0 is dropped, ./harness ci green.
cursor[bot]
cursor Bot previously requested changes Jun 4, 2026
Comment thread deny.toml
Comment thread deny.toml Outdated
Comment thread harness Outdated
Base automatically changed from chunk1/harness to main June 8, 2026 08:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant