From 40a90308a5510c3d9fe774b92ce18d12a41a85a6 Mon Sep 17 00:00:00 2001 From: Lynnesbian Date: Fri, 26 Mar 2021 00:28:03 +1000 Subject: [PATCH] initial (and somewhat bonked) powershell support, cargo update --- Cargo.lock | 8 ++--- src/formats.rs | 78 ++++++++++++++++++++++++++++++++++++++++++++--- src/main.rs | 22 +++++++------ src/parameters.rs | 15 ++++++++- 4 files changed, 103 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3514282..f26e6cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -265,9 +265,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.89" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "538c092e5586f4cdd7dd8078c4a79220e3e168880218124dcbce860f0ea938c6" +checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" [[package]] name = "log" @@ -650,9 +650,9 @@ checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "walkdir" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", "winapi", diff --git a/src/formats.rs b/src/formats.rs index a6b8d59..db09466 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -1,14 +1,15 @@ //! The various formats that [fif](crate) can output to. -use std::io::{self, Write}; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; +use std::io::{self, Write}; use std::path::Path; use snailquote::escape; use crate::scan_error::ScanError; use crate::{Findings, BACKEND}; +use std::ffi::OsStr; /// The current version of fif, as defined in Cargo.toml. const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); @@ -36,6 +37,12 @@ impl<'a> From<&'a Path> for Writable<'a> { } } +impl<'a> From<&'a OsStr> for Writable<'a> { + fn from(p: &'a OsStr) -> Writable<'a> { + Writable::Path(p.as_ref()) + } +} + fn smart_write(f: &mut W, writeables: &[Writable]) -> io::Result<()> { // ehhhh for writeable in writeables { @@ -47,13 +54,15 @@ fn smart_write(f: &mut W, writeables: &[Writable]) -> io::Result<()> { if let Some(string) = path.to_str() { write!(f, "{}", escape(string))? } else { - write!(f, "'''")?; + write!(f, "'")?; #[cfg(unix)] f.write_all(&*path.as_os_str().as_bytes())?; + // TODO: implement bonked strings for windows + // something like: + // f.write_all(&*path.as_os_str().encode_wide().collect::>())?; #[cfg(windows)] - write!(f, "{}", path.as_os_str().to_string_lossy())?; // TODO: implement bonked strings for windows - // f.write_all(&*path.as_os_str().encode_wide().collect::>())?; - write!(f, "'''")? + write!(f, "{}", path.as_os_str().to_string_lossy())?; + write!(f, "'")? } } } @@ -158,3 +167,62 @@ impl Format for Script { writeln!(f, "\necho 'Done.'") } } + +/// PowerShell script. +pub struct PowerShell {} + +impl Format for PowerShell { + fn new() -> Self { + Self {} + } + + fn rename(&self, f: &mut W, from: &Path, to: &Path) -> io::Result<()> { + // unfortunately there doesn't seem to be an equivalent of sh's `mv -i` -- passing the '-Confirm' flag will prompt + // the user to confirm every single rename, and using Move-Item -Force will always overwrite without prompting. + // there doesn't seem to be a way to rename the file, prompting only if the target already exists. + smart_write( + f, + &[ + "Rename-Item -Path ".into(), + from.into(), + " -NewName ".into(), + to.file_name().unwrap().into(), + Writable::Newline, + ], + ) + } + + fn no_known_extension(&self, f: &mut W, path: &Path) -> io::Result<()> { + smart_write( + f, + &["Write-Output @'\nNo known extension for ".into(), path.into(), "\n'@".into()], + ) + } + + fn unreadable(&self, f: &mut W, path: &Path) -> io::Result<()> { + smart_write( + f, + &["Write-Output @'\nFailed to read ".into(), path.into(), "\n'@".into()], + ) + } + + fn unknown_type(&self, f: &mut W, path: &Path) -> io::Result<()> { + smart_write( + f, + &["<# Failed to detect mime type for ".into(), path.into(), "#>".into()], + ) + } + + fn header(&self, _: &Entries, f: &mut W) -> io::Result<()> { + writeln!( + f, + "#!/usr/bin/env pwsh\n# Generated by fif {} ({} backend)", + VERSION.unwrap_or("???"), + BACKEND + ) + } + + fn footer(&self, _: &Entries, f: &mut W) -> io::Result<()> { + writeln!(f, "\nWrite-Output 'Done!'") + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 185a98c..98d9b7a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,7 @@ use smartstring::alias::String; use walkdir::{DirEntry, WalkDir}; use crate::findings::Findings; -use crate::formats::{Format, Script}; +use crate::formats::{Format, Script, PowerShell}; use crate::mime_db::MimeDb; use crate::parameters::{OutputFormat, ScanOpts}; use crate::scan_error::ScanError; @@ -125,15 +125,17 @@ fn main() { exit(0); } - match args.output_format { - OutputFormat::Script => { - let s = Script::new(); - if s.write_all(&results, &mut BufWriter::new(stdout().lock())).is_err() { - error!("Failed to write to stdout."); - exit(exitcode::IOERR); - } - } - OutputFormat::Text => todo!(), + let mut buffered_stdout = BufWriter::new(stdout()); + + let result = match args.output_format { + OutputFormat::Script => Script::new().write_all(&results, &mut buffered_stdout), + OutputFormat::PowerShell | OutputFormat::Powershell => PowerShell::new().write_all(&results, &mut buffered_stdout), + OutputFormat::Text => todo!() + }; + + if result.is_err() { + error!("Failed to write to stdout."); + exit(exitcode::IOERR); } debug!("Done"); diff --git a/src/parameters.rs b/src/parameters.rs index 6524513..b6dd6ae 100644 --- a/src/parameters.rs +++ b/src/parameters.rs @@ -5,11 +5,24 @@ use std::path::PathBuf; use crate::extension_set::ExtensionSet; use clap::{AppSettings, Clap}; use smartstring::{LazyCompact, SmartString}; +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(windows)] { + const DEFAULT_FORMAT: &str = "powershell"; + } else { + const DEFAULT_FORMAT: &str = "script"; + } +} #[derive(Clap, PartialEq, Debug)] pub enum OutputFormat { /// A Bourne shell compatible script. Script, + /// A PowerShell script. + PowerShell, + /// Also a PowerShell script, with different casing to allow for `fif -o powershell`. + Powershell, /// Plain text. Text, } @@ -52,7 +65,7 @@ pub struct Parameters { pub scan_extensionless: bool, /// Output format to use - #[clap(short, long, default_value = "script", arg_enum)] + #[clap(short, long, default_value = DEFAULT_FORMAT, arg_enum)] pub output_format: OutputFormat, /// Directory to process