mirror of
https://github.com/qpdf/qpdf.git
synced 2025-02-07 14:18:24 +00:00
JSON: detect duplicate dictionary keys while parsing
This commit is contained in:
parent
6d4e3ba8a4
commit
3eb77a7004
@ -42,6 +42,7 @@
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -149,6 +150,14 @@ class JSON
|
||||
QPDF_DLL
|
||||
bool isDictionary() const;
|
||||
|
||||
// If the key is already in the dictionary, return true.
|
||||
// Otherwise, mark it has seen and return false. This is primarily
|
||||
// intended to used by the parser to detect duplicate keys when
|
||||
// the reactor blocks them from being added to the final
|
||||
// dictionary.
|
||||
QPDF_DLL
|
||||
bool checkDictionaryKeySeen(std::string const& key);
|
||||
|
||||
// Accessors. Accessor behavior:
|
||||
//
|
||||
// - If argument is wrong type, including null, return false
|
||||
@ -314,6 +323,7 @@ class JSON
|
||||
virtual ~JSON_dictionary() = default;
|
||||
virtual void write(Pipeline*, size_t depth) const;
|
||||
std::map<std::string, std::shared_ptr<JSON_value>> members;
|
||||
std::set<std::string> parsed_keys;
|
||||
};
|
||||
struct JSON_array: public JSON_value
|
||||
{
|
||||
|
@ -274,6 +274,21 @@ JSON::addDictionaryMember(std::string const& key, JSON const& val)
|
||||
return obj->members[encode_string(key)];
|
||||
}
|
||||
|
||||
bool
|
||||
JSON::checkDictionaryKeySeen(std::string const& key)
|
||||
{
|
||||
JSON_dictionary* obj = dynamic_cast<JSON_dictionary*>(this->m->value.get());
|
||||
if (0 == obj) {
|
||||
throw std::logic_error(
|
||||
"JSON::checkDictionaryKey called on non-dictionary");
|
||||
}
|
||||
if (obj->parsed_keys.count(key)) {
|
||||
return true;
|
||||
}
|
||||
obj->parsed_keys.insert(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
JSON
|
||||
JSON::makeArray()
|
||||
{
|
||||
@ -565,7 +580,8 @@ namespace
|
||||
u_count(0),
|
||||
offset(0),
|
||||
done(false),
|
||||
parser_state(ps_top)
|
||||
parser_state(ps_top),
|
||||
dict_key_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -625,6 +641,7 @@ namespace
|
||||
std::vector<std::shared_ptr<JSON>> stack;
|
||||
std::vector<parser_state_e> ps_stack;
|
||||
std::string dict_key;
|
||||
size_t dict_key_offset;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -1201,11 +1218,18 @@ JSONParser::handleToken()
|
||||
case ps_dict_begin:
|
||||
case ps_dict_after_comma:
|
||||
this->dict_key = s_value;
|
||||
this->dict_key_offset = item->getStart();
|
||||
item = nullptr;
|
||||
next_state = ps_dict_after_key;
|
||||
break;
|
||||
|
||||
case ps_dict_after_colon:
|
||||
if (tos->checkDictionaryKeySeen(dict_key)) {
|
||||
QTC::TC("libtests", "JSON parse duplicate key");
|
||||
throw std::runtime_error(
|
||||
"JSON: offset " + QUtil::uint_to_string(dict_key_offset) +
|
||||
": duplicated dictionary key");
|
||||
}
|
||||
if (!reactor || !reactor->dictionaryItem(dict_key, *item)) {
|
||||
tos->addDictionaryMember(dict_key, *item);
|
||||
}
|
||||
|
@ -92,3 +92,4 @@ JSON optional key 0
|
||||
JSON 16 high high 0
|
||||
JSON 16 low not after high 0
|
||||
JSON 16 dangling high 0
|
||||
JSON parse duplicate key 0
|
||||
|
@ -120,6 +120,7 @@ my @bad = (
|
||||
"stray low surrogate", # 37
|
||||
"high high surrogate", # 38
|
||||
"dangling high surrogate", # 39
|
||||
"duplicate dictionary key", # 40
|
||||
);
|
||||
|
||||
my $i = 0;
|
||||
|
6
libtests/qtest/json_parse/bad-40.json
Normal file
6
libtests/qtest/json_parse/bad-40.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"one": 1,
|
||||
"two": 2,
|
||||
"one": 3,
|
||||
"four": 4
|
||||
}
|
1
libtests/qtest/json_parse/bad-40.out
Normal file
1
libtests/qtest/json_parse/bad-40.out
Normal file
@ -0,0 +1 @@
|
||||
exception: bad-40.json: JSON: offset 28: duplicated dictionary key
|
Loading…
x
Reference in New Issue
Block a user