Skip to content

Commit 9c90456

Browse files
committed
Merge pull request #167 from cdunn2001/fail-if-extra
Add `failIfExtra` feature to `CharReaderBuilder`.
2 parents aa13a8b + f4be815 commit 9c90456

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

Diff for: include/json/reader.h

+3
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,9 @@ class JSON_API CharReaderBuilder : public CharReader::Factory {
315315
cause an exception.
316316
- This is a security issue (seg-faults caused by deeply nested JSON),
317317
so the default is low.
318+
- `"failIfExtra": false or true`
319+
- If true, `parse()` returns false when extra non-whitespace trails
320+
the JSON value in the input string.
318321
319322
You can examine 'settings_` yourself
320323
to see the defaults. You can also write and read them just like any

Diff for: src/lib_json/json_reader.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,7 @@ class OurFeatures {
914914
bool strictRoot_;
915915
bool allowDroppedNullPlaceholders_;
916916
bool allowNumericKeys_;
917+
bool failIfExtra_;
917918
int stackLimit_;
918919
}; // OurFeatures
919920

@@ -1083,6 +1084,12 @@ bool OurReader::parse(const char* beginDoc,
10831084
bool successful = readValue();
10841085
Token token;
10851086
skipCommentTokens(token);
1087+
if (features_.failIfExtra_) {
1088+
if (token.type_ != tokenError && token.type_ != tokenEndOfStream) {
1089+
addError("Extra non-whitespace after JSON value.", token);
1090+
return false;
1091+
}
1092+
}
10861093
if (collectComments_ && !commentsBefore_.empty())
10871094
root.setComment(commentsBefore_, commentAfter);
10881095
if (features_.strictRoot_) {
@@ -1870,6 +1877,7 @@ CharReader* CharReaderBuilder::newCharReader() const
18701877
features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool();
18711878
features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
18721879
features.stackLimit_ = settings_["stackLimit"].asInt();
1880+
features.failIfExtra_ = settings_["failIfExtra"].asBool();
18731881
return new OurCharReader(collectComments, features);
18741882
}
18751883
static void getValidReaderKeys(std::set<std::string>* valid_keys)
@@ -1881,6 +1889,7 @@ static void getValidReaderKeys(std::set<std::string>* valid_keys)
18811889
valid_keys->insert("allowDroppedNullPlaceholders");
18821890
valid_keys->insert("allowNumericKeys");
18831891
valid_keys->insert("stackLimit");
1892+
valid_keys->insert("failIfExtra");
18841893
}
18851894
bool CharReaderBuilder::validate(Json::Value* invalid) const
18861895
{
@@ -1908,6 +1917,7 @@ void CharReaderBuilder::strictMode(Json::Value* settings)
19081917
(*settings)["strictRoot"] = true;
19091918
(*settings)["allowDroppedNullPlaceholders"] = false;
19101919
(*settings)["allowNumericKeys"] = false;
1920+
(*settings)["failIfExtra"] = true;
19111921
//! [CharReaderBuilderStrictMode]
19121922
}
19131923
// static
@@ -1920,6 +1930,7 @@ void CharReaderBuilder::setDefaults(Json::Value* settings)
19201930
(*settings)["allowDroppedNullPlaceholders"] = false;
19211931
(*settings)["allowNumericKeys"] = false;
19221932
(*settings)["stackLimit"] = 1000;
1933+
(*settings)["failIfExtra"] = false;
19231934
//! [CharReaderBuilderDefaults]
19241935
}
19251936

Diff for: src/test_lib_json/main.cpp

+126
Original file line numberDiff line numberDiff line change
@@ -1741,6 +1741,126 @@ JSONTEST_FIXTURE(CharReaderTest, parseWithStackLimit) {
17411741
}
17421742
}
17431743

1744+
struct CharReaderFailIfExtraTest : JsonTest::TestCase {};
1745+
1746+
JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue164) {
1747+
// This is interpretted as a string value followed by a colon.
1748+
Json::CharReaderBuilder b;
1749+
Json::Value root;
1750+
char const doc[] =
1751+
" \"property\" : \"value\" }";
1752+
{
1753+
b.settings_["failIfExtra"] = false;
1754+
Json::CharReader* reader(b.newCharReader());
1755+
std::string errs;
1756+
bool ok = reader->parse(
1757+
doc, doc + std::strlen(doc),
1758+
&root, &errs);
1759+
JSONTEST_ASSERT(ok);
1760+
JSONTEST_ASSERT(errs == "");
1761+
JSONTEST_ASSERT_EQUAL("property", root);
1762+
delete reader;
1763+
}
1764+
{
1765+
b.settings_["failIfExtra"] = true;
1766+
Json::CharReader* reader(b.newCharReader());
1767+
std::string errs;
1768+
bool ok = reader->parse(
1769+
doc, doc + std::strlen(doc),
1770+
&root, &errs);
1771+
JSONTEST_ASSERT(!ok);
1772+
JSONTEST_ASSERT_STRING_EQUAL(errs,
1773+
"* Line 1, Column 13\n"
1774+
" Extra non-whitespace after JSON value.\n");
1775+
JSONTEST_ASSERT_EQUAL("property", root);
1776+
delete reader;
1777+
}
1778+
{
1779+
b.settings_["failIfExtra"] = false;
1780+
b.strictMode(&b.settings_);
1781+
Json::CharReader* reader(b.newCharReader());
1782+
std::string errs;
1783+
bool ok = reader->parse(
1784+
doc, doc + std::strlen(doc),
1785+
&root, &errs);
1786+
JSONTEST_ASSERT(!ok);
1787+
JSONTEST_ASSERT_STRING_EQUAL(errs,
1788+
"* Line 1, Column 13\n"
1789+
" Extra non-whitespace after JSON value.\n");
1790+
JSONTEST_ASSERT_EQUAL("property", root);
1791+
delete reader;
1792+
}
1793+
}
1794+
JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue107) {
1795+
// This is interpretted as an int value followed by a colon.
1796+
Json::CharReaderBuilder b;
1797+
Json::Value root;
1798+
char const doc[] =
1799+
"1:2:3";
1800+
b.settings_["failIfExtra"] = true;
1801+
Json::CharReader* reader(b.newCharReader());
1802+
std::string errs;
1803+
bool ok = reader->parse(
1804+
doc, doc + std::strlen(doc),
1805+
&root, &errs);
1806+
JSONTEST_ASSERT(!ok);
1807+
JSONTEST_ASSERT_STRING_EQUAL(
1808+
"* Line 1, Column 2\n"
1809+
" Extra non-whitespace after JSON value.\n",
1810+
errs);
1811+
JSONTEST_ASSERT_EQUAL(1, root.asInt());
1812+
delete reader;
1813+
}
1814+
JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterObject) {
1815+
Json::CharReaderBuilder b;
1816+
Json::Value root;
1817+
{
1818+
char const doc[] =
1819+
"{ \"property\" : \"value\" } //trailing\n//comment\n";
1820+
b.settings_["failIfExtra"] = true;
1821+
Json::CharReader* reader(b.newCharReader());
1822+
std::string errs;
1823+
bool ok = reader->parse(
1824+
doc, doc + std::strlen(doc),
1825+
&root, &errs);
1826+
JSONTEST_ASSERT(ok);
1827+
JSONTEST_ASSERT_STRING_EQUAL("", errs);
1828+
JSONTEST_ASSERT_EQUAL("value", root["property"]);
1829+
delete reader;
1830+
}
1831+
}
1832+
JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterArray) {
1833+
Json::CharReaderBuilder b;
1834+
Json::Value root;
1835+
char const doc[] =
1836+
"[ \"property\" , \"value\" ] //trailing\n//comment\n";
1837+
b.settings_["failIfExtra"] = true;
1838+
Json::CharReader* reader(b.newCharReader());
1839+
std::string errs;
1840+
bool ok = reader->parse(
1841+
doc, doc + std::strlen(doc),
1842+
&root, &errs);
1843+
JSONTEST_ASSERT(ok);
1844+
JSONTEST_ASSERT_STRING_EQUAL("", errs);
1845+
JSONTEST_ASSERT_EQUAL("value", root[1u]);
1846+
delete reader;
1847+
}
1848+
JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterBool) {
1849+
Json::CharReaderBuilder b;
1850+
Json::Value root;
1851+
char const doc[] =
1852+
" true /*trailing\ncomment*/";
1853+
b.settings_["failIfExtra"] = true;
1854+
Json::CharReader* reader(b.newCharReader());
1855+
std::string errs;
1856+
bool ok = reader->parse(
1857+
doc, doc + std::strlen(doc),
1858+
&root, &errs);
1859+
JSONTEST_ASSERT(ok);
1860+
JSONTEST_ASSERT_STRING_EQUAL("", errs);
1861+
JSONTEST_ASSERT_EQUAL(true, root.asBool());
1862+
delete reader;
1863+
}
17441864
int main(int argc, const char* argv[]) {
17451865
JsonTest::Runner runner;
17461866
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr);
@@ -1779,6 +1899,12 @@ int main(int argc, const char* argv[]) {
17791899
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithDetailError);
17801900
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithStackLimit);
17811901

1902+
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, issue164);
1903+
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, issue107);
1904+
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterObject);
1905+
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterArray);
1906+
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterBool);
1907+
17821908
JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders);
17831909
JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, dropNullPlaceholders);
17841910

0 commit comments

Comments
 (0)