Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/brpc/couchbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ constexpr uint32_t APPLE_VBUCKET_COUNT ALLOW_UNUSED = 64;
constexpr uint32_t DEFAULT_VBUCKET_COUNT ALLOW_UNUSED = 1024;
constexpr int CONNECTION_ID_SIZE = 33;
constexpr size_t RANDOM_ID_HEX_SIZE = 67; // 33 bytes * 2 + null terminator

// Bound nesting before building the DOM, whose destruction is recursive.
struct ManifestDepthHandler
: BUTIL_RAPIDJSON_NAMESPACE::BaseReaderHandler<
BUTIL_RAPIDJSON_NAMESPACE::UTF8<>, ManifestDepthHandler> {
unsigned depth = 0;
bool StartObject() { return ++depth <= 100; }
bool StartArray() { return ++depth <= 100; }
bool EndObject(BUTIL_RAPIDJSON_NAMESPACE::SizeType) { --depth; return true; }
bool EndArray(BUTIL_RAPIDJSON_NAMESPACE::SizeType) { --depth; return true; }
};
} // namespace

// Static member definitions
Expand Down Expand Up @@ -124,9 +135,16 @@ bool CouchbaseManifestManager::jsonToCollectionManifest(
return false;
}

// Parse JSON using RapidJSON
BUTIL_RAPIDJSON_NAMESPACE::Reader reader;
BUTIL_RAPIDJSON_NAMESPACE::StringStream stream(json.c_str());
ManifestDepthHandler handler;
if (!reader.Parse<BUTIL_RAPIDJSON_NAMESPACE::kParseIterativeFlag>(stream, handler)
|| stream.Tell() != json.size()) {
return false;
}

BUTIL_RAPIDJSON_NAMESPACE::Document document;
document.Parse(json.c_str());
document.Parse<BUTIL_RAPIDJSON_NAMESPACE::kParseIterativeFlag>(json.c_str());

if (document.HasParseError()) {
DEBUG_PRINT("Failed to parse JSON: " << document.GetParseError());
Expand Down
18 changes: 18 additions & 0 deletions test/brpc_couchbase_unittest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,24 @@ namespace {
// Unit Tests - No Server Required
class CouchbaseUnitTest : public testing::Test {};

TEST_F(CouchbaseUnitTest, ManifestJsonNesting) {
brpc::CouchbaseManifestManager manager;
brpc::CouchbaseManifestManager::CollectionManifest manifest;
const std::string prefix = "{\"uid\":\"1\",\"scopes\":[],\"extra\":";
for (size_t depth : {size_t(99), size_t(100), size_t(20000)}) {
const std::string json = prefix + std::string(depth, '[') + "0"
+ std::string(depth, ']') + "}";
EXPECT_EQ(depth < 100, manager.jsonToCollectionManifest(json, &manifest))
<< "depth=" << depth;
}
EXPECT_TRUE(manager.jsonToCollectionManifest(
"{\"uid\":\"1\",\"scopes\":[],\"extra\":\"[\\\"{]\"}", &manifest));
std::string with_nul = "{\"uid\":\"1\",\"scopes\":[]}";
with_nul.push_back('\0');
with_nul += "[]";
EXPECT_FALSE(manager.jsonToCollectionManifest(with_nul, &manifest));
}

TEST_F(CouchbaseUnitTest, RejectOversizedResponseBeforeBufferingBody) {
GFLAGS_NAMESPACE::FlagSaver flag_saver;
brpc::FLAGS_max_body_size = 1024;
Expand Down
Loading