83 lines
2.4 KiB
Rust
83 lines
2.4 KiB
Rust
#![no_main]
|
|
#![allow(missing_docs)]
|
|
#![allow(clippy::unwrap_used)]
|
|
|
|
use anyhow::Result;
|
|
use libfuzzer_sys::fuzz_target;
|
|
use std::sync::LazyLock;
|
|
|
|
static TOKIO_RT: LazyLock<tokio::runtime::Runtime> =
|
|
LazyLock::new(|| tokio::runtime::Runtime::new().unwrap());
|
|
|
|
static SHELL_TEMPLATE: LazyLock<brush_core::Shell> = LazyLock::new(|| {
|
|
TOKIO_RT
|
|
.block_on(
|
|
brush_core::Shell::builder()
|
|
.profile(brush_core::ProfileLoadBehavior::Skip)
|
|
.rc(brush_core::RcLoadBehavior::Skip)
|
|
.build(),
|
|
)
|
|
.unwrap()
|
|
});
|
|
|
|
#[expect(clippy::unused_async)]
|
|
async fn parse_async(shell: brush_core::Shell, input: String) -> Result<()> {
|
|
const DEFAULT_TIMEOUT_IN_SECONDS: u64 = 15;
|
|
|
|
//
|
|
// Instantiate a brush shell with defaults, then try to parse the input.
|
|
//
|
|
let our_parse_result = shell.parse_string(input.clone());
|
|
|
|
//
|
|
// Now run it under 'bash -n -t' as a crude way to see if it's at least valid syntax.
|
|
//
|
|
let mut oracle_cmd = std::process::Command::new("bash");
|
|
oracle_cmd
|
|
.arg("--noprofile")
|
|
.arg("--norc")
|
|
.arg("-O")
|
|
.arg("extglob")
|
|
.arg("-n")
|
|
.arg("-t");
|
|
|
|
let mut oracle_cmd = assert_cmd::Command::from_std(oracle_cmd);
|
|
|
|
oracle_cmd.timeout(std::time::Duration::from_secs(DEFAULT_TIMEOUT_IN_SECONDS));
|
|
|
|
let mut input = input;
|
|
input.push('\n');
|
|
oracle_cmd.write_stdin(input.as_bytes());
|
|
|
|
let oracle_result = oracle_cmd.output()?;
|
|
|
|
//
|
|
// Compare results.
|
|
//
|
|
if our_parse_result.is_ok() != oracle_result.status.success() {
|
|
Err(anyhow::anyhow!(
|
|
"Mismatched parse results: {oracle_result:?} vs {our_parse_result:?}"
|
|
))
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fuzz_target!(|input: String| {
|
|
// Ignore known problematic cases without actually running them.
|
|
if input.is_empty()
|
|
|| input.contains(|c: char| c.is_ascii_control() || !c.is_ascii()) // non-ascii chars (or control sequences)
|
|
|| input.contains('!') // history expansions
|
|
|| (input.contains('[') && !input.contains(']')) // ???
|
|
|| input.contains("<<") // weirdness with here docs
|
|
|| input.ends_with('\\') // unterminated trailing escape char?
|
|
|| input.contains("|&")
|
|
// unimplemented bash-ism
|
|
{
|
|
return;
|
|
}
|
|
|
|
let shell = SHELL_TEMPLATE.clone();
|
|
TOKIO_RT.block_on(parse_async(shell, input)).unwrap();
|
|
});
|