diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 46315ae..30df2f5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,10 @@ jobs: - uses: dtolnay/rust-toolchain@stable - run: cargo check --workspace - run: cargo test --workspace + - run: cargo clippy --workspace -- -D warnings + - run: | + cargo install cargo-audit + cargo audit - run: cargo doc --no-deps --document-private-items --workspace - name: Upload doc artifact if: github.ref == 'refs/heads/master' diff --git a/fuzz/fuzz_targets/parse_gvas.rs b/fuzz/fuzz_targets/parse_gvas.rs new file mode 100644 index 0000000..8e5bee0 --- /dev/null +++ b/fuzz/fuzz_targets/parse_gvas.rs @@ -0,0 +1,7 @@ +#[fuzz] +mod target { + #[fuzz] + fn parse_gvas(data: &[u8]) { + let _ = notalterra::gvas::extract_metadata_from_bytes(data); + } +} diff --git a/src/gvas.rs b/src/gvas.rs index 622e5ab..ee78196 100644 --- a/src/gvas.rs +++ b/src/gvas.rs @@ -201,7 +201,7 @@ fn extract_bool_property(data: &[u8], prop_name: &str) -> Option { /// Scan for a double value near a marker byte sequence. fn scan_double_near(data: &[u8], marker: &[u8]) -> Option { let pos = data.windows(marker.len()).position(|w| w == marker)?; - let end = (pos + 60).min(data.len()); + let _end = (pos + 60).min(data.len()); for off in 8..50 { if pos + off + 8 > data.len() { break; } let val = f64::from_le_bytes(data[pos+off..pos+off+8].try_into().ok()?); @@ -313,6 +313,26 @@ pub struct SaveMetadata { } /// Parse a `.sav` or `.bak` file and return its GVAS metadata. +/// Parse GVAS metadata from an in-memory byte slice. Useful for fuzzing. +pub fn extract_metadata_from_bytes(data: &[u8]) -> Result { + let mut errors = Vec::new(); + let slot_name = match extract_str_property(data, "SlotName") { + Ok(v) => Some(v), + Err(e) => { errors.push(e); None } + }; + let display_name = match extract_str_property(data, "DisplayName") { + Ok(v) => Some(v), + Err(e) => { errors.push(e); None } + }; + Ok(SaveMetadata { + slot_name, + display_name, + is_online: extract_bool_property(data, "OnlineMode").unwrap_or(false), + playtime_seconds: extract_double_property(data, "PlayTime"), + errors, + }) +} + pub fn extract_metadata(path: &Path) -> Result { let data = fs::read(path) .with_context(|| format!("failed to read {}", path.display()))?; diff --git a/src/lib.rs b/src/lib.rs index 1f2cba9..296ad4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] // NotAlterra library — re-exports for integration tests and examples. pub mod config; pub mod discovery; diff --git a/src/main.rs b/src/main.rs index 648129e..5c4b9cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] //! NotAlterra — Subnautica 2 save-file manager. //! //! Cross-platform terminal application. Locates save folders, recovers