mod support {
pub mod parser_hardening;
}
use proptest::prelude::*;
use reddb_server::storage::query::parser::{self, ParseError, ParserLimits};
use support::parser_hardening::{
self as harness, assert_no_panic_on, corpus::adversarial_inputs, sql_grammar, HardenedParser,
};
pub struct SqlParser;
impl HardenedParser for SqlParser {
type Error = ParseError;
fn parse(input: &str) -> Result<(), Self::Error> {
parser::parse(input).map(|_| ())
}
fn parse_with_limits(input: &str, limits: ParserLimits) -> Result<(), Self::Error> {
let mut p = parser::Parser::with_limits(input, limits)?;
p.parse().map(|_| ())
}
}
#[test]
fn parser_does_not_panic_on_adversarial_corpus() {
let handle = std::thread::Builder::new()
.stack_size(8 * 1024 * 1024)
.spawn(|| {
for (name, input) in adversarial_inputs() {
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
assert_no_panic_on::<SqlParser>(&input);
}));
if result.is_err() {
panic!("adversarial corpus entry {} panicked", name);
}
}
})
.expect("spawn corpus thread");
handle.join().expect("corpus thread panic");
}
proptest! {
#![proptest_config(ProptestConfig {
cases: 256,
max_shrink_iters: 64,
..ProptestConfig::default()
})]
#[test]
fn proptest_select_roundtrips(s in sql_grammar::select_stmt()) {
harness::roundtrip_property::<SqlParser>(&s);
prop_assert!(SqlParser::parse(&s).is_ok(), "select did not parse: {}", s);
}
#[test]
fn proptest_insert_roundtrips(s in sql_grammar::insert_stmt()) {
harness::roundtrip_property::<SqlParser>(&s);
prop_assert!(SqlParser::parse(&s).is_ok(), "insert did not parse: {}", s);
}
#[test]
fn proptest_update_roundtrips(s in sql_grammar::update_stmt()) {
harness::roundtrip_property::<SqlParser>(&s);
prop_assert!(SqlParser::parse(&s).is_ok(), "update did not parse: {}", s);
}
#[test]
fn proptest_delete_roundtrips(s in sql_grammar::delete_stmt()) {
harness::roundtrip_property::<SqlParser>(&s);
prop_assert!(SqlParser::parse(&s).is_ok(), "delete did not parse: {}", s);
}
#[test]
fn proptest_arbitrary_bytes_no_panic(s in ".{0,2048}") {
harness::roundtrip_property::<SqlParser>(&s);
}
#[test]
fn proptest_input_size_limit_enforced(
len in 100usize..1000,
) {
let limits = ParserLimits {
max_input_bytes: 64,
..ParserLimits::default()
};
let input = "a".repeat(len);
let r = SqlParser::parse_with_limits(&input, limits);
prop_assert!(r.is_err(), "oversized input must error");
}
}