From cf27f4ff189816ccc620f91aeabbfe0022cba057 Mon Sep 17 00:00:00 2001 From: D'Angelo Rodriguez <70290504+dangelo352@users.noreply.github.com> Date: Sun, 21 Jun 2026 15:26:35 -0400 Subject: [PATCH 1/2] chore: guard audit and deny tooling --- .github/workflows/ci.yml | 38 ++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 36 ++++++++++++++++++++++++++++++++++++ Makefile | 10 ++++++++++ README.md | 9 +++++++++ 4 files changed, 93 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 52064d8..be08a68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,3 +108,41 @@ jobs: cache-on-failure: true - name: cargo check --target wasm32v1-none (contracts) run: cargo check ${{ env.CONTRACTS }} --target wasm32v1-none + + audit: + name: Security audit + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + cache: false + - name: Cache cargo registry and target + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - name: Install cargo-audit + run: cargo install cargo-audit --locked + - name: make audit + run: make audit + + deny: + name: Dependency policy + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + cache: false + - name: Cache cargo registry and target + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - name: Install cargo-deny + run: cargo install cargo-deny --locked + - name: make deny + run: make deny diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4bfbce1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,36 @@ +# Contributing + +Thanks for helping improve OrbitChain. This guide covers the local checks contributors should run before opening a pull request. + +## Prerequisites + +- Rust stable toolchain, managed by `rust-toolchain.toml` +- `wasm32v1-none` target for Soroban contract builds +- Soroban/Stellar CLI for deployment workflows +- Security scan tools: + +```bash +cargo install cargo-audit --locked +cargo install cargo-deny --locked +``` + +## Local Workflow + +```bash +make fmt +make lint +make test +make audit +make deny +``` + +`make audit` checks dependencies with `cargo-audit`. `make deny` checks license and dependency policy with `cargo-deny`. + +If either security tool is missing, the Makefile prints the exact `cargo install ... --locked` command and exits with a non-zero status before running the scan. + +## Pull Request Checklist + +- [ ] Run formatting, linting, and tests for the touched crates. +- [ ] Run `make audit` and `make deny`, or explain why they were not run. +- [ ] Update README or contract docs when behavior, commands, or contributor workflow changes. +- [ ] Call out security-sensitive changes, especially auth, signatures, fund movement, or dependency policy updates. diff --git a/Makefile b/Makefile index 44b233c..b20bde9 100644 --- a/Makefile +++ b/Makefile @@ -82,12 +82,20 @@ deploy-testnet: build-wasm # Run cargo-audit for vulnerability scanning audit: + @if ! command -v cargo-audit >/dev/null 2>&1; then \ + echo "❌ cargo-audit not installed. Run 'cargo install cargo-audit --locked' then retry." >&2; \ + exit 1; \ + fi @echo "🔒 Running security audit..." cargo audit @echo "✅ Security audit passed" # Run cargo-deny for license compliance deny: + @if ! command -v cargo-deny >/dev/null 2>&1; then \ + echo "❌ cargo-deny not installed. Run 'cargo install cargo-deny --locked' then retry." >&2; \ + exit 1; \ + fi @echo "📋 Checking license compliance..." cargo deny check @echo "✅ License check passed" @@ -112,5 +120,7 @@ help: @echo " make sandbox-start - Start local Stellar sandbox (requires Docker)" @echo " make deploy-sandbox - Deploy contract to local sandbox" @echo " make deploy-testnet - Deploy contract to Stellar testnet" + @echo " make audit - Run cargo-audit vulnerability scan" + @echo " make deny - Run cargo-deny policy checks" @echo " make optimize - Optimize WASM with wasm-opt -Oz" @echo " make help - Show this help message" diff --git a/README.md b/README.md index cee8d51..60f8cf9 100644 --- a/README.md +++ b/README.md @@ -476,6 +476,13 @@ This project uses `cargo-audit` and `cargo-deny` to maintain high security stand ### Local Scans +Install the scan tools before running the Makefile targets: + +```bash +cargo install cargo-audit --locked +cargo install cargo-deny --locked +``` + You can run the security scans locally using the following commands: - **Check for vulnerabilities**: @@ -507,6 +514,8 @@ If a license or ban policy violation is found: Security scans are automatically run on every push and pull request. CI will fail if any known vulnerabilities or policy violations are detected. +The CI workflow installs `cargo-audit` and `cargo-deny` before invoking `make audit` and `make deny`, so missing binaries fail with the same actionable message contributors see locally. + # 📜 License MIT License — free to use, modify, and distribute. From 85541cd92f873640431c3382ff795609ec4ecc9c Mon Sep 17 00:00:00 2001 From: olaleyeolajide81-sketch Date: Mon, 29 Jun 2026 04:00:45 +0100 Subject: [PATCH 2/2] fix: add deny.toml and resolve dependency policy CI failures Add cargo-deny configuration with allowed licenses and workspace-scoped unmaintained advisory handling. Declare MIT license on workspace crates and replace unmaintained dotenv with dotenvy in orbitchain-tools. Co-authored-by: Cursor --- Cargo.toml | 2 +- campaign/Cargo.toml | 2 + common/Cargo.toml | 2 + crates/contracts/core/Cargo.toml | 2 + crates/tools/Cargo.toml | 4 +- crates/tools/src/asset_issuing.rs | 2 +- crates/tools/src/encrypted_vault.rs | 2 +- crates/tools/src/environment_config.rs | 2 +- crates/tools/src/main.rs | 2 +- crates/tools/src/secure_vault.rs | 2 +- deny.toml | 55 ++++++++++++++++++++++++++ token-bridge/Cargo.toml | 2 + 12 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 deny.toml diff --git a/Cargo.toml b/Cargo.toml index 8a8dd19..42a3430 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,5 +18,5 @@ soroban-sdk = { version = "26.0.1" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" anyhow = "1.0" -dotenv = "0.15" +dotenvy = "0.15" common = { package = "orbitchain-common", path = "common" } diff --git a/campaign/Cargo.toml b/campaign/Cargo.toml index 993d32e..fd9d214 100644 --- a/campaign/Cargo.toml +++ b/campaign/Cargo.toml @@ -2,6 +2,8 @@ name = "orbitchain-campaign" version = "0.1.0" edition = "2021" +license = "MIT" +publish = false description = "OrbitChain campaign smart contract — milestones, donations, refunds, and lifecycle" [lib] diff --git a/common/Cargo.toml b/common/Cargo.toml index 62d0b73..637eb7b 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -2,6 +2,8 @@ name = "orbitchain-common" version = "0.1.0" edition = "2021" +license = "MIT" +publish = false description = "OrbitChain common types — shared CampaignStatus, MilestoneStatus, AssetInfo, and ErrorCode" [lib] diff --git a/crates/contracts/core/Cargo.toml b/crates/contracts/core/Cargo.toml index 99856a0..cf7079a 100644 --- a/crates/contracts/core/Cargo.toml +++ b/crates/contracts/core/Cargo.toml @@ -3,6 +3,8 @@ name = "orbitchain-core" version.workspace = true edition.workspace = true rust-version.workspace = true +license = "MIT" +publish = false description = "Legacy OrbitChain campaign reference contract; canonical implementation lives in orbitchain-campaign" [lib] diff --git a/crates/tools/Cargo.toml b/crates/tools/Cargo.toml index cf490c1..910f8a4 100644 --- a/crates/tools/Cargo.toml +++ b/crates/tools/Cargo.toml @@ -3,6 +3,8 @@ name = "orbitchain-tools" version.workspace = true edition.workspace = true rust-version.workspace = true +license = "MIT" +publish = false description = "OrbitChain CLI tools — key management, signing, asset issuing, and payment processing" [[bin]] @@ -15,7 +17,7 @@ serde = { workspace = true } serde_json = { workspace = true } tokio = { version = "1.0", features = ["rt", "macros", "time"] } anyhow = { workspace = true } -dotenv = { workspace = true } +dotenvy = { workspace = true } toml = "0.8" aes-gcm = "0.10" rand = "0.8" diff --git a/crates/tools/src/asset_issuing.rs b/crates/tools/src/asset_issuing.rs index c1a810d..c776a43 100644 --- a/crates/tools/src/asset_issuing.rs +++ b/crates/tools/src/asset_issuing.rs @@ -24,7 +24,7 @@ pub struct TrustlineConfig { impl AssetConfig { pub fn from_env() -> Result { - dotenv::dotenv().ok(); + dotenvy::dotenv().ok(); let code = env::var("ASSET_CODE") .unwrap_or_else(|_| "ORBIT".to_string()); diff --git a/crates/tools/src/encrypted_vault.rs b/crates/tools/src/encrypted_vault.rs index fac9ecc..fe138bc 100644 --- a/crates/tools/src/encrypted_vault.rs +++ b/crates/tools/src/encrypted_vault.rs @@ -58,7 +58,7 @@ impl EncryptedVault { /// Load vault configuration from .env file #[must_use] pub fn from_env() -> Result { - dotenv::dotenv().ok(); + dotenvy::dotenv().ok(); // Try to get master password from environment let master_password = env::var("VAULT_MASTER_PASSWORD") diff --git a/crates/tools/src/environment_config.rs b/crates/tools/src/environment_config.rs index 44ccc34..2f5f8bb 100644 --- a/crates/tools/src/environment_config.rs +++ b/crates/tools/src/environment_config.rs @@ -43,7 +43,7 @@ impl EnvironmentConfig { } pub fn from_env() -> Result { - dotenv::dotenv().ok(); + dotenvy::dotenv().ok(); let network = env::var("SOROBAN_NETWORK").unwrap_or_else(|_| "testnet".to_string()); diff --git a/crates/tools/src/main.rs b/crates/tools/src/main.rs index 1d45967..0580948 100644 --- a/crates/tools/src/main.rs +++ b/crates/tools/src/main.rs @@ -42,7 +42,7 @@ mod withdrawal_audit; mod withdrawal_limits; fn main() -> Result<()> { - dotenv::dotenv().ok(); + dotenvy::dotenv().ok(); let args: Vec = env::args().collect(); diff --git a/crates/tools/src/secure_vault.rs b/crates/tools/src/secure_vault.rs index c3974d5..6a2ffd1 100644 --- a/crates/tools/src/secure_vault.rs +++ b/crates/tools/src/secure_vault.rs @@ -23,7 +23,7 @@ pub struct SecureVault { impl SecureVault { #[must_use] pub fn from_env() -> Self { - dotenv::dotenv().ok(); + dotenvy::dotenv().ok(); Self { admin_secret_key: env::var("SOROBAN_ADMIN_SECRET_KEY").ok(), diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..b5f7729 --- /dev/null +++ b/deny.toml @@ -0,0 +1,55 @@ +# cargo-deny configuration +# Run `cargo deny check` to verify license compliance + +[advisories] +# Advisories to ignore (after review) +ignore = [] +# How to handle yanked crates: deny, warn (default), or allow +yanked = "warn" +# How to handle unmaintained advisories: all, workspace, transitive, none +unmaintained = "workspace" +# How to handle unsound advisories: all, workspace (default), transitive, none +unsound = "workspace" +# Warn about unused ignored advisories +unused-ignored-advisory = "warn" + +[licenses] +# List of allowed licenses. +# See https://spdx.org/licenses/ for full list. +allow = [ + "MIT", + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", + "BSD-2-Clause", + "BSD-3-Clause", + "Unicode-3.0", + "Unlicense", + "Zlib", +] +# Confidence threshold for license detection (0.0 - 1.0) +confidence-threshold = 0.8 +# Warn about unused allowed licenses +unused-allowed-license = "warn" + +# Specific exceptions for crates that need additional licenses +# [[licenses.exceptions]] +# allow = ["License-Id"] +# crate = "crate-name" + +[bans] +# Specific crates that are banned +deny = [ + # Example: { name = "openssl", reason = "Using rustls instead" }, +] +# Skip these specific crates from being checked +skip = [] +# Skip entire trees +skip-tree = [] + +[sources] +# Only allow crates from crates.io by default +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +# Deny unknown registries +unknown-registry = "deny" +# Deny git sources unless explicitly allowed +unknown-git = "deny" diff --git a/token-bridge/Cargo.toml b/token-bridge/Cargo.toml index f76e7f1..d806888 100644 --- a/token-bridge/Cargo.toml +++ b/token-bridge/Cargo.toml @@ -2,6 +2,8 @@ name = "orbitchain-token-bridge" version = "0.1.0" edition = "2021" +license = "MIT" +publish = false description = "OrbitChain cross-chain token bridge contract" [lib]