@@ -227,11 +227,18 @@ pub(crate) struct Parser<'parser> {
227227 /// and so on.
228228 scope_stack : Vec < Rc < RefCell < Value > > > ,
229229
230+ /// To avoid accidentally overflowing the program stack, limit the number of
231+ /// nested scopes and generate an error if it is exceeded.
232+ nesting_limit : usize ,
233+
230234 /// Captures a colon token when expected.
231235 colon_capturer : Capturer ,
232236}
233237
234238impl < ' parser > Parser < ' parser > {
239+ /// The default limit of nested scopes when parsing a JSON5 document.
240+ pub const DEFAULT_NESTING_LIMIT : usize = 1000 ;
241+
235242 pub fn new ( filename : & ' parser Option < String > ) -> Self {
236243 let remaining = "" ;
237244 let current_line = & remaining;
@@ -244,11 +251,19 @@ impl<'parser> Parser<'parser> {
244251 column_number : 1 ,
245252 next_line_number : 1 ,
246253 next_column_number : 1 ,
247- scope_stack : vec ! [ Rc :: new( RefCell :: new( Array :: new( vec![ ] ) ) ) ] ,
254+ scope_stack : Vec :: default ( ) ,
255+ nesting_limit : Self :: DEFAULT_NESTING_LIMIT ,
248256 colon_capturer : Capturer :: new ( & COLON ) ,
249257 }
250258 }
251259
260+ /// To avoid accidentally overflowing the program stack, there is a mutable
261+ /// limit on the number of nested scopes allowed. If this limit is exceeded
262+ /// while parsing a document, a parser error is generated.
263+ pub fn set_nesting_limit ( & mut self , new_limit : usize ) {
264+ self . nesting_limit = new_limit;
265+ }
266+
252267 fn current_scope ( & self ) -> Rc < RefCell < Value > > {
253268 assert ! ( self . scope_stack. len( ) > 0 ) ;
254269 self . scope_stack . last ( ) . unwrap ( ) . clone ( )
@@ -311,6 +326,12 @@ impl<'parser> Parser<'parser> {
311326 self . with_container ( |container| container. add_value ( value_ref. clone ( ) , self ) ) ?;
312327 if is_container {
313328 self . scope_stack . push ( value_ref. clone ( ) ) ;
329+ if self . scope_stack . len ( ) > self . nesting_limit {
330+ return Err ( self . error ( format ! (
331+ "The given JSON5 document exceeds the parser's nesting limit of {}" ,
332+ self . nesting_limit
333+ ) ) ) ;
334+ }
314335 }
315336 Ok ( ( ) )
316337 }
@@ -483,7 +504,11 @@ impl<'parser> Parser<'parser> {
483504
484505 fn exit_scope ( & mut self ) -> Result < ( ) , Error > {
485506 self . scope_stack . pop ( ) ;
486- Ok ( ( ) )
507+ if self . scope_stack . is_empty ( ) {
508+ Err ( self . error ( "Closing brace without a matching opening brace" ) )
509+ } else {
510+ Ok ( ( ) )
511+ }
487512 }
488513
489514 fn close_object ( & mut self ) -> Result < ( ) , Error > {
@@ -515,7 +540,7 @@ impl<'parser> Parser<'parser> {
515540 indicator += & "~" . repeat ( if self . line_number == self . next_line_number {
516541 self . next_column_number - self . column_number - 1
517542 } else {
518- self . current_line . len ( ) - self . column_number
543+ 0
519544 } ) ;
520545 }
521546 Error :: parse ( self . location ( ) , format ! ( "{}:\n {}\n {}" , err, self . current_line, indicator) )
@@ -563,8 +588,34 @@ impl<'parser> Parser<'parser> {
563588 }
564589 }
565590
591+ /// Parse the given document string as a JSON5 document containing Array
592+ /// elements (with implicit outer braces). Document locations (use in, for
593+ /// example, error messages), are 1-based and start at line 1, column 1.
566594 pub fn parse ( & mut self , buffer : & ' parser str ) -> Result < Array , Error > {
595+ self . parse_from_location ( buffer, 1 , 1 )
596+ }
597+
598+ /// Parse the given document string as a JSON5 document containing Array
599+ /// elements (with implicit outer braces), and use the given 1-based line
600+ /// and column numbers when referring to document locations.
601+ pub fn parse_from_location (
602+ & mut self ,
603+ buffer : & ' parser str ,
604+ starting_line_number : usize ,
605+ starting_column_number : usize ,
606+ ) -> Result < Array , Error > {
567607 self . remaining = buffer;
608+ self . current_line = & self . remaining ;
609+
610+ assert ! ( starting_line_number > 0 , "document line numbers are 1-based" ) ;
611+ self . next_line_number = starting_line_number;
612+ self . next_column_number = starting_column_number;
613+
614+ self . next_line = self . current_line ;
615+ self . line_number = self . next_line_number - 1 ;
616+ self . column_number = self . next_column_number - 1 ;
617+ self . scope_stack = vec ! [ Rc :: new( RefCell :: new( Array :: new( vec![ ] ) ) ) ] ;
618+
568619 let mut next_token = Capturer :: new ( & NEXT_TOKEN ) ;
569620 let mut single_quoted = Capturer :: new ( & SINGLE_QUOTED ) ;
570621 let mut double_quoted = Capturer :: new ( & DOUBLE_QUOTED ) ;
@@ -1680,4 +1731,85 @@ mod tests {
16801731 let object_value = Object :: new ( vec ! [ ] ) ;
16811732 assert ! ( object_value. is_object( ) ) ;
16821733 }
1734+
1735+ #[ test]
1736+ fn test_document_exceeds_nesting_limit ( ) {
1737+ let mut parser = Parser :: new ( & None ) ;
1738+ parser. set_nesting_limit ( 5 ) ;
1739+ let good_buffer = r##"{
1740+ list_of_lists_of_lists: [[[]]]
1741+ }"## ;
1742+ parser. parse_from_location ( & good_buffer, 8 , 15 ) . expect ( "should NOT exceed nesting limit" ) ;
1743+
1744+ let bad_buffer = r##"{
1745+ list_of_lists_of_lists: [[[[]]]]
1746+ }"## ;
1747+ let err = parser
1748+ . parse_from_location ( & bad_buffer, 8 , 15 )
1749+ . expect_err ( "should exceed nesting limit" ) ;
1750+ match err {
1751+ Error :: Parse ( _, message) => {
1752+ assert_eq ! (
1753+ message,
1754+ r##"The given JSON5 document exceeds the parser's nesting limit of 5:
1755+ list_of_lists_of_lists: [[[[]]]]
1756+ ^"##
1757+ )
1758+ }
1759+ _ => panic ! ( "expected a parser error" ) ,
1760+ }
1761+ }
1762+
1763+ #[ test]
1764+ fn test_parse_from_location_error_location ( ) {
1765+ let filename = Some ( "mixed_content.md" . to_string ( ) ) ;
1766+ let mixed_document = r##"
1767+ Mixed Content Doc
1768+ =================
1769+
1770+ This is a document with embedded JSON5 content.
1771+
1772+ ```json5
1773+ json5_value = {
1774+ // The next line should generate a parser error
1775+ 999,
1776+ }
1777+ ```
1778+
1779+ End of mixed content document.
1780+ "## ;
1781+ let json5_slice =
1782+ & mixed_document[ mixed_document. find ( "{" ) . unwrap ( ) ..mixed_document. find ( "}" ) . unwrap ( ) ] ;
1783+ let mut parser = Parser :: new ( & filename) ;
1784+ let err = parser
1785+ . parse_from_location ( json5_slice, 8 , 15 )
1786+ . expect_err ( "check error message for location" ) ;
1787+ match err {
1788+ Error :: Parse ( Some ( loc) , message) => {
1789+ assert_eq ! ( loc. file, Some ( "mixed_content.md" . to_owned( ) ) ) ;
1790+ assert_eq ! ( loc. line, 10 ) ;
1791+ assert_eq ! ( loc. col, 5 ) ;
1792+ assert_eq ! (
1793+ message,
1794+ r##"Object values require property names:
1795+ 999,
1796+ ^~~"##
1797+ )
1798+ }
1799+ _ => panic ! ( "expected a parser error" ) ,
1800+ }
1801+ }
1802+
1803+ #[ test]
1804+ fn test_doc_with_nulls ( ) {
1805+ let mut parser = Parser :: new ( & None ) ;
1806+ let buffer = "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[////[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\u{000} \u{000} \u{000} \u{000} \u{000} \u{000} \u{000} \u{000} \u{000} \u{000} \u{000} \u{000} \u{000} \u{000} ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" ;
1807+ let err = parser. parse ( & buffer) . expect_err ( "should fail" ) ;
1808+ match err {
1809+ Error :: Parse ( _, message) => {
1810+ assert ! ( message. starts_with( "Mismatched braces in the document:" ) ) ;
1811+ }
1812+ _ => panic ! ( "expected a parser error" ) ,
1813+ }
1814+ }
16831815}
0 commit comments