Skip to content

fix(review): address #175 audit findings #504

fix(review): address #175 audit findings

fix(review): address #175 audit findings #504

Workflow file for this run

name: Static Analysis
# Supply-chain + lint gates that don't need a full build matrix.
# Runs on every PR and on develop/main pushes. Kept in a separate
# workflow from `ci.yml` so a slow `cargo deny` advisory-db fetch
# doesn't block the build matrix.
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
RUST_TOOLCHAIN: "1.91"
jobs:
changes:
name: Classify changes
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
outputs:
contracts: ${{ steps.decide.outputs.contracts }}
rust: ${{ steps.decide.outputs.rust }}
full: ${{ steps.decide.outputs.full }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Collect changed files
id: changed
env:
EVENT_NAME: ${{ github.event_name }}
BEFORE_SHA: ${{ github.event.before || '' }}
BASE_REF: ${{ github.base_ref || '' }}
run: |
set -euo pipefail
changed_file_list="$(mktemp)"
if [ "$EVENT_NAME" = "pull_request" ]; then
git fetch --no-tags --depth=1 origin "$BASE_REF:refs/remotes/origin/$BASE_REF"
git diff --name-only "origin/$BASE_REF" "$GITHUB_SHA" > "$changed_file_list"
elif [ -n "$BEFORE_SHA" ] && ! [[ "$BEFORE_SHA" =~ ^0+$ ]]; then
git diff --name-only "$BEFORE_SHA" "$GITHUB_SHA" > "$changed_file_list"
else
git diff-tree --no-commit-id --name-only -r "$GITHUB_SHA" > "$changed_file_list"
fi
{
echo 'files<<EOF'
cat "$changed_file_list"
echo 'EOF'
} >> "$GITHUB_OUTPUT"
- name: Decide static lanes
id: decide
env:
CHANGED_FILES: ${{ steps.changed.outputs.files || '' }}
run: |
set -euo pipefail
full=false
contracts=false
rust=false
static=false
while IFS= read -r file; do
[ -n "$file" ] || continue
case "$file" in
contracts/*|deployments/*|foundry.toml|slither.config.json)
contracts=true
;;
esac
case "$file" in
.cargo/*|Cargo.toml|Cargo.lock|deny.toml|patches/*|scripts/*|trading-*/*)
rust=true
;;
esac
case "$file" in
.cargo/audit.toml|.github/workflows/static-analysis.yml|deny.toml|slither.config.json)
static=true
;;
esac
done <<< "$CHANGED_FILES"
if [ "$static" = "true" ]; then
full=true
contracts=true
rust=true
fi
echo "full=$full" >> "$GITHUB_OUTPUT"
echo "contracts=$contracts" >> "$GITHUB_OUTPUT"
echo "rust=$rust" >> "$GITHUB_OUTPUT"
cargo-deny:
name: Cargo Deny (advisories + licences + sources + bans)
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
# The action wraps `cargo install cargo-deny --locked` and
# caches the binary across runs. `command: check` runs all four
# sections (advisories, bans, licenses, sources) — same as a
# local `cargo deny check`.
# `deny.toml` is auto-detected from the repo root, so we don't pass
# `--config` explicitly — older cargo-deny CLIs only accept it as a
# subcommand flag and the action injects `arguments` at the top
# level. Keeping the call minimal keeps us version-portable.
- uses: EmbarkStudios/cargo-deny-action@v2
with:
log-level: warn
command: check
cargo-audit:
name: Cargo Audit (RustSec advisory DB)
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
- uses: Swatinem/rust-cache@v2
- name: Install cargo-audit
run: cargo install cargo-audit --locked
# Reads .cargo/audit.toml for the ignore list. Every entry there
# is cross-referenced in deny.toml's [advisories].ignore and in
# audits/static-analysis-triage.md. `-D warnings` denies on
# unmaintained / unsound / yanked; whitelisted IDs cover known
# transitive deps (substrate / solana-sdk / ethers-v2).
- name: Run cargo audit
run: cargo audit -D warnings
slither:
name: Slither (Solidity static analysis)
needs: changes
if: needs.changes.outputs.contracts == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install slither + solc-select
run: |
python -m pip install --upgrade pip
pip install slither-analyzer solc-select
solc-select install 0.8.20
solc-select use 0.8.20
- uses: foundry-rs/foundry-toolchain@v1
with:
version: v1.5.1
- name: Install Soldeer deps
run: forge soldeer install
# slither.config.json filters dependencies/, contracts/test/,
# contracts/script/ and excludes already-triaged informational
# detectors. `--fail-pedantic` treats any new HIGH/MEDIUM as a CI
# failure. Inline `slither-disable-next-line` comments document
# any FP suppression, with rationale traceable to
# audits/static-analysis-triage.md.
- name: Run slither on production contracts
run: |
set -e
for f in TradeValidator TradingVault PolicyEngine FeeDistributor StrategyRegistry VaultDeployer VaultFactory VaultShare VaultShareDeployer ChainlinkUsdValuator WrappedAssetValuator UniswapV3TwapValuator; do
echo "=== slither contracts/src/$f.sol ==="
slither contracts/src/$f.sol --config-file slither.config.json
done
static-analysis-gate:
name: Static Analysis Gate
needs: [changes, cargo-deny, cargo-audit, slither]
if: always()
runs-on: ubuntu-latest
steps:
- name: Check required static lanes
env:
CHANGES_RESULT: ${{ needs.changes.result }}
CONTRACTS_NEEDED: ${{ needs.changes.outputs.contracts }}
RUST_NEEDED: ${{ needs.changes.outputs.rust }}
CARGO_DENY_RESULT: ${{ needs.cargo-deny.result }}
CARGO_AUDIT_RESULT: ${{ needs.cargo-audit.result }}
SLITHER_RESULT: ${{ needs.slither.result }}
run: |
set -euo pipefail
failed=false
if [ "$CHANGES_RESULT" != "success" ]; then
echo "::error::Change classification failed with result '$CHANGES_RESULT'"
exit 1
fi
require_success() {
local label="$1"
local needed="$2"
local result="$3"
if [ "$needed" = "true" ] && [ "$result" != "success" ]; then
echo "::error::$label required but finished with result '$result'"
failed=true
fi
}
require_success "Cargo Deny" "$RUST_NEEDED" "$CARGO_DENY_RESULT"
require_success "Cargo Audit" "$RUST_NEEDED" "$CARGO_AUDIT_RESULT"
require_success "Slither" "$CONTRACTS_NEEDED" "$SLITHER_RESULT"
if [ "$CONTRACTS_NEEDED" != "true" ] && [ "$RUST_NEEDED" != "true" ]; then
echo "No Rust or Solidity surfaces changed; static analysis gate is green."
fi
if [ "$failed" = "true" ]; then
exit 1
fi