commit
176d026cbb
7 changed files with 67 additions and 10 deletions
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
|
@ -68,6 +68,12 @@ jobs:
|
||||||
run: make lint
|
run: make lint
|
||||||
fuzz:
|
fuzz:
|
||||||
name: Fuzz
|
name: Fuzz
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target:
|
||||||
|
- parse
|
||||||
|
- parse_balance
|
||||||
|
- html
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
|
@ -77,13 +83,10 @@ jobs:
|
||||||
rustup update nightly
|
rustup update nightly
|
||||||
rustup default nightly
|
rustup default nightly
|
||||||
cargo install afl
|
cargo install afl
|
||||||
- name: "Fuzz parser"
|
- name: "Fuzz"
|
||||||
run: |
|
run: |
|
||||||
echo core | sudo tee /proc/sys/kernel/core_pattern
|
echo core | sudo tee /proc/sys/kernel/core_pattern
|
||||||
AFL_TARGET=parse make afl_quick
|
AFL_TARGET=${{ matrix.target }} make afl_quick
|
||||||
- name: "Fuzz html"
|
|
||||||
run: |
|
|
||||||
AFL_TARGET=html make afl_quick
|
|
||||||
bench:
|
bench:
|
||||||
name: Benchmark
|
name: Benchmark
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
@ -557,8 +557,9 @@ impl<'s> Parser<'s> {
|
||||||
self.input.span = self.input.span.after(len);
|
self.input.span = self.input.span.after(len);
|
||||||
self.push(EventKind::Enter(Autolink));
|
self.push(EventKind::Enter(Autolink));
|
||||||
self.push(EventKind::Str);
|
self.push(EventKind::Str);
|
||||||
|
self.push(EventKind::Exit(Autolink));
|
||||||
self.input.span = self.input.span.after(1);
|
self.input.span = self.input.span.after(1);
|
||||||
return self.push(EventKind::Exit(Autolink));
|
return Some(Continue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -1507,22 +1508,22 @@ mod test {
|
||||||
"<https://example.com>",
|
"<https://example.com>",
|
||||||
(Enter(Autolink), "https://example.com"),
|
(Enter(Autolink), "https://example.com"),
|
||||||
(Str, "https://example.com"),
|
(Str, "https://example.com"),
|
||||||
(Exit(Autolink), ">")
|
(Exit(Autolink), "https://example.com")
|
||||||
);
|
);
|
||||||
test_parse!(
|
test_parse!(
|
||||||
"<a@b.c>",
|
"<a@b.c>",
|
||||||
(Enter(Autolink), "a@b.c"),
|
(Enter(Autolink), "a@b.c"),
|
||||||
(Str, "a@b.c"),
|
(Str, "a@b.c"),
|
||||||
(Exit(Autolink), ">"),
|
(Exit(Autolink), "a@b.c"),
|
||||||
);
|
);
|
||||||
test_parse!(
|
test_parse!(
|
||||||
"<http://a.b><http://c.d>",
|
"<http://a.b><http://c.d>",
|
||||||
(Enter(Autolink), "http://a.b"),
|
(Enter(Autolink), "http://a.b"),
|
||||||
(Str, "http://a.b"),
|
(Str, "http://a.b"),
|
||||||
(Exit(Autolink), ">"),
|
(Exit(Autolink), "http://a.b"),
|
||||||
(Enter(Autolink), "http://c.d"),
|
(Enter(Autolink), "http://c.d"),
|
||||||
(Str, "http://c.d"),
|
(Str, "http://c.d"),
|
||||||
(Exit(Autolink), ">")
|
(Exit(Autolink), "http://c.d"),
|
||||||
);
|
);
|
||||||
test_parse!("<not-a-url>", (Str, "<not-a-url>"));
|
test_parse!("<not-a-url>", (Str, "<not-a-url>"));
|
||||||
}
|
}
|
||||||
|
|
30
src/lib.rs
30
src/lib.rs
|
@ -1540,6 +1540,36 @@ mod test {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn autolink() {
|
||||||
|
test_parse!(
|
||||||
|
"<proto:url>\n",
|
||||||
|
Start(Paragraph, Attributes::new()),
|
||||||
|
Start(
|
||||||
|
Link("proto:url".into(), LinkType::AutoLink),
|
||||||
|
Attributes::new()
|
||||||
|
),
|
||||||
|
Str("proto:url".into()),
|
||||||
|
End(Link("proto:url".into(), LinkType::AutoLink)),
|
||||||
|
End(Paragraph),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn email() {
|
||||||
|
test_parse!(
|
||||||
|
"<name@domain>\n",
|
||||||
|
Start(Paragraph, Attributes::new()),
|
||||||
|
Start(
|
||||||
|
Link("name@domain".into(), LinkType::Email),
|
||||||
|
Attributes::new()
|
||||||
|
),
|
||||||
|
Str("name@domain".into()),
|
||||||
|
End(Link("name@domain".into(), LinkType::Email)),
|
||||||
|
End(Paragraph),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn footnote_references() {
|
fn footnote_references() {
|
||||||
test_parse!(
|
test_parse!(
|
||||||
|
|
|
@ -17,6 +17,10 @@ path = "src/main.rs"
|
||||||
name = "parse"
|
name = "parse"
|
||||||
path = "src/parse.rs"
|
path = "src/parse.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "parse_balance"
|
||||||
|
path = "src/parse_balance.rs"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "html"
|
name = "html"
|
||||||
path = "src/html.rs"
|
path = "src/html.rs"
|
||||||
|
|
|
@ -11,6 +11,21 @@ pub fn parse(data: &[u8]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensure containers are always balanced, i.e. opened and closed in correct order.
|
||||||
|
pub fn parse_balance(data: &[u8]) {
|
||||||
|
if let Ok(s) = std::str::from_utf8(data) {
|
||||||
|
let mut open = Vec::new();
|
||||||
|
for event in jotdown::Parser::new(s) {
|
||||||
|
match event {
|
||||||
|
jotdown::Event::Start(c, ..) => open.push(c.clone()),
|
||||||
|
jotdown::Event::End(c) => assert_eq!(open.pop().unwrap(), c),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(open, &[]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn html(data: &[u8]) {
|
pub fn html(data: &[u8]) {
|
||||||
if data.iter().any(|i| *i == 0) {
|
if data.iter().any(|i| *i == 0) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -8,6 +8,7 @@ fn main() {
|
||||||
|
|
||||||
let f = match target.as_str() {
|
let f = match target.as_str() {
|
||||||
"parse" => jotdown_afl::parse,
|
"parse" => jotdown_afl::parse,
|
||||||
|
"parse_balance" => jotdown_afl::parse_balance,
|
||||||
"html" => jotdown_afl::html,
|
"html" => jotdown_afl::html,
|
||||||
_ => panic!("unknown target '{}'", target),
|
_ => panic!("unknown target '{}'", target),
|
||||||
};
|
};
|
||||||
|
|
3
tests/afl/src/parse_balance.rs
Normal file
3
tests/afl/src/parse_balance.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
afl::fuzz!(|data: &[u8]| { jotdown_afl::parse_balance(data) });
|
||||||
|
}
|
Loading…
Reference in a new issue