From cc1cc27613617f6e314c1d13dbad3249e28b1649 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 16 Dec 2021 01:11:47 +0900 Subject: [PATCH] fix: disallow merging dotted key and inline table current code mistakenly allows the following TOML file. ```toml a.b = 42 # table "a" is defined here, implicitly a = {c = 3.14} # table "a" is overwritten here ``` But we need to allow the following (structually similar) TOML file. ```toml a.b = 42 # table "a" is defined here, implicitly a.c = 3.14 # table "a" is merged with {c = 3.14} ``` To distinguish those, we check whether the current table is defined as an inline table or via dotted key. If the table we are inserting is defined via dotted key, we accept it and merge the table. If the table being inserted is defined as an inline table, then we report an error. --- toml/parser.hpp | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index ff62390..e311799 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1257,6 +1257,9 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last) // forward decl for is_valid_forward_table_definition result, region>, std::string> parse_table_key(location& loc); +template +result, std::string> +parse_inline_table(location& loc); // The following toml file is allowed. // ```toml @@ -1282,16 +1285,41 @@ parse_table_key(location& loc); // of the key. If the key region points deeper node, it would be allowed. // Otherwise, the key points the same node. It would be rejected. template -bool is_valid_forward_table_definition(const Value& fwd, +bool is_valid_forward_table_definition(const Value& fwd, const Value& inserting, Iterator key_first, Iterator key_curr, Iterator key_last) { + // ------------------------------------------------------------------------ + // check type of the value to be inserted/merged + + std::string inserting_reg = ""; + if(const auto ptr = detail::get_region(inserting)) + { + inserting_reg = ptr->str(); + } + location inserting_def("internal", std::move(inserting_reg)); + if(const auto inlinetable = parse_inline_table(inserting_def)) + { + // check if we are overwriting existing table. + // ```toml + // # NG + // a.b = 42 + // a = {d = 3.14} + // ``` + // Inserting an inline table to a existing super-table is not allowed in + // any case. If we found it, we can reject it without further checking. + return false; + } + + // ------------------------------------------------------------------------ + // check table defined before + std::string internal = ""; if(const auto ptr = detail::get_region(fwd)) { internal = ptr->str(); } location def("internal", std::move(internal)); - if(const auto tabkeys = parse_table_key(def)) + if(const auto tabkeys = parse_table_key(def)) // [table.key] { // table keys always contains all the nodes from the root. const auto& tks = tabkeys.unwrap().first; @@ -1481,7 +1509,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, if(tab->at(k).is_table() && v.is_table()) { if(!is_valid_forward_table_definition( - tab->at(k), first, iter, last)) + tab->at(k), v, first, iter, last)) { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: table (\"",