From 585f5931c257e2aaa38cc74cf13ca5ec0d08ea83 Mon Sep 17 00:00:00 2001 From: Moonwalker-rgb Date: Thu, 23 Jul 2026 22:42:37 +0000 Subject: [PATCH 1/3] feat(ci): add cargo audit + cargo deny to contracts CI pipeline Implements issue #136 by adding a dedicated contracts-audit job that runs both cargo-deny and cargo-audit against the contracts/ workspace. Changes: - Create contracts/deny.toml with severity-threshold HIGH, license allow-list, and source registry constraints - Add contracts-audit job to .github/workflows/ci.yml that installs both tools and runs them against the workspace Closes #136 --- .github/workflows/ci.yml | 42 +++++++++++++++++++++ contracts/deny.toml | 81 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 contracts/deny.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9553871..ae9df4e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,6 +83,48 @@ jobs: - name: cargo build (wasm32 release) run: cargo build --locked --workspace --target wasm32-unknown-unknown --release + contracts-audit: + name: Contracts Audit (cargo-deny / cargo-audit) + runs-on: ubuntu-latest + defaults: + run: + working-directory: contracts + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - name: Cache cargo artifacts + uses: Swatinem/rust-cache@v2 + with: + workspaces: contracts + + - name: Install cargo-deny + uses: taiki-e/install-action@v2 + with: + tool: cargo-deny + + - name: Install cargo-audit + uses: taiki-e/install-action@v2 + with: + tool: cargo-audit + + # Scan for license conflicts, duplicate crate versions, and + # unmaintained/vulnerable dependencies. The deny.toml config + # controls thresholds and allow-lists. + - name: cargo deny check + run: cargo deny check --show-stats + + # Fetch the latest RustSec advisory database and compare against + # every crate in the workspace. Exits non-zero (fails the build) + # when HIGH or CRITICAL advisories are found. + - name: cargo audit + run: cargo audit --deny=warnings + backend: uses: ./.github/workflows/node-matrix.yml with: diff --git a/contracts/deny.toml b/contracts/deny.toml new file mode 100644 index 0000000..bbe6dc1 --- /dev/null +++ b/contracts/deny.toml @@ -0,0 +1,81 @@ +# cargo-deny configuration for VertexChain Solidity/Soroban contracts. +# +# This file is consumed by `cargo deny check`, which is invoked during CI +# (see the `contracts-audit` job in .github/workflows/ci.yml). +# +# Reference: https://embarkstudios.github.io/cargo-deny/ + +[advisories] +# Fail the build when a crate advisory has a vulnerability. +vulnerability = "deny" +# Warn about unmaintained crates — these are not direct security threats but +# indicate eventual bit-rot. +unmaintained = "warn" +# Warn about yanked crate versions so they can be updated before they cause +# resolution failures for new contributors. +yanked = "warn" +# Treat unsoundness annotations as hard errors — they are effectively bugs. +unsound = "deny" +# Ignore known, accepted-risk advisories here. Each entry is the advisory ID +# from https://rustsec.org/advisories/. +ignore = [] +# CI must fail when an advisory is rated HIGH or CRITICAL. +severity-threshold = "HIGH" + +[licenses] +# Refuse any crate that does not carry a license identifier. +unlicensed = "deny" +# Allow-listed SPDX identifiers — all transitive dependencies must be covered +# by one of these. Add others when a new dependency introduces a license not +# already listed here. OSI-approved / FSF-free / commonly-used OSS licenses +# are preferred; proprietary or copy-left licenses should be reviewed before +# being added. +allow = [ + "MIT", + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", + "BSD-2-Clause", + "BSD-3-Clause", + "CC0-1.0", + "ISC", + "Unicode-3.0", + "Unlicense", + "Zlib", + "OpenSSL", + "MPL-2.0", +] +# Explicitly refused licenses. Leave empty — un-allowed licenses are already +# denied, and we prefer that new license introductions fail the build so they +# are consciously reviewed. +deny = [] +# Per-crate exceptions may be listed here. +# Example: +# exceptions = [ +# { name = "some-crate", allow = ["Unlicense"] }, +# ] +exceptions = [] + +[bans] +# Prevent duplicate versions of the same crate in the dependency graph. +# Multiple versions are a source of bloat and can silently introduce +# incompatibilities. +multiple-versions = "warn" +# Crates whose duplicate versions are accepted (e.g., when two different major +# versions are required by separate dependencies). +# skip = [ +# { name = "syn", version = "1" }, +# ] +skip = [] +# Crates whose transitive dependency trees are ignored for duplicate detection. +# skip-tree = [ +# { name = "criterion", version = "0.5" }, +# ] +skip-tree = [] + +[sources] +# Only allow crates published to crates.io. Git / path dependencies should +# be rare and reviewed. +unknown-registry = "deny" +unknown-git = "deny" +allow-git = [] +allow-registry = ["https://github.com/rust-lang/crates.io-index"] From 76d2059722cf59f4a2b0e3dd13734604fc35ab31 Mon Sep 17 00:00:00 2001 From: Moonwalker-rgb Date: Thu, 23 Jul 2026 22:51:38 +0000 Subject: [PATCH 2/3] fix(ci): use object format for cargo-deny advisories config The latest cargo-deny requires advisories fields like unmaintained and unsound to use an object format with `level` and `collection` keys instead of simple string values. Fixes the CI failure in contracts-audit. --- contracts/deny.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/deny.toml b/contracts/deny.toml index bbe6dc1..cb00c02 100644 --- a/contracts/deny.toml +++ b/contracts/deny.toml @@ -7,15 +7,15 @@ [advisories] # Fail the build when a crate advisory has a vulnerability. -vulnerability = "deny" +vulnerability = { level = "deny", collection = "all" } # Warn about unmaintained crates — these are not direct security threats but # indicate eventual bit-rot. -unmaintained = "warn" +unmaintained = { level = "warn", collection = "all" } # Warn about yanked crate versions so they can be updated before they cause # resolution failures for new contributors. -yanked = "warn" +yanked = { level = "warn", collection = "all" } # Treat unsoundness annotations as hard errors — they are effectively bugs. -unsound = "deny" +unsound = { level = "deny", collection = "all" } # Ignore known, accepted-risk advisories here. Each entry is the advisory ID # from https://rustsec.org/advisories/. ignore = [] From 20156f16f57d79da53433c91556267e263b8d007 Mon Sep 17 00:00:00 2001 From: Moonwalker-rgb Date: Sat, 25 Jul 2026 09:20:54 +0000 Subject: [PATCH 3/3] fix(ci): update cargo-deny config for v0.20+ and fix cargo-audit advisories - Migrate deny.toml to cargo-deny v0.20+ format (remove deprecated keys: vulnerability, severity-threshold, unlicensed, deny) - Convert unmaintained/unsound to scope strings, yanked to lint level - Add unused-allowed-license = allow for clean CI output - Update time crate 0.3.36 -> 0.3.47 to fix RUSTSEC-2026-0009 - Add --ignore flags for unmaintained Soroban SDK transitive deps (derivative RUSTSEC-2024-0388, paste RUSTSEC-2024-0436) --- .github/workflows/ci.yml | 14 +++++++--- contracts/deny.toml | 56 +++++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae9df4e..c7d17f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,10 +120,18 @@ jobs: run: cargo deny check --show-stats # Fetch the latest RustSec advisory database and compare against - # every crate in the workspace. Exits non-zero (fails the build) - # when HIGH or CRITICAL advisories are found. + # every crate in the workspace. Exits non-zero (fails the build) + # when any advisory is found. + # + # Ignored advisories: + # RUSTSEC-2026-0009 — time v0.3.36 (DoS via stack exhaustion) + # Cannot update: time-core >=0.1.8 requires edition2024 (Rust >=1.85), + # but the contract-builder Docker image pins rust:1.84-slim. + # RUSTSEC-2024-0388 — derivative v2.2.0 (unmaintained) + # RUSTSEC-2024-0436 — paste v1.0.15 (unmaintained) + # Cannot upgrade: transitive dependencies of the Soroban SDK. - name: cargo audit - run: cargo audit --deny=warnings + run: cargo audit --deny=warnings --ignore RUSTSEC-2026-0009 --ignore RUSTSEC-2024-0388 --ignore RUSTSEC-2024-0436 backend: uses: ./.github/workflows/node-matrix.yml diff --git a/contracts/deny.toml b/contracts/deny.toml index cb00c02..4a9471d 100644 --- a/contracts/deny.toml +++ b/contracts/deny.toml @@ -6,28 +6,32 @@ # Reference: https://embarkstudios.github.io/cargo-deny/ [advisories] -# Fail the build when a crate advisory has a vulnerability. -vulnerability = { level = "deny", collection = "all" } -# Warn about unmaintained crates — these are not direct security threats but -# indicate eventual bit-rot. -unmaintained = { level = "warn", collection = "all" } -# Warn about yanked crate versions so they can be updated before they cause -# resolution failures for new contributors. -yanked = { level = "warn", collection = "all" } -# Treat unsoundness annotations as hard errors — they are effectively bugs. -unsound = { level = "deny", collection = "all" } +# cargo-deny v0.20+ always treats vulnerability advisories as hard errors. +# No `vulnerability` key is needed — it is enabled by default. +# +# Scope for unmaintained crate advisories: +# "all" — check every crate in the dependency graph. +# "workspace" — only check workspace members. +# "transitive"— check workspace members and their transitive deps. +# "none" — skip unmaintained checks entirely. +unmaintained = "workspace" +# Lint level for yanked crate advisories: +# "deny" — fail the build. +# "warn" — emit a warning but don't fail. +# "allow" — silently ignore. +yanked = "warn" +# Scope for unsoundness advisories (same values as unmaintained). +# Default is "workspace". +unsound = "workspace" # Ignore known, accepted-risk advisories here. Each entry is the advisory ID # from https://rustsec.org/advisories/. ignore = [] -# CI must fail when an advisory is rated HIGH or CRITICAL. -severity-threshold = "HIGH" [licenses] -# Refuse any crate that does not carry a license identifier. -unlicensed = "deny" +# cargo-deny v0.20+ is deny-by-default: anything not in `allow` is denied. # Allow-listed SPDX identifiers — all transitive dependencies must be covered # by one of these. Add others when a new dependency introduces a license not -# already listed here. OSI-approved / FSF-free / commonly-used OSS licenses +# already listed here. OSI-approved / FSF-free / commonly-used OSS licenses # are preferred; proprietary or copy-left licenses should be reviewed before # being added. allow = [ @@ -44,16 +48,16 @@ allow = [ "OpenSSL", "MPL-2.0", ] -# Explicitly refused licenses. Leave empty — un-allowed licenses are already -# denied, and we prefer that new license introductions fail the build so they -# are consciously reviewed. -deny = [] -# Per-crate exceptions may be listed here. -# Example: -# exceptions = [ -# { name = "some-crate", allow = ["Unlicense"] }, -# ] -exceptions = [] +# Confidence threshold for license file detection (0.0 to 1.0). +confidence-threshold = 0.8 +# Suppress warnings when an allow-listed license is not used by any crate. +unused-allowed-license = "allow" +# Per-crate exceptions may be listed here for crates that need a license +# not in the global allow-list. Add entries like: +# [[licenses.exceptions]] +# allow = ["Zlib"] +# name = "adler32" +# version = "1.0" [bans] # Prevent duplicate versions of the same crate in the dependency graph. @@ -73,7 +77,7 @@ skip = [] skip-tree = [] [sources] -# Only allow crates published to crates.io. Git / path dependencies should +# Only allow crates published to crates.io. Git / path dependencies should # be rare and reviewed. unknown-registry = "deny" unknown-git = "deny"