blob: 1c80a75cb60bdb23b372f177969980c3ef99e368 [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h"
#include <stddef.h>
#include "base/check.h"
#include "base/json/json_file_value_serializer.h"
#include "base/strings/string_util.h"
#include "chrome/common/chrome_features.h"
namespace extensions {
NativeMessagingHostManifest::~NativeMessagingHostManifest() = default;
// static
bool NativeMessagingHostManifest::IsValidName(const std::string& name) {
if (name.empty()) {
return false;
}
for (size_t i = 0; i < name.size(); ++i) {
char c = name[i];
// Verify that only the following characters are used: [a-z0-9._].
if (!(base::IsAsciiLower(c) || base::IsAsciiDigit(c) || c == '.' ||
c == '_')) {
return false;
}
// Verify that dots are separated by other characters and that string
// doesn't begin or end with a dot.
if (c == '.' && (i == 0 || name[i - 1] == '.' || i == name.size() - 1)) {
return false;
}
}
return true;
}
// static
std::unique_ptr<NativeMessagingHostManifest> NativeMessagingHostManifest::Load(
const base::FilePath& file_path,
std::string* error_message) {
DCHECK(error_message);
JSONFileValueDeserializer deserializer(file_path);
std::unique_ptr<base::Value> parsed =
deserializer.Deserialize(nullptr, error_message);
if (!parsed) {
return nullptr;
}
if (!parsed->is_dict()) {
*error_message = "Invalid manifest file.";
return nullptr;
}
const base::Value::Dict& dict = parsed->GetDict();
std::unique_ptr<NativeMessagingHostManifest> result(
new NativeMessagingHostManifest());
if (!result->Parse(dict, error_message)) {
return nullptr;
}
return result;
}
NativeMessagingHostManifest::NativeMessagingHostManifest() = default;
bool NativeMessagingHostManifest::Parse(const base::Value::Dict& dict,
std::string* error_message) {
const std::string* name_str = dict.FindString("name");
if (!name_str || !IsValidName(*name_str)) {
*error_message = "Invalid value for name.";
return false;
}
name_ = *name_str;
const std::string* desc_str = dict.FindString("description");
if (!desc_str || desc_str->empty()) {
*error_message = "Invalid value for description.";
return false;
}
description_ = *desc_str;
const std::string* type = dict.FindString("type");
// stdio is the only host type that's currently supported.
if (!type || *type != "stdio") {
*error_message = "Invalid value for type.";
return false;
}
interface_ = HOST_INTERFACE_STDIO;
const std::string* path = dict.FindString("path");
// JSON parsed checks that all strings are valid UTF8.
if (!path || (path_ = base::FilePath::FromUTF8Unsafe(*path)).empty()) {
*error_message = "Invalid value for path.";
return false;
}
const base::Value::List* allowed_origins_list =
dict.FindList("allowed_origins");
if (!allowed_origins_list) {
*error_message =
"Invalid value for allowed_origins. Expected a list of strings.";
return false;
}
allowed_origins_.ClearPatterns();
for (const auto& entry : *allowed_origins_list) {
if (!entry.is_string()) {
*error_message = "allowed_origins must be list of strings.";
return false;
}
std::string pattern_string = entry.GetString();
URLPattern pattern(URLPattern::SCHEME_EXTENSION);
URLPattern::ParseResult result = pattern.Parse(pattern_string);
if (result != URLPattern::ParseResult::kSuccess) {
*error_message = "Failed to parse pattern \"" + pattern_string +
"\": " + URLPattern::GetParseResultString(result);
return false;
}
// Disallow patterns that are too broad. Set of allowed origins must be a
// fixed list of extensions.
if (pattern.match_all_urls() || pattern.match_subdomains()) {
*error_message = "Pattern \"" + pattern_string + "\" is not allowed.";
return false;
}
allowed_origins_.AddPattern(pattern);
}
if (base::FeatureList::IsEnabled(features::kOnConnectNative)) {
if (const base::Value* supports_native_initiated_connections =
dict.Find("supports_native_initiated_connections")) {
if (!supports_native_initiated_connections->is_bool()) {
*error_message =
"supports_native_initiated_connections must be a boolean.";
return false;
}
supports_native_initiated_connections_ =
supports_native_initiated_connections->GetBool();
}
}
return true;
}
} // namespace extensions