Automated security analysis pipeline for Credence smart contracts. This document covers the security scanning tools, how to run them locally, interpret results, and manage findings.
The security scanning pipeline runs automatically on every push and pull request to main and develop branches. It consists of three complementary tools:
- cargo-audit - Scans dependencies for known security vulnerabilities
- cargo-clippy - Static analysis with security-focused lints
- cargo-geiger - Detects unsafe code blocks in contracts
The pipeline will FAIL on:
- Dependency vulnerabilities or audit warnings reported by
cargo audit --deny warnings - Security lint violations (clippy with
-D warnings)
The pipeline will PASS WITH WARNINGS on:
- Unsafe code detected in contracts (informational only)
On pull requests, the dependency audit job posts or updates a single cargo audit
comment. The comment includes the command that ran, vulnerability and warning
counts, and a concise list of reported vulnerabilities with advisory IDs,
affected crates, severity, patched versions, and advisory URLs. The full JSON
report remains available as the cargo-audit-report workflow artifact.
Install the required tools:
# Install cargo-audit (version pinned to match CI)
cargo install cargo-audit --version 0.22.0 --locked
# Install cargo-geiger (version pinned to match CI)
cargo install cargo-geiger --version 0.12.0 --locked
# Clippy is included with rustup
rustup component add clippyScan all dependencies for known vulnerabilities:
# Basic scan
cargo audit
# Generate JSON report
cargo audit --json > audit-report.json
# Match the CI dependency gate
cargo audit --deny warningsWhat it checks:
- Known CVEs in direct and transitive dependencies
- Unmaintained crates
- Yanked crate versions
- Supports CVSS 3.x and 4.0 scoring (requires cargo-audit 0.22.0+)
Run static analysis with security-focused lints:
# Run the same checks as CI
cargo clippy --all-targets -- \
-W clippy::integer_arithmetic \
-W clippy::unwrap_used \
-W clippy::expect_used \
-W clippy::panic \
-W clippy::todo \
-W clippy::unimplemented \
-W clippy::indexing_slicing \
-W clippy::cast_possible_truncation \
-W clippy::cast_sign_loss \
-D warningsWhat it checks:
- Integer overflow/underflow risks
- Panic-inducing operations (unwrap, expect, panic!)
- Unsafe type casting
- Array indexing without bounds checks
- Incomplete code markers (todo!, unimplemented!)
Detect unsafe code blocks in contracts:
# Scan contracts directory
cd contracts
cargo geiger
# Generate markdown report
cargo geiger --output-format GitHubMarkdown > geiger-report.md
# Generate JSON for programmatic analysis
cargo geiger --output-format Json > geiger-report.jsonWhat it checks:
- Unsafe functions, expressions, implementations
- Unsafe traits and methods
- Unsafe code in dependencies vs. your code
{
"vulnerabilities": {
"list": [
{
"advisory": {
"id": "RUSTSEC-2024-XXXX",
"severity": "critical",
"title": "Vulnerability description",
"description": "Detailed explanation"
},
"versions": {
"patched": [">=1.2.3"]
}
}
]
}
}Severity levels:
critical- Immediate action requiredhigh- Review and plan remediationmedium- Monitor and update when convenientlow- Informational
CI treats every vulnerability and audit warning as blocking because the workflow
runs cargo audit --deny warnings.
Action items:
- Update affected dependencies to patched versions
- If no patch available, consider alternatives or mitigations
- Document accepted risks in
audit.tomlif needed
Clippy outputs warnings/errors with file location and explanation:
warning: used `unwrap()` on a `Result` value
--> contracts/credence_bond/src/lib.rs:42:18
|
42 | let value = result.unwrap();
| ^^^^^^^^^^^^^^
|
= help: consider using `expect()` with a meaningful message or proper error handling
Common security lints:
integer_arithmetic- Potential overflow/underflowunwrap_used- Can panic on None/Errindexing_slicing- Can panic on out-of-boundscast_possible_truncation- Data loss in type conversion
Action items:
- Replace
unwrap()with proper error handling - Use checked arithmetic operations
- Add bounds checks before indexing
- Use safe type conversions
Metric output format: x/y
x = unsafe code used by the build
y = total unsafe code found in the crate
Functions Expressions Impls Traits Methods Dependency
0/0 0/0 0/0 0/0 0/0 credence_bond
2/2 5/5 0/0 0/0 1/1 soroban-sdk
Interpretation:
- First number: unsafe code actually used
- Second number: total unsafe code available
- Focus on your contract crates (credence_*)
- Dependencies may have unsafe code (expected for low-level libs)
Action items:
- Minimize unsafe code in contracts
- Document why unsafe is necessary if used
- Prefer safe abstractions from dependencies
Download reports from GitHub Actions artifacts:
- Go to the workflow run
- Scroll to "Artifacts" section
- Download relevant reports
Critical/High:
- Does it affect contract logic?
- Can it be exploited?
- Is there a patch available?
Medium/Low:
- What's the attack surface?
- Is the vulnerable code path reachable?
- What's the remediation timeline?
Option A: Update Dependencies
# Update specific crate
cargo update -p <crate-name>
# Update all dependencies
cargo update
# Test after update
cargo testOption B: Replace Dependency
# In Cargo.toml, replace vulnerable crate
[dependencies]
# old-crate = "1.0"
new-crate = "2.0"Option C: Accept Risk (with documentation)
Create audit.toml in workspace root:
[advisories]
ignore = [
"RUSTSEC-2024-XXXX", # Reason: Not exploitable in our context
]Option D: Fix Code Issues
For clippy findings, refactor code:
// Before (unsafe)
let value = result.unwrap();
// After (safe)
let value = result.expect("Failed to get value: this should never happen");
// Or better:
let value = result.map_err(|e| Error::InvalidValue)?;# Run all security scans locally
cargo audit
cargo clippy --all-targets -- -D warnings
cd contracts && cargo geiger
# Run tests
cargo testInclude in your PR description:
- Which finding was addressed
- How it was fixed
- Why the approach was chosen
- Test results
Scanner versions are pinned in .github/workflows/security.yml to ensure deterministic results.
- New scanner version with important features
- Security fix in the scanner itself
- Compatibility with new Rust version
- Test locally first:
# Install new version
cargo install cargo-audit --version 0.21.0 --locked
# Run full scan
cargo audit- Update workflow file:
- name: Install cargo-audit
run: cargo install cargo-audit --version 0.21.0 --locked-
Update this documentation with new version
-
Test in CI by pushing to a feature branch
| Tool | Current Version | Last Updated |
|---|---|---|
| cargo-audit | 0.22.0 | 2024-02-23 |
| cargo-geiger | 0.12.0 | 2024-02-23 |
| clippy | stable | (follows Rust toolchain) |
The dependency gate is intentionally strict:
cargo audit --deny warningsDo not relax this in the workflow to count only selected severities. If a finding
is not exploitable in this repository, document the rationale in audit.toml
with the narrowest advisory ignore possible so future scans remain blocking for
new vulnerabilities.
Current configuration uses -D warnings which treats all warnings as errors.
To allow specific lints:
# Change from -W (warn) to -A (allow)
-A clippy::todo # Allow TODO markersTo add more strict lints:
# Add additional security lints
-W clippy::mem_forget
-W clippy::print_stdout
-W clippy::exitEvery security scan run produces downloadable artifacts:
-
cargo-audit-report (JSON)
- Vulnerability details
- Affected versions
- Patch information
-
clippy-security-report (HTML + JSON + TXT)
- HTML: Human-readable report
- JSON: Machine-parseable output
- TXT: Summary statistics
-
cargo-geiger-report (Markdown)
- Unsafe code metrics
- Per-crate breakdown
-
security-summary (Markdown)
- Overall scan status
- Quick reference
- Navigate to Actions tab in GitHub
- Click on the workflow run
- Scroll to "Artifacts" section at bottom
- Click artifact name to download ZIP
Artifacts are retained for 30 days.
You can trigger security scans manually without pushing code:
- Go to Actions tab
- Select "Security Scanning" workflow
- Click "Run workflow" button
- Select branch
- Click "Run workflow"
Useful for:
- Testing scanner configuration changes
- Periodic security audits
- Generating reports for compliance
The security scanning workflow is fully automated and requires no secrets, tokens, or manual configuration.
cargo-audit automatically downloads the RustSec advisory database. To pre-populate locally:
cargo audit fetchCreate audit.toml in workspace root to customize behavior:
[advisories]
# Ignore specific advisories (with justification)
ignore = []
# Fail on informational advisories
informational_warnings = ["unmaintained"]
[yanked]
# Fail on yanked crates
enabled = true# Clear cargo cache
rm -rf ~/.cargo/registry
rm -rf ~/.cargo/git
# Reinstall
cargo install cargo-audit --version 0.20.0 --locked --forceIf a vulnerability doesn't apply to your usage:
- Document why in
audit.toml - Add to ignore list with comment
- Link to issue/discussion if available
If you see errors like "unsupported CVSS version: 4.0":
error parsing RUSTSEC-2026-XXXX.md: unsupported CVSS version: 4.0
This means your cargo-audit version is too old. The RustSec advisory database now includes CVSS 4.0 scores (introduced November 2023).
Solution: Upgrade to cargo-audit 0.22.0 or later:
cargo install cargo-audit --version 0.22.0 --locked --forceWhy this happens:
- CVSS 4.0 was released in November 2023
- RustSec advisories started using CVSS 4.0 in 2026
- Older cargo-audit versions only support CVSS 3.x
- This is a tooling compatibility issue, not a security vulnerability in your project
If CI shows stale results:
- Go to Actions tab
- Click "Caches" in left sidebar
- Delete relevant caches
- Re-run workflow
- Run locally before pushing - Catch issues early
- Review all findings - Don't blindly ignore warnings
- Update regularly - Keep dependencies current
- Document exceptions - Explain why risks are accepted
- Monitor advisories - Subscribe to RustSec announcements
- Test after fixes - Ensure functionality isn't broken
- Scope scans to contracts - Focus on critical code
- RustSec Advisory Database
- Clippy Lint Documentation
- cargo-audit Documentation
- cargo-geiger Documentation
- Soroban Security Best Practices
For questions or issues with security scanning:
- Check this documentation
- Review existing GitHub issues
- Open a new issue with:
- Scanner output
- Steps to reproduce
- Expected vs actual behavior