Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions .github/workflows/rust-gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Rust Gate — a required, always-reporting status check for Rust quality.
#
# Why this exists:
# The comprehensive Rust workflows (rust-crypto.yml, rust-security-suite.yml)
# are path-filtered to rust_crypto/** and crypto_core/**. A path-filtered
# workflow that is made a *required* status check deadlocks every PR that does
# NOT touch those paths — the check never runs, so the PR waits forever.
#
# This workflow has NO path filter, so it always runs and always reports a
# conclusion, making it safe to require in branch protection. It does the
# expensive work (clippy + crypto_core tests) only when Rust sources actually
# changed; otherwise it passes trivially and fast.
#
# Detection is fail-safe: if the diff can't be computed for any reason, the
# checks RUN (a slightly slower pass) rather than being skipped, so real Rust
# changes can never slip through un-linted.

name: Rust Gate

on:
pull_request:
branches: [main]
push:
branches: [main]

permissions:
contents: read

concurrency:
group: rust-gate-${{ github.ref }}
cancel-in-progress: true

jobs:
rust-gate:
name: Rust Gate (clippy + tests)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0

- name: Detect Rust source changes
id: changes
run: |
run_rust=true
if [ "${{ github.event_name }}" = "pull_request" ]; then
if git fetch --no-tags origin "${{ github.base_ref }}" 2>/dev/null; then
diff=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD" 2>/dev/null || echo "__ERR__")
if [ "$diff" != "__ERR__" ]; then
if echo "$diff" | grep -qE '^(rust_crypto|crypto_core)/'; then
run_rust=true
else
run_rust=false
fi
fi
fi
fi
echo "rust=$run_rust" >> "$GITHUB_OUTPUT"
if [ "$run_rust" = "true" ]; then
echo "::notice::Rust sources changed (or detection inconclusive) — running clippy + crypto_core tests."
else
echo "::notice::No Rust source changes — gate passes without building."
fi

- name: Install Rust toolchain
if: steps.changes.outputs.rust == 'true'
uses: dtolnay/rust-toolchain@d0592fe69e35bc8f12e3dbaf9ad2694d976cb8e3 # stable
with:
toolchain: stable
components: clippy

- name: Install system dependencies
if: steps.changes.outputs.rust == 'true'
run: |
sudo apt-get update
sudo apt-get install -y build-essential libssl-dev pkg-config libpcsclite-dev libudev-dev

- name: Clippy (deny warnings)
if: steps.changes.outputs.rust == 'true'
run: cargo clippy --workspace --all-targets -- -D warnings

- name: crypto_core tests
if: steps.changes.outputs.rust == 'true'
run: cargo test -p crypto_core
6 changes: 3 additions & 3 deletions crypto_core/src/meow_fountain/wire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ mod tests {
buf.extend_from_slice(&0u32.to_be_bytes());
buf.extend_from_slice(&1u16.to_be_bytes());
buf.extend_from_slice(&0u16.to_be_bytes());
buf.extend(std::iter::repeat(0xFFu8).take(99));
buf.extend(std::iter::repeat_n(0xFFu8, 99));
assert!(matches!(
Droplet::from_wire(&buf, 100),
Err(WireError::DataLengthMismatch {
Expand All @@ -240,7 +240,7 @@ mod tests {
buf.extend_from_slice(&3u16.to_be_bytes());
buf.extend_from_slice(&1u16.to_be_bytes());
buf.extend_from_slice(&2u16.to_be_bytes());
buf.extend(std::iter::repeat(0u8).take(32));
buf.extend(std::iter::repeat_n(0u8, 32));
assert!(matches!(
Droplet::from_wire(&buf, 32),
Err(WireError::UnsortedOrDuplicateIndices)
Expand All @@ -254,7 +254,7 @@ mod tests {
buf.extend_from_slice(&2u16.to_be_bytes());
buf.extend_from_slice(&5u16.to_be_bytes());
buf.extend_from_slice(&5u16.to_be_bytes());
buf.extend(std::iter::repeat(0u8).take(32));
buf.extend(std::iter::repeat_n(0u8, 32));
assert!(matches!(
Droplet::from_wire(&buf, 32),
Err(WireError::UnsortedOrDuplicateIndices)
Expand Down
2 changes: 1 addition & 1 deletion crypto_core/src/secure_alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ impl<T: Zeroize> SecureBox<T> {
};

// Round data up to page boundary
let data_pages = (data_size + page_size - 1) / page_size;
let data_pages = data_size.div_ceil(page_size);
let data_region_size = data_pages * page_size;
// Total: guard_before + data + guard_after
let total_size = data_region_size + 2 * page_size;
Expand Down
2 changes: 1 addition & 1 deletion crypto_core/tests/coverage_boost_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ mod secure_alloc_coverage {
fn test_secure_box_deref_mut() {
let mut sbox = SecureBox::new([0u8; 32]).expect("alloc failed");
// Exercise DerefMut
let data: &mut [u8; 32] = &mut *sbox;
let data: &mut [u8; 32] = &mut sbox;
data[0] = 0xFF;
data[31] = 0xAA;
assert_eq!(sbox[0], 0xFF);
Expand Down
2 changes: 1 addition & 1 deletion rust_crypto/tests/coverage_boost_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ mod stego_coverage {
let mut costs = vec![1.0f64; 100];
stego::compute_adaptive_costs(&seed, &cover_bits, &pixels, &mut costs);
// With medium texture, some costs get texture_factor=1.0 (unchanged) plus jitter
assert!(costs.iter().any(|&c| c >= 0.9 && c <= 1.1));
assert!(costs.iter().any(|&c| (0.9..=1.1).contains(&c)));
}
}

Expand Down
Loading