1
1
use crate :: arena_tree:: Node ;
2
2
use crate :: nodes;
3
- use crate :: nodes:: { Ast , AstNode , NodeValue , TableAlignment } ;
3
+ use crate :: nodes:: { Ast , AstNode , NodeTable , NodeValue , TableAlignment } ;
4
4
use crate :: parser:: Parser ;
5
5
use crate :: scanners;
6
6
use crate :: strings:: trim;
@@ -9,14 +9,17 @@ use std::cmp::min;
9
9
10
10
use super :: inlines:: count_newlines;
11
11
12
+ // Limit to prevent a malicious input from causing a denial of service.
13
+ const MAX_AUTOCOMPLETED_CELLS : usize = 500_000 ;
14
+
12
15
pub fn try_opening_block < ' a > (
13
16
parser : & mut Parser < ' a , ' _ , ' _ > ,
14
17
container : & ' a AstNode < ' a > ,
15
18
line : & [ u8 ] ,
16
19
) -> Option < ( & ' a AstNode < ' a > , bool , bool ) > {
17
20
let aligns = match container. data . borrow ( ) . value {
18
21
NodeValue :: Paragraph => None ,
19
- NodeValue :: Table ( ref aligns ) => Some ( aligns . clone ( ) ) ,
22
+ NodeValue :: Table ( NodeTable { ref alignments , .. } ) => Some ( alignments . clone ( ) ) ,
20
23
_ => return None ,
21
24
} ;
22
25
@@ -74,7 +77,15 @@ fn try_opening_header<'a>(
74
77
}
75
78
76
79
let start = container. data . borrow ( ) . sourcepos . start ;
77
- let child = Ast :: new ( NodeValue :: Table ( alignments) , start) ;
80
+ let child = Ast :: new (
81
+ NodeValue :: Table ( NodeTable {
82
+ alignments,
83
+ num_columns : header_row. cells . len ( ) ,
84
+ num_rows : 0 ,
85
+ num_nonempty_cells : 0 ,
86
+ } ) ,
87
+ start,
88
+ ) ;
78
89
let table = parser. arena . alloc ( Node :: new ( RefCell :: new ( child) ) ) ;
79
90
container. append ( table) ;
80
91
@@ -88,7 +99,10 @@ fn try_opening_header<'a>(
88
99
) ;
89
100
}
90
101
91
- for cell in header_row. cells {
102
+ let mut i = 0 ;
103
+
104
+ while i < header_row. cells . len ( ) {
105
+ let cell = & header_row. cells [ i] ;
92
106
let ast_cell = parser. add_child (
93
107
header,
94
108
NodeValue :: TableCell ,
@@ -100,8 +114,12 @@ fn try_opening_header<'a>(
100
114
start. column_add ( ( cell. end_offset - header_row. paragraph_offset ) as isize ) ;
101
115
ast. internal_offset = cell. internal_offset ;
102
116
ast. content = cell. content . clone ( ) ;
117
+
118
+ i += 1 ;
103
119
}
104
120
121
+ incr_table_row_count ( container, i) ;
122
+
105
123
let offset = line. len ( ) - 1 - parser. offset ;
106
124
parser. advance_offset ( line, offset, false ) ;
107
125
@@ -117,6 +135,11 @@ fn try_opening_row<'a>(
117
135
if parser. blank {
118
136
return None ;
119
137
}
138
+
139
+ if get_num_autocompleted_cells ( container) > MAX_AUTOCOMPLETED_CELLS {
140
+ return None ;
141
+ }
142
+
120
143
let sourcepos = container. data . borrow ( ) . sourcepos ;
121
144
let this_row = match row ( & line[ parser. first_nonspace ..] ) {
122
145
Some ( this_row) => this_row,
@@ -148,9 +171,12 @@ fn try_opening_row<'a>(
148
171
cell_ast. content = cell. content . clone ( ) ;
149
172
150
173
last_column = cell_ast. sourcepos . end . column ;
174
+
151
175
i += 1 ;
152
176
}
153
177
178
+ incr_table_row_count ( container, i) ;
179
+
154
180
while i < alignments. len ( ) {
155
181
parser. add_child ( new_row, NodeValue :: TableCell , last_column) ;
156
182
i += 1 ;
@@ -305,6 +331,40 @@ fn unescape_pipes(string: &[u8]) -> Vec<u8> {
305
331
v
306
332
}
307
333
334
+ // Increment the number of rows in the table. Also update n_nonempty_cells,
335
+ // which keeps track of the number of cells which were parsed from the
336
+ // input file. (If one of the rows is too short, then the trailing cells
337
+ // are autocompleted. Autocompleted cells are not counted in n_nonempty_cells.)
338
+ // The purpose of this is to prevent a malicious input from generating a very
339
+ // large number of autocompleted cells, which could cause a denial of service
340
+ // vulnerability.
341
+ fn incr_table_row_count < ' a > ( container : & ' a AstNode < ' a > , i : usize ) -> bool {
342
+ return match container. data . borrow_mut ( ) . value {
343
+ NodeValue :: Table ( ref mut node_table) => {
344
+ node_table. num_rows += 1 ;
345
+ node_table. num_nonempty_cells += i;
346
+ true
347
+ }
348
+ _ => false ,
349
+ } ;
350
+ }
351
+
352
+ // Calculate the number of autocompleted cells.
353
+ fn get_num_autocompleted_cells < ' a > ( container : & ' a AstNode < ' a > ) -> usize {
354
+ return match container. data . borrow ( ) . value {
355
+ NodeValue :: Table ( ref node_table) => {
356
+ let num_cells = node_table. num_columns * node_table. num_rows ;
357
+
358
+ if num_cells < node_table. num_nonempty_cells {
359
+ 0
360
+ } else {
361
+ ( node_table. num_columns * node_table. num_rows ) - node_table. num_nonempty_cells
362
+ }
363
+ }
364
+ _ => 0 ,
365
+ } ;
366
+ }
367
+
308
368
pub fn matches ( line : & [ u8 ] ) -> bool {
309
369
row ( line) . is_some ( )
310
370
}
0 commit comments