openssf scorecard suggested improvements

This commit is contained in:
Mick Grove 2026-03-19 23:52:38 -07:00
commit 0c77e3c4a3
7 changed files with 3710 additions and 1 deletions

8
.gitignore vendored
View file

@ -19,8 +19,13 @@ logs/*
*.html
!docs/access-map-viewer/index.html
*.dot
fuzz/
fuzz/*
!fuzz/Cargo.toml
!fuzz/Cargo.lock
!fuzz/fuzz_targets/
!fuzz/fuzz_targets/*.rs
fuzz/corpus/
fuzz/target/
### macOS ###
# General
@ -69,6 +74,7 @@ bin/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
!fuzz/Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk

3573
fuzz/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

49
fuzz/Cargo.toml Normal file
View file

@ -0,0 +1,49 @@
[package]
name = "kingfisher-fuzz"
version = "0.0.0"
publish = false
edition = "2021"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
[dependencies.kingfisher-core]
path = "../crates/kingfisher-core"
[dependencies.kingfisher-scanner]
path = "../crates/kingfisher-scanner"
# Expose base64 for the fuzz target that exercises get_base64_strings
[dependencies.base64]
version = "0.22"
[workspace]
members = ["."]
# Must mirror the root workspace patch so vectorscan-rs builds from the vendored copy
[patch.crates-io]
vectorscan-rs = { path = "../vendor/vectorscan-rs/vectorscan-rs" }
vectorscan-rs-sys = { path = "../vendor/vectorscan-rs/vectorscan-rs-sys" }
[[bin]]
name = "fuzz_entropy"
path = "fuzz_targets/fuzz_entropy.rs"
doc = false
[[bin]]
name = "fuzz_location"
path = "fuzz_targets/fuzz_location.rs"
doc = false
[[bin]]
name = "fuzz_base64"
path = "fuzz_targets/fuzz_base64.rs"
doc = false
[[bin]]
name = "fuzz_span"
path = "fuzz_targets/fuzz_span.rs"
doc = false

View file

@ -0,0 +1,16 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use kingfisher_scanner::primitives::get_base64_strings;
fuzz_target!(|data: &[u8]| {
let results = get_base64_strings(data);
for decoded in &results {
// Every returned span must be within the input bounds
assert!(decoded.pos_start <= decoded.pos_end);
assert!(decoded.pos_end <= data.len());
// Decoded data must be non-empty and ASCII (per the function contract)
assert!(!decoded.decoded.is_empty());
assert!(decoded.decoded.is_ascii());
}
});

View file

@ -0,0 +1,11 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use kingfisher_core::calculate_shannon_entropy;
fuzz_target!(|data: &[u8]| {
let entropy = calculate_shannon_entropy(data);
// Invariants that must always hold:
assert!(entropy.is_finite(), "entropy must be finite");
assert!(entropy >= 0.0, "entropy must be non-negative");
assert!(entropy <= 8.0, "entropy must be <= 8.0 for byte data");
});

View file

@ -0,0 +1,27 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use kingfisher_core::location::{LocationMapping, OffsetSpan};
fuzz_target!(|data: &[u8]| {
if data.len() < 4 {
return;
}
// Use last 4 bytes to derive offsets, rest as the input text
let text_len = data.len() - 4;
let text = &data[..text_len];
let offset_a = u16::from_le_bytes([data[text_len], data[text_len + 1]]) as usize;
let offset_b = u16::from_le_bytes([data[text_len + 2], data[text_len + 3]]) as usize;
let mapping = LocationMapping::new(text);
// Exercise get_source_point with an arbitrary offset (may be beyond text length)
let point = mapping.get_source_point(offset_a);
assert!(point.line >= 1, "line numbers are 1-indexed");
// Exercise get_source_span with a span that might be empty, inverted, or out of bounds
let start = offset_a.min(offset_b);
let end = offset_a.max(offset_b);
let span = OffsetSpan { start, end };
let _source_span = mapping.get_source_span(&span);
});

View file

@ -0,0 +1,27 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use kingfisher_core::OffsetSpan;
use kingfisher_scanner::primitives::insert_span;
fuzz_target!(|data: &[u8]| {
// Interpret the input as a sequence of (start: u16, end: u16) span pairs
if data.len() < 4 {
return;
}
let mut spans: Vec<OffsetSpan> = Vec::new();
let mut i = 0;
while i + 3 < data.len() {
let start = u16::from_le_bytes([data[i], data[i + 1]]) as usize;
let end = u16::from_le_bytes([data[i + 2], data[i + 3]]) as usize;
i += 4;
let span = OffsetSpan { start, end };
insert_span(&mut spans, span);
// Invariant: the spans list must remain sorted by start offset
for w in spans.windows(2) {
assert!(w[0].start <= w[1].start, "spans must stay sorted");
}
}
});