diff --git a/Cargo.toml b/Cargo.toml index e87f0b9..8df72fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "yamd" description = "Yet Another Markdown Document (flavour)" -version = "0.14.0" +version = "0.14.1" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/Lurk/yamd" @@ -9,9 +9,9 @@ readme = "README.md" keywords = ["markdown", "parser"] [dependencies] -serde = { version = "1.0.197", features = ["derive"] } +serde = { version = "1.0.215", features = ["derive"] } [dev-dependencies] -pretty_assertions = "1.4.0" +pretty_assertions = "1.4.1" diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000..1a45eee --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000..64a7a80 --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "yamd-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" + +[dependencies.yamd] +path = ".." + +[[bin]] +name = "deserialize" +path = "fuzz_targets/deserialize.rs" +test = false +doc = false +bench = false diff --git a/fuzz/fuzz_targets/deserialize.rs b/fuzz/fuzz_targets/deserialize.rs new file mode 100644 index 0000000..b68f78d --- /dev/null +++ b/fuzz/fuzz_targets/deserialize.rs @@ -0,0 +1,9 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + if let Ok(s) = std::str::from_utf8(data) { + let _ = yamd::deserialize(s); + } +}); diff --git a/src/parser/anchor.rs b/src/parser/anchor.rs index a926843..9f724d2 100644 --- a/src/parser/anchor.rs +++ b/src/parser/anchor.rs @@ -23,7 +23,9 @@ pub(crate) fn anchor(p: &mut Parser<'_>) -> Option { p.next_token(); paren_count += 1; } - TokenKind::RightParenthesis if right_square_bracket_pos.is_some() => { + TokenKind::RightParenthesis + if right_square_bracket_pos.is_some() && paren_count > 0 => + { last_right_paren_pos.replace(pos); p.next_token(); paren_count -= 1; @@ -133,4 +135,14 @@ mod tests { Some((&Token::new(TokenKind::Literal, "[", Position::default()), 0)) ) } + + #[test] + fn right_paren() { + let mut p = Parser::new("[a])"); + assert_eq!(anchor(&mut p), None); + assert_eq!( + p.peek(), + Some((&Token::new(TokenKind::Literal, "[", Position::default()), 0)) + ) + } } diff --git a/src/parser/metadata.rs b/src/parser/metadata.rs index dd8d6ad..b4a5ede 100644 --- a/src/parser/metadata.rs +++ b/src/parser/metadata.rs @@ -13,7 +13,11 @@ pub(crate) fn metadata(p: &mut Parser) -> Option { match t.kind { TokenKind::Minus if t.slice.len() == 3 && t.position.column == 0 => { p.next_token(); - return Some(p.range_to_string(start + 2..pos - 1)); + if (start + 2) > (pos - 1) { + return Some(String::from("")); + } else { + return Some(p.range_to_string(start + 2..pos - 1)); + } } _ => { p.next_token(); @@ -85,4 +89,10 @@ mod tests { )) ) } + + #[test] + fn no_content() { + let mut p = Parser::new("---\n---"); + assert_eq!(metadata(&mut p), Some(String::from(""))); + } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 5ca2e46..e0265b7 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -55,8 +55,9 @@ impl<'input> Parser<'input> { pub fn next_token(&mut self) -> Option<&Token<'input>> { if self.stack.len() > self.stack_pos { + let res = self.stack.get(self.stack_pos); self.stack_pos += 1; - return self.stack.get(self.stack_pos); + return res; }; self.stack.push(self.lexer.next()?); @@ -210,4 +211,15 @@ mod tests { Some((&Token::new(TokenKind::Literal, "!", Position::default()), 0)) ) } + + #[test] + fn backtrack() { + let mut p = Parser::new("!"); + p.next_token(); + p.backtrack(0); + assert_eq!( + p.next_token(), + Some(&Token::new(TokenKind::Bang, "!", Position::default())) + ); + } }