use anyhow::{anyhow, Result};
use ascii::AsciiStr;
use clap::{Parser, Subcommand};
use fs_err as fs;
use object::{Object, ObjectSection};
use sbat::{ImageSbat, ImageSbatVec};
use std::path::{Path, PathBuf};
#[derive(Parser)]
struct Args {
#[command(subcommand)]
action: Action,
}
#[derive(Subcommand)]
enum Action {
Dump { input: PathBuf },
Validate { input: PathBuf },
}
fn read_sbat_section(input: &Path) -> Result<Vec<u8>> {
let data = fs::read(input)?;
let file = object::File::parse(&*data)?;
let section = file
.section_by_name(".sbat")
.ok_or(anyhow!("missing '.sbat' section"))?;
Ok(section.data()?.to_vec())
}
fn dump_sbat(input: &Path) -> Result<()> {
let data = read_sbat_section(input)?;
let sbat = std::str::from_utf8(&data)?;
println!("{sbat}");
Ok(())
}
fn validate_sbat(input: &Path) -> Result<()> {
let data = read_sbat_section(input)?;
let image_sbat = ImageSbatVec::parse(&data).unwrap();
let mut builder = tabled::builder::Builder::default();
builder.set_header([
"component",
"gen",
"vendor",
"package",
"version",
"url",
]);
for entry in image_sbat.entries() {
let component = entry.component;
let vendor = entry.vendor;
let opt_ascii_to_string = |opt: Option<&AsciiStr>| {
opt.map(|s| s.to_string()).unwrap_or_default()
};
builder.push_record([
component.name.to_string(),
component.generation.to_string(),
opt_ascii_to_string(vendor.name),
opt_ascii_to_string(vendor.package_name),
opt_ascii_to_string(vendor.version),
opt_ascii_to_string(vendor.url),
]);
}
println!("{}", builder.build());
Ok(())
}
fn main() -> Result<()> {
let args = Args::parse();
match &args.action {
Action::Dump { input } => dump_sbat(input),
Action::Validate { input } => validate_sbat(input),
}
}