From 5d39a1134f262f7b52f7938656b31bb6956c69b6 Mon Sep 17 00:00:00 2001 From: Dave Grantham Date: Thu, 16 Jul 2026 20:38:50 -0600 Subject: [PATCH 1/2] hardening Signed-off-by: Dave Grantham --- .github/workflows/build.yml | 50 ++++++++++++++++++++++++++++++++++++- CONCURRENCY.md | 10 ++++---- Cargo.toml | 2 +- SECURITY.md | 12 ++++----- 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7e2855e..1b95a0c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,11 @@ name: build -on: [push, pull_request] +on: + push: + pull_request: + schedule: + - cron: '0 3 * * *' + workflow_dispatch: jobs: build: @@ -113,3 +118,46 @@ jobs: - name: Upload Code Coverage uses: codecov/codecov-action@v5 + + fuzz: + name: Fuzz + runs-on: ubuntu-latest + # Bounded fuzzing is expensive. Only run on schedule or manual dispatch + # to avoid exhausting CI minutes; regular push/PR CI already covers + # fmt, clippy, audit, MSRV, no_std, and coverage. + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + + steps: + - name: Checkout Sources + uses: actions/checkout@v5 + + - name: Install Rust Toolchain (nightly) + uses: dtolnay/rust-toolchain@nightly + + - name: Cache Dependencies & Build Outputs + uses: actions/cache@v4 + with: + path: | + ~/.cargo + target + fuzz/target + key: ${{ runner.os }}-fuzz-${{ hashFiles('**/Cargo.lock') }} + + - name: Install cargo-fuzz + run: cargo install cargo-fuzz + shell: bash + + - name: Run fuzz_decode (60s) + run: cargo fuzz run fuzz_decode -- -max_total_time=60 + working-directory: fuzz + shell: bash + + - name: Run fuzz_encode (60s) + run: cargo fuzz run fuzz_encode -- -max_total_time=60 + working-directory: fuzz + shell: bash + + - name: Run fuzz_roundtrip (60s) + run: cargo fuzz run fuzz_roundtrip -- -max_total_time=60 + working-directory: fuzz + shell: bash diff --git a/CONCURRENCY.md b/CONCURRENCY.md index 3c50fde..b346aa8 100644 --- a/CONCURRENCY.md +++ b/CONCURRENCY.md @@ -118,7 +118,7 @@ let handles: Vec<_> = (0..10) .map(|_| { let d = data.clone(); thread::spawn(move || { - multibase::encode(Base::Base64, &d) + multi_base::encode(Base::Base64, &d) }) }) .collect(); @@ -140,7 +140,7 @@ let handles: Vec<_> = (0..10) .map(|_| { let e = Arc::clone(&encoded); thread::spawn(move || { - multibase::decode(&*e, true) + multi_base::decode(&*e, true) }) }) .collect(); @@ -164,8 +164,8 @@ let handles: Vec<_> = (0..10) let mut decode_buffer = Vec::new(); for _ in 0..100 { - multibase::encode_into(Base::Base64, b"data", &mut encode_buffer); - multibase::decode_into(&encode_buffer, true, &mut decode_buffer).unwrap(); + multi_base::encode_into(Base::Base64, b"data", &mut encode_buffer); + multi_base::decode_into(&encode_buffer, true, &mut decode_buffer).unwrap(); } }) }) @@ -215,7 +215,7 @@ let handles: Vec<_> = (0..10) .map(|_| { thread::spawn(move || { let mut buf = shared_buffer.lock().unwrap(); - multibase::encode_into(Base::Base64, b"data", &mut buf); + multi_base::encode_into(Base::Base64, b"data", &mut buf); }) }) .collect(); diff --git a/Cargo.toml b/Cargo.toml index 88b109b..bf1b335 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "multi-base" -version = "1.0.2" +version = "1.0.3" authors = ["Friedel Ziegelmayer ", "Dave Grantham "] edition = "2024" rust-version = "1.85" diff --git a/SECURITY.md b/SECURITY.md index ca08c15..4f34b63 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -59,7 +59,7 @@ fn safe_decode(input: &str) -> Result<(Base, Vec), Error> { if input.len() > MAX_INPUT_SIZE { return Err(Error::InvalidBaseString); // or custom error } - multibase::decode(input, true) + multi_base::decode(input, true) } ``` @@ -68,7 +68,7 @@ fn safe_decode(input: &str) -> Result<(Base, Vec), Error> { Always handle errors properly and avoid exposing detailed error messages to untrusted parties: ```rust -match multibase::decode(untrusted_input, true) { +match multi_base::decode(untrusted_input, true) { Ok((base, data)) => { // Process data } @@ -89,10 +89,10 @@ The Identity encoding (`\0` prefix) uses lossy UTF-8 conversion: ```rust // For exact binary data preservation, use Base64 or Base58 -let encoded = multibase::encode(Base::Base64, binary_data); +let encoded = multi_base::encode(Base::Base64, binary_data); // Identity is only appropriate for UTF-8 text -let text_encoded = multibase::encode(Base::Identity, "valid utf-8 text".as_bytes()); +let text_encoded = multi_base::encode(Base::Identity, "valid utf-8 text".as_bytes()); ``` ### 4. Strict vs Permissive Decoding @@ -101,10 +101,10 @@ Use strict decoding (`true`) for untrusted input to ensure stricter validation: ```rust // For untrusted input, always use strict mode -let (base, data) = multibase::decode(untrusted, true)?; +let (base, data) = multi_base::decode(untrusted, true)?; // Permissive mode allows case-insensitive decoding for some bases -let (base, data) = multibase::decode(trusted, false)?; +let (base, data) = multi_base::decode(trusted, false)?; ``` ## Security Testing From c669598965b2cd90c203ab9cffb3181a85b56b96 Mon Sep 17 00:00:00 2001 From: Dave Grantham Date: Thu, 16 Jul 2026 20:40:20 -0600 Subject: [PATCH 2/2] lock Signed-off-by: Dave Grantham --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 56c8b84..62da7f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -386,7 +386,7 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "multi-base" -version = "1.0.2" +version = "1.0.3" dependencies = [ "base-x", "criterion",