blob: 0a75ad35380e1bb63c8402acf8251808e07eabef [file] [log] [blame]
// 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<base::RunLoop> run_loop_;
};
void JsonSanitizerTest::CheckSuccess(const std::string& json) {
SCOPED_TRACE(json);
Sanitize(json);
scoped_ptr<base::Value> 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<base::Value> 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