// 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 "components/safe_json/json_sanitizer.h" #include "base/android/jni_string.h" #include "base/bind.h" #include "base/callback.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" #include "jni/JsonSanitizer_jni.h" namespace safe_json { namespace { // An implementation of JsonSanitizer that calls into Java. It deals with // malformed input (in particular malformed Unicode encodings) in the following // steps: // 1. The input string is checked for whether it is well-formed UTF-8. Malformed // UTF-8 is rejected. // 2. The UTF-8 string is converted in native code to a Java String, which is // encoded as UTF-16. // 2. The Java String is parsed as JSON in the memory-safe environment of the // Java VM and any string literals are unescaped. // 3. The string literals themselves are now untrusted, so they are checked in // Java for whether they are valid UTF-16. // 4. The parsed JSON with sanitized literals is encoded back into a Java // String and passed back to native code. // 5. The Java String is converted back to UTF-8 in native code. // This ensures that both invalid UTF-8 and invalid escaped UTF-16 will be // rejected. class JsonSanitizerAndroid : public JsonSanitizer { public: JsonSanitizerAndroid(const StringCallback& success_callback, const StringCallback& error_callback); ~JsonSanitizerAndroid() {} void Sanitize(const std::string& unsafe_json); void OnSuccess(const std::string& json); void OnError(const std::string& error); private: StringCallback success_callback_; StringCallback error_callback_; DISALLOW_COPY_AND_ASSIGN(JsonSanitizerAndroid); }; JsonSanitizerAndroid::JsonSanitizerAndroid( const StringCallback& success_callback, const StringCallback& error_callback) : success_callback_(success_callback), error_callback_(error_callback) {} void JsonSanitizerAndroid::Sanitize(const std::string& unsafe_json) { // The JSON parser only accepts wellformed UTF-8. if (!base::IsStringUTF8(unsafe_json)) { OnError("Unsupported encoding"); return; } JNIEnv* env = base::android::AttachCurrentThread(); base::android::ScopedJavaLocalRef unsafe_json_java = base::android::ConvertUTF8ToJavaString(env, unsafe_json); // This will synchronously call either OnSuccess() or OnError(). Java_JsonSanitizer_sanitize(env, reinterpret_cast(this), unsafe_json_java.obj()); } void JsonSanitizerAndroid::OnSuccess(const std::string& json) { base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(success_callback_, json)); } void JsonSanitizerAndroid::OnError(const std::string& error) { base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(error_callback_, error)); } } // namespace void OnSuccess(JNIEnv* env, jclass clazz, jlong jsanitizer, jstring json) { JsonSanitizerAndroid* sanitizer = reinterpret_cast(jsanitizer); sanitizer->OnSuccess(base::android::ConvertJavaStringToUTF8(env, json)); } void OnError(JNIEnv* env, jclass clazz, jlong jsanitizer, jstring error) { JsonSanitizerAndroid* sanitizer = reinterpret_cast(jsanitizer); sanitizer->OnError(base::android::ConvertJavaStringToUTF8(env, error)); } // static void JsonSanitizer::Sanitize(const std::string& unsafe_json, const StringCallback& success_callback, const StringCallback& error_callback) { // JsonSanitizerAndroid does all its work synchronously, but posts any // callbacks to the current message loop. This means it can be destroyed at // the end of this method. JsonSanitizerAndroid sanitizer(success_callback, error_callback); sanitizer.Sanitize(unsafe_json); } // static bool JsonSanitizer::Register(JNIEnv* env) { return RegisterNativesImpl(env); } } // namespace safe_json