auditable-extract-0.3.5/.cargo_vcs_info.json0000644000000001570000000000100144460ustar { "git": { "sha1": "4ccbc7276a6708561de827467475faec3c75b213" }, "path_in_vcs": "auditable-extract" }auditable-extract-0.3.5/.gitignore000064400000000000000000000000101046102023000152120ustar 00000000000000/target auditable-extract-0.3.5/CHANGELOG.md000064400000000000000000000014371046102023000150510ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.3.5] - 2024-10-14 ### Changed - When extracting audit data from WASM, nested sections are now also checked for audit data. This makes extraction work for WebAssembly components. ## [0.3.4] - 2024-05-08 ### Changed - Upgraded to `wasmparser` 0.207.0, removing nearly all dependencies with `unsafe` code in them - Enabled the `wasm` feature by default now that it doesn't pull in `unsafe` code ### Added - This changelog file ## [0.3.3] - 2024-05-03 ### Added - WebAssembly support, gated behind the non-default `wasm` feature auditable-extract-0.3.5/Cargo.toml0000644000000023350000000000100124440ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "auditable-extract" version = "0.3.5" authors = ['Sergey "Shnatsel" Davidoff '] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "Extract the dependency trees embedded in binaries by `cargo auditable`" readme = "README.md" categories = ["encoding"] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-secure-code/cargo-auditable" [lib] name = "auditable_extract" path = "src/lib.rs" [dependencies.binfarce] version = "0.2" [dependencies.wasmparser] version = "0.207.0" optional = true default-features = false [features] default = ["wasm"] wasm = ["wasmparser"] [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = ["cfg(fuzzing)"] auditable-extract-0.3.5/Cargo.toml.orig000064400000000000000000000012641046102023000161250ustar 00000000000000[package] name = "auditable-extract" version = "0.3.5" authors = ["Sergey \"Shnatsel\" Davidoff "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-secure-code/cargo-auditable" description = "Extract the dependency trees embedded in binaries by `cargo auditable`" categories = ["encoding"] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] binfarce = "0.2" wasmparser = { version = "0.207.0", default-features = false, optional = true } [features] default = ["wasm"] wasm = ["wasmparser"] [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } auditable-extract-0.3.5/README.md000064400000000000000000000040721046102023000145150ustar 00000000000000Extracts the dependency tree information embedded in executables by [`cargo auditable`](https://github.com/rust-secure-code/cargo-auditable). This crate parses platform-specific binary formats ([ELF](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format), [PE](https://en.wikipedia.org/wiki/Portable_Executable), [Mach-O](https://en.wikipedia.org/wiki/Mach-O), [WASM](https://en.wikipedia.org/wiki/WebAssembly)) and obtains the compressed audit data. Unlike other binary parsing crates, it is specifically designed to be resilient to malicious input. It 100% safe Rust (including all dependencies) and performs no heap allocations. ## Usage **Note:** this is a low-level crate that only implements binary parsing. It rarely should be used directly. You probably want the higher-level [`auditable-info`](https://docs.rs/auditable-info) crate instead. The following snippet demonstrates full extraction pipeline using this crate, including decompression using the safe-Rust [`miniz_oxide`](http://docs.rs/miniz_oxide/) and optional JSON parsing via [`auditable-serde`](http://docs.rs/auditable-serde/): ```rust,ignore use std::io::{Read, BufReader}; use std::{error::Error, fs::File, str::FromStr}; ! fn main() -> Result<(), Box> { // Read the input let f = File::open("target/release/hello-world")?; let mut f = BufReader::new(f); let mut input_binary = Vec::new(); f.read_to_end(&mut input_binary)?; // Extract the compressed audit data let compressed_audit_data = auditable_extract::raw_auditable_data(&input_binary)?; // Decompress it with your Zlib implementation of choice. We recommend miniz_oxide use miniz_oxide::inflate::decompress_to_vec_zlib; let decompressed_data = decompress_to_vec_zlib(&compressed_audit_data) .map_err(|_| "Failed to decompress audit data")?; let decompressed_data = String::from_utf8(decompressed_data)?; println!("{}", decompressed_data); // Parse the audit data to Rust data structures let dependency_tree = auditable_serde::VersionInfo::from_str(&decompressed_data); Ok(()) } ```auditable-extract-0.3.5/src/lib.rs000064400000000000000000000066011046102023000151410ustar 00000000000000#![forbid(unsafe_code)] #![doc = include_str!("../README.md")] #[cfg(feature = "wasm")] mod wasm; use binfarce::Format; /// Extracts the Zlib-compressed dependency info from an executable. /// /// This function does not allocate any memory on the heap and can be safely given untrusted input. pub fn raw_auditable_data(data: &[u8]) -> Result<&[u8], Error> { match binfarce::detect_format(data) { Format::Elf32 { byte_order } => { let section = binfarce::elf32::parse(data, byte_order)? .section_with_name(".dep-v0")? .ok_or(Error::NoAuditData)?; Ok(data.get(section.range()?).ok_or(Error::UnexpectedEof)?) } Format::Elf64 { byte_order } => { let section = binfarce::elf64::parse(data, byte_order)? .section_with_name(".dep-v0")? .ok_or(Error::NoAuditData)?; Ok(data.get(section.range()?).ok_or(Error::UnexpectedEof)?) } Format::Macho => { let parsed = binfarce::macho::parse(data)?; let section = parsed.section_with_name("__DATA", ".dep-v0")?; let section = section.ok_or(Error::NoAuditData)?; Ok(data.get(section.range()?).ok_or(Error::UnexpectedEof)?) } Format::PE => { let parsed = binfarce::pe::parse(data)?; let section = parsed .section_with_name(".dep-v0")? .ok_or(Error::NoAuditData)?; Ok(data.get(section.range()?).ok_or(Error::UnexpectedEof)?) } Format::Unknown => { #[cfg(feature = "wasm")] if data.starts_with(b"\0asm") { return wasm::raw_auditable_data_wasm(data); } Err(Error::NotAnExecutable) } } } #[cfg(all(fuzzing, feature = "wasm"))] pub fn raw_auditable_data_wasm_for_fuzz(input: &[u8]) -> Result<&[u8], Error> { wasm::raw_auditable_data_wasm(input) } #[derive(Debug, Copy, Clone)] pub enum Error { NoAuditData, NotAnExecutable, UnexpectedEof, MalformedFile, SymbolsSectionIsMissing, SectionIsMissing, UnexpectedSectionType, } impl std::error::Error for Error {} impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let message = match self { Error::NoAuditData => "No audit data found in the executable", Error::NotAnExecutable => "Not an executable file", Error::UnexpectedEof => "Unexpected end of file", Error::MalformedFile => "Malformed executable file", Error::SymbolsSectionIsMissing => "Symbols section missing from executable", Error::SectionIsMissing => "Section is missing from executable", Error::UnexpectedSectionType => "Unexpected executable section type", }; write!(f, "{message}") } } impl From for Error { fn from(e: binfarce::ParseError) -> Self { match e { binfarce::ParseError::MalformedInput => Error::MalformedFile, binfarce::ParseError::UnexpectedEof => Error::UnexpectedEof, binfarce::ParseError::SymbolsSectionIsMissing => Error::SymbolsSectionIsMissing, binfarce::ParseError::SectionIsMissing(_) => Error::SectionIsMissing, binfarce::ParseError::UnexpectedSectionType { .. } => Error::UnexpectedSectionType, } } } auditable-extract-0.3.5/src/wasm.rs000064400000000000000000000013241046102023000153370ustar 00000000000000//! Implements WASM parsing use crate::Error; use wasmparser::{self, Payload}; pub(crate) fn raw_auditable_data_wasm(input: &[u8]) -> Result<&[u8], Error> { for payload in wasmparser::Parser::new(0).parse_all(input) { match payload.map_err(|_| Error::MalformedFile)? { Payload::CustomSection(reader) => { if reader.name() == ".dep-v0" { return Ok(reader.data()); } } // We reached the end without seeing ".dep-v0" custom section Payload::End(_) => return Err(Error::NoAuditData), // ignore everything that's not a custom section _ => {} } } Err(Error::MalformedFile) }