blob: 808af47fa30148e07366dae265686fb44e50c792 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/renderer/bindings/api_response_validator.h"
#include <algorithm>
#include <ostream>
#include "base/containers/contains.h"
#include "extensions/renderer/bindings/api_binding_util.h"
#include "extensions/renderer/bindings/api_signature.h"
#include "extensions/renderer/bindings/api_type_reference_map.h"
namespace extensions {
namespace {
APIResponseValidator::TestHandler* g_handler_for_testing = nullptr;
} // namespace
APIResponseValidator::TestHandler::TestHandler(HandlerMethod method)
: method_(method) {
DCHECK(!g_handler_for_testing)
<< "Only one TestHandler is allowed at a time.";
g_handler_for_testing = this;
}
APIResponseValidator::TestHandler::~TestHandler() {
DCHECK_EQ(this, g_handler_for_testing);
g_handler_for_testing = nullptr;
}
void APIResponseValidator::TestHandler::IgnoreSignature(std::string signature) {
signatures_to_ignore_.insert(std::move(signature));
}
void APIResponseValidator::TestHandler::HandleFailure(
const std::string& signature_name,
const std::string& error) {
method_.Run(signature_name, error);
}
bool APIResponseValidator::TestHandler::ShouldIgnoreSignature(
const std::string& signature_name) const {
return base::Contains(signatures_to_ignore_, signature_name);
}
APIResponseValidator::APIResponseValidator(const APITypeReferenceMap* type_refs)
: type_refs_(type_refs) {}
APIResponseValidator::~APIResponseValidator() = default;
void APIResponseValidator::ValidateResponse(
v8::Local<v8::Context> context,
const std::string& method_name,
const v8::LocalVector<v8::Value>& response_arguments,
const std::string& api_error,
CallbackType callback_type) {
DCHECK(binding::IsResponseValidationEnabled());
// If the callback is API-provided, the response can't be validated against
// the expected schema because the callback may modify the arguments.
if (callback_type == CallbackType::kAPIProvided)
return;
// If the call failed, there are no expected arguments.
if (!api_error.empty()) {
// TODO(devlin): It would be really nice to validate that
// |response_arguments| is empty here, but some functions both set an error
// and supply arguments.
return;
}
const APISignature* signature =
type_refs_->GetAsyncResponseSignature(method_name);
// If there's no corresponding signature, don't validate. This can
// legitimately happen with APIs that create custom requests.
if (!signature || !signature->has_async_return_signature())
return;
std::string error;
if (signature->ValidateResponse(context, response_arguments, *type_refs_,
&error)) {
// Response was valid.
return;
}
// The response did not match the expected schema.
if (g_handler_for_testing) {
g_handler_for_testing->HandleFailure(method_name, error);
} else {
NOTREACHED() << "Error validating response to `" << method_name
<< "`: " << error;
}
}
void APIResponseValidator::ValidateEvent(
v8::Local<v8::Context> context,
const std::string& event_name,
const v8::LocalVector<v8::Value>& event_args) {
DCHECK(binding::IsResponseValidationEnabled());
const APISignature* signature = type_refs_->GetEventSignature(event_name);
// If there's no corresponding signature, don't validate. This can
// legitimately happen with APIs that create custom requests.
if (!signature)
return;
if (g_handler_for_testing &&
g_handler_for_testing->ShouldIgnoreSignature(event_name))
return;
// The following signatures are incorrect (the parameters dispatched to the
// event don't match the schema's event definition). These should be fixed
// and then validated.
// TODO(crbug.com/40226845): Eliminate this list.
static constexpr char const* kBrokenSignaturesToIgnore[] = {
"automationInternal.onAccessibilityEvent",
"chromeWebViewInternal.onClicked",
"input.ime.onFocus",
"inputMethodPrivate.onFocus",
"test.onMessage",
// https://crbug.com/1343611.
"runtime.onMessage",
"runtime.onConnect",
"contextMenus.onClicked",
// https://crbug.com/1375903.
"downloads.onCreated",
};
if (std::ranges::find(kBrokenSignaturesToIgnore, event_name) !=
std::end(kBrokenSignaturesToIgnore)) {
return;
}
std::string error;
if (signature->ValidateCall(context, event_args, *type_refs_, &error)) {
// Response was valid.
return;
}
// The response did not match the expected schema.
// Pull to helper method.
if (g_handler_for_testing) {
g_handler_for_testing->HandleFailure(event_name, error);
} else {
NOTREACHED() << "Error validating event arguments to `" << event_name
<< "`: " << error;
}
}
} // namespace extensions