|
1 | 1 | #![no_main] |
2 | 2 |
|
| 3 | +use libfuzzer_sys::arbitrary::{self, Arbitrary}; |
3 | 4 | use libfuzzer_sys::fuzz_target; |
| 5 | +use std::sync::Arc; |
4 | 6 |
|
5 | 7 | use comrak::{ |
6 | 8 | markdown_to_commonmark, markdown_to_commonmark_xml, markdown_to_html, options, Options, |
7 | 9 | ResolvedReference, |
8 | 10 | }; |
9 | | -use std::sync::Arc; |
10 | 11 |
|
11 | | -fuzz_target!(|s: &str| { |
12 | | - let url_rewriter = |input: &str| format!("{input}#rewritten"); |
13 | | - let extension = options::Extension { |
14 | | - strikethrough: true, |
15 | | - tagfilter: true, |
16 | | - table: true, |
17 | | - autolink: true, |
18 | | - tasklist: true, |
19 | | - superscript: true, |
20 | | - header_ids: Some("user-content-".to_string()), |
21 | | - footnotes: true, |
22 | | - inline_footnotes: true, |
23 | | - description_lists: true, |
24 | | - front_matter_delimiter: Some("---".to_string()), |
25 | | - multiline_block_quotes: true, |
26 | | - alerts: true, |
27 | | - math_dollars: true, |
28 | | - math_code: true, |
29 | | - shortcodes: true, |
30 | | - wikilinks_title_after_pipe: true, |
31 | | - wikilinks_title_before_pipe: true, |
32 | | - underline: true, |
33 | | - subscript: true, |
34 | | - spoiler: true, |
35 | | - greentext: true, |
36 | | - image_url_rewriter: Some(Arc::new(url_rewriter)), |
37 | | - link_url_rewriter: Some(Arc::new(url_rewriter)), |
38 | | - cjk_friendly_emphasis: true, |
39 | | - subtext: true, |
40 | | - }; |
| 12 | +#[derive(Arbitrary, Debug)] |
| 13 | +struct FuzzOptions { |
| 14 | + extension: FuzzExtensionOptions, |
| 15 | + parse: FuzzParseOptions, |
| 16 | + render: FuzzRenderOptions, |
| 17 | +} |
| 18 | + |
| 19 | +impl FuzzOptions { |
| 20 | + fn to_options<'c>( |
| 21 | + &self, |
| 22 | + url_rewriter: Arc<dyn options::URLRewriter + 'c>, |
| 23 | + broken_link_callback: Arc<dyn options::BrokenLinkCallback + 'c>, |
| 24 | + ) -> Options<'c> { |
| 25 | + Options { |
| 26 | + extension: self.extension.to_options(url_rewriter), |
| 27 | + parse: self.parse.to_options(broken_link_callback), |
| 28 | + render: self.render.to_options(), |
| 29 | + } |
| 30 | + } |
| 31 | +} |
| 32 | + |
| 33 | +#[derive(Arbitrary, Debug)] |
| 34 | +struct FuzzExtensionOptions { |
| 35 | + strikethrough: bool, |
| 36 | + tagfilter: bool, |
| 37 | + table: bool, |
| 38 | + autolink: bool, |
| 39 | + tasklist: bool, |
| 40 | + superscript: bool, |
| 41 | + footnotes: bool, |
| 42 | + description_lists: bool, |
| 43 | + multiline_block_quotes: bool, |
| 44 | + math_dollars: bool, |
| 45 | + math_code: bool, |
| 46 | + shortcodes: bool, |
| 47 | + wikilinks_title_after_pipe: bool, |
| 48 | + wikilinks_title_before_pipe: bool, |
| 49 | + underline: bool, |
| 50 | + spoiler: bool, |
| 51 | + greentext: bool, |
| 52 | + alerts: bool, |
| 53 | + inline_footnotes: bool, |
| 54 | + subscript: bool, |
| 55 | + subtext: bool, |
| 56 | + cjk_friendly_emphasis: bool, |
| 57 | + header_ids: bool, |
| 58 | + front_matter_delimiter: bool, |
| 59 | + image_url_rewriter: bool, |
| 60 | + link_url_rewriter: bool, |
| 61 | +} |
| 62 | + |
| 63 | +impl FuzzExtensionOptions { |
| 64 | + fn to_options<'c>( |
| 65 | + &self, |
| 66 | + url_rewriter: Arc<dyn options::URLRewriter + 'c>, |
| 67 | + ) -> options::Extension<'c> { |
| 68 | + options::Extension { |
| 69 | + strikethrough: self.strikethrough, |
| 70 | + tagfilter: self.tagfilter, |
| 71 | + table: self.table, |
| 72 | + autolink: self.autolink, |
| 73 | + tasklist: self.tasklist, |
| 74 | + superscript: self.superscript, |
| 75 | + footnotes: self.footnotes, |
| 76 | + inline_footnotes: self.inline_footnotes, |
| 77 | + description_lists: self.description_lists, |
| 78 | + multiline_block_quotes: self.multiline_block_quotes, |
| 79 | + math_dollars: self.math_dollars, |
| 80 | + math_code: self.math_code, |
| 81 | + shortcodes: self.shortcodes, |
| 82 | + wikilinks_title_after_pipe: self.wikilinks_title_after_pipe, |
| 83 | + wikilinks_title_before_pipe: self.wikilinks_title_before_pipe, |
| 84 | + underline: self.underline, |
| 85 | + spoiler: self.spoiler, |
| 86 | + greentext: self.greentext, |
| 87 | + alerts: self.alerts, |
| 88 | + subscript: self.subscript, |
| 89 | + subtext: self.subtext, |
| 90 | + cjk_friendly_emphasis: self.cjk_friendly_emphasis, |
| 91 | + header_ids: if self.header_ids { |
| 92 | + Some("user-content-".into()) |
| 93 | + } else { |
| 94 | + None |
| 95 | + }, |
| 96 | + front_matter_delimiter: if self.front_matter_delimiter { |
| 97 | + Some("---".into()) |
| 98 | + } else { |
| 99 | + None |
| 100 | + }, |
| 101 | + image_url_rewriter: if self.image_url_rewriter { |
| 102 | + Some(url_rewriter.clone()) |
| 103 | + } else { |
| 104 | + None |
| 105 | + }, |
| 106 | + link_url_rewriter: if self.link_url_rewriter { |
| 107 | + Some(url_rewriter) |
| 108 | + } else { |
| 109 | + None |
| 110 | + }, |
| 111 | + } |
| 112 | + } |
| 113 | +} |
| 114 | + |
| 115 | +#[derive(Arbitrary, Debug)] |
| 116 | +struct FuzzParseOptions { |
| 117 | + smart: bool, |
| 118 | + relaxed_tasklist_matching: bool, |
| 119 | + relaxed_autolinks: bool, |
| 120 | + ignore_setext: bool, |
| 121 | + tasklist_in_table: bool, |
| 122 | + leave_footnote_definitions: bool, |
| 123 | + default_info_string: bool, |
| 124 | + broken_link_callback: bool, |
| 125 | +} |
| 126 | + |
| 127 | +impl FuzzParseOptions { |
| 128 | + fn to_options<'c>( |
| 129 | + &self, |
| 130 | + broken_link_callback: Arc<dyn options::BrokenLinkCallback + 'c>, |
| 131 | + ) -> options::Parse<'c> { |
| 132 | + options::Parse { |
| 133 | + smart: self.smart, |
| 134 | + relaxed_tasklist_matching: self.relaxed_tasklist_matching, |
| 135 | + relaxed_autolinks: self.relaxed_autolinks, |
| 136 | + ignore_setext: self.ignore_setext, |
| 137 | + tasklist_in_table: self.tasklist_in_table, |
| 138 | + leave_footnote_definitions: self.leave_footnote_definitions, |
| 139 | + default_info_string: if self.default_info_string { |
| 140 | + Some("rust".into()) |
| 141 | + } else { |
| 142 | + None |
| 143 | + }, |
| 144 | + broken_link_callback: if self.broken_link_callback { |
| 145 | + Some(broken_link_callback) |
| 146 | + } else { |
| 147 | + None |
| 148 | + }, |
| 149 | + } |
| 150 | + } |
| 151 | +} |
41 | 152 |
|
42 | | - let cb = |link_ref: options::BrokenLinkReference| { |
| 153 | +#[derive(Arbitrary, Debug)] |
| 154 | +struct FuzzRenderOptions { |
| 155 | + hardbreaks: bool, |
| 156 | + github_pre_lang: bool, |
| 157 | + full_info_string: bool, |
| 158 | + width: usize, |
| 159 | + r#unsafe: bool, |
| 160 | + escape: bool, |
| 161 | + list_style: options::ListStyleType, |
| 162 | + sourcepos: bool, |
| 163 | + escaped_char_spans: bool, |
| 164 | + ignore_empty_links: bool, |
| 165 | + gfm_quirks: bool, |
| 166 | + prefer_fenced: bool, |
| 167 | + figure_with_caption: bool, |
| 168 | + tasklist_classes: bool, |
| 169 | + ol_width: usize, |
| 170 | + experimental_minimize_commonmark: bool, |
| 171 | +} |
| 172 | + |
| 173 | +impl FuzzRenderOptions { |
| 174 | + fn to_options(&self) -> options::Render { |
| 175 | + options::Render { |
| 176 | + hardbreaks: self.hardbreaks, |
| 177 | + github_pre_lang: self.github_pre_lang, |
| 178 | + full_info_string: self.full_info_string, |
| 179 | + width: self.width, |
| 180 | + r#unsafe: self.r#unsafe, |
| 181 | + escape: self.escape, |
| 182 | + list_style: self.list_style, |
| 183 | + sourcepos: self.sourcepos, |
| 184 | + escaped_char_spans: self.escaped_char_spans, |
| 185 | + ignore_empty_links: self.ignore_empty_links, |
| 186 | + gfm_quirks: self.gfm_quirks, |
| 187 | + prefer_fenced: self.prefer_fenced, |
| 188 | + figure_with_caption: self.figure_with_caption, |
| 189 | + tasklist_classes: self.tasklist_classes, |
| 190 | + ol_width: self.ol_width, |
| 191 | + experimental_minimize_commonmark: self.experimental_minimize_commonmark, |
| 192 | + } |
| 193 | + } |
| 194 | +} |
| 195 | + |
| 196 | +#[derive(Arbitrary, Debug)] |
| 197 | +struct Input { |
| 198 | + options: FuzzOptions, |
| 199 | + markdown: String, |
| 200 | +} |
| 201 | + |
| 202 | +fuzz_target!(|input: Input| { |
| 203 | + let url_rewriter = |input: &str| format!("{input}#rewritten"); |
| 204 | + let broken_link_callback = |link_ref: options::BrokenLinkReference| { |
43 | 205 | Some(ResolvedReference { |
44 | 206 | url: link_ref.normalized.to_string(), |
45 | 207 | title: link_ref.original.to_string(), |
46 | 208 | }) |
47 | 209 | }; |
48 | | - let parse = options::Parse { |
49 | | - smart: true, |
50 | | - default_info_string: Some("rust".to_string()), |
51 | | - relaxed_tasklist_matching: true, |
52 | | - relaxed_autolinks: true, |
53 | | - broken_link_callback: Some(Arc::new(cb)), |
54 | | - ignore_setext: true, |
55 | | - tasklist_in_table: true, |
56 | | - }; |
57 | | - |
58 | | - let render = options::Render { |
59 | | - hardbreaks: true, |
60 | | - github_pre_lang: true, |
61 | | - full_info_string: true, |
62 | | - width: 80, |
63 | | - r#unsafe: true, |
64 | | - escape: true, |
65 | | - list_style: options::ListStyleType::Star, |
66 | | - sourcepos: true, |
67 | | - escaped_char_spans: true, |
68 | | - ignore_empty_links: true, |
69 | | - gfm_quirks: true, |
70 | | - prefer_fenced: true, |
71 | | - figure_with_caption: true, |
72 | | - tasklist_classes: true, |
73 | | - ol_width: 3, |
74 | | - experimental_minimize_commonmark: true, |
75 | | - }; |
76 | 210 |
|
77 | | - let options = Options { |
78 | | - extension, |
79 | | - parse, |
80 | | - render, |
81 | | - }; |
| 211 | + let s = &input.markdown; |
| 212 | + let options = input |
| 213 | + .options |
| 214 | + .to_options(Arc::new(url_rewriter), Arc::new(broken_link_callback)); |
82 | 215 |
|
83 | 216 | let _ = markdown_to_html(s, &options); |
84 | 217 | let _ = markdown_to_commonmark(s, &options); |
|
0 commit comments