// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/bind.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/values.h" #include "components/safe_json/json_sanitizer.h" #include "components/safe_json/safe_json_parser.h" #include "testing/gtest/include/gtest/gtest.h" #if !defined(OS_ANDROID) #include "components/safe_json/testing_json_parser.h" #endif namespace safe_json { class JsonSanitizerTest : public ::testing::Test { public: void TearDown() override { // Flush any tasks from the message loop to avoid leaks. base::RunLoop().RunUntilIdle(); } protected: void CheckSuccess(const std::string& json); void CheckError(const std::string& json); private: enum class State { // ERROR is a #define on Windows, so we prefix the values with STATE_. STATE_IDLE, STATE_SUCCESS, STATE_ERROR, }; void Sanitize(const std::string& json); void OnSuccess(const std::string& json); void OnError(const std::string& error); base::MessageLoop message_loop_; #if !defined(OS_ANDROID) safe_json::TestingJsonParser::ScopedFactoryOverride factory_override_; #endif std::string result_; std::string error_; State state_; scoped_ptr run_loop_; }; void JsonSanitizerTest::CheckSuccess(const std::string& json) { SCOPED_TRACE(json); Sanitize(json); scoped_ptr parsed = base::JSONReader::Read(json); ASSERT_TRUE(parsed); EXPECT_EQ(State::STATE_SUCCESS, state_) << "Error: " << error_; // The JSON parser should accept the result. int error_code; std::string error; scoped_ptr reparsed = base::JSONReader::ReadAndReturnError( result_, base::JSON_PARSE_RFC, &error_code, &error); EXPECT_TRUE(reparsed) << "Invalid result: " << error; // The parsed values should be equal. EXPECT_TRUE(reparsed->Equals(parsed.get())); } void JsonSanitizerTest::CheckError(const std::string& json) { SCOPED_TRACE(json); Sanitize(json); EXPECT_EQ(State::STATE_ERROR, state_) << "Result: " << result_; } void JsonSanitizerTest::Sanitize(const std::string& json) { state_ = State::STATE_IDLE; result_.clear(); error_.clear(); run_loop_.reset(new base::RunLoop); JsonSanitizer::Sanitize( json, base::Bind(&JsonSanitizerTest::OnSuccess, base::Unretained(this)), base::Bind(&JsonSanitizerTest::OnError, base::Unretained(this))); // We should never get a result immediately. EXPECT_EQ(State::STATE_IDLE, state_); run_loop_->Run(); } void JsonSanitizerTest::OnSuccess(const std::string& json) { ASSERT_EQ(State::STATE_IDLE, state_); state_ = State::STATE_SUCCESS; result_ = json; run_loop_->Quit(); } void JsonSanitizerTest::OnError(const std::string& error) { ASSERT_EQ(State::STATE_IDLE, state_); state_ = State::STATE_ERROR; error_ = error; run_loop_->Quit(); } TEST_F(JsonSanitizerTest, Json) { // Valid JSON: CheckSuccess("{\n \"foo\": \"bar\"\n}"); CheckSuccess("[true]"); CheckSuccess("[42]"); CheckSuccess("[3.14]"); CheckSuccess("[4.0]"); CheckSuccess("[null]"); CheckSuccess("[\"foo\", \"bar\"]"); // JSON syntax errors: CheckError(""); CheckError("["); CheckError("null"); // Unterminated array. CheckError("[1,2,3,]"); } TEST_F(JsonSanitizerTest, Nesting) { // 99 nested arrays are fine. std::string nested(99u, '['); nested.append(99u, ']'); CheckSuccess(nested); // 100 nested arrays is too much. CheckError(std::string(100u, '[') + std::string(100u, ']')); } TEST_F(JsonSanitizerTest, Unicode) { // Non-ASCII characters encoded either directly as UTF-8 or escaped as UTF-16: CheckSuccess("[\"☃\"]"); CheckSuccess("[\"\\u2603\"]"); CheckSuccess("[\"😃\"]"); CheckSuccess("[\"\\ud83d\\ude03\"]"); // Malformed UTF-8: // A continuation byte outside of a sequence. CheckError("[\"\x80\"]"); // A start byte that is missing a continuation byte. CheckError("[\"\xc0\"]"); // An invalid byte in UTF-8. CheckError("[\"\xfe\"]"); // An overlong encoding (of the letter 'A'). CheckError("[\"\xc1\x81\"]"); // U+D83D, a code point reserved for (high) surrogates. CheckError("[\"\xed\xa0\xbd\"]"); // U+4567890, a code point outside of the valid range for Unicode. CheckError("[\"\xfc\x84\x95\xa7\xa2\x90\"]"); // Malformed escaped UTF-16: // An unmatched high surrogate. CheckError("[\"\\ud83d\"]"); // An unmatched low surrogate. CheckError("[\"\\ude03\"]"); // A low surrogate followed by a high surrogate. CheckError("[\"\\ude03\\ud83d\"]"); // Valid escaped UTF-16 that encodes non-characters: CheckError("[\"\\ufdd0\"]"); CheckError("[\"\\ufffe\"]"); CheckError("[\"\\ud83f\\udffe\"]"); } } // namespace safe_json