blob: bdfc9fa3eaffe383ff9cd397df27a37761d07144 [file] [log] [blame]
// Copyright 2020 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/js_injection/common/origin_matcher.h"
#include "components/js_injection/common/origin_matcher_internal.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/base/parse_number.h"
#include "net/base/url_util.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_constants.h"
#include "url/url_util.h"
namespace js_injection {
namespace {
inline int GetDefaultPortForSchemeIfNoPortInfo(const std::string& scheme,
int port) {
// The input has explicit port information, so don't modify it.
if (port != -1)
return port;
// Hard code the port for http and https.
if (scheme == url::kHttpScheme)
return 80;
if (scheme == url::kHttpsScheme)
return 443;
return port;
}
} // namespace
OriginMatcher::OriginMatcher(const OriginMatcher& rhs) {
*this = rhs;
}
OriginMatcher& OriginMatcher::operator=(const OriginMatcher& rhs) {
rules_.clear();
for (const auto& rule : rhs.Serialize())
AddRuleFromString(rule);
return *this;
}
void OriginMatcher::SetRules(RuleList rules) {
rules_.swap(rules);
}
bool OriginMatcher::AddRuleFromString(const std::string& raw_untrimmed) {
std::string raw;
base::TrimWhitespaceASCII(raw_untrimmed, base::TRIM_ALL, &raw);
if (raw == "*") {
rules_.push_back(std::make_unique<MatchAllOriginsRule>());
return true;
}
// Extract scheme-restriction.
std::string::size_type scheme_pos = raw.find("://");
if (scheme_pos == std::string::npos)
return false;
const std::string scheme = raw.substr(0, scheme_pos);
if (!SubdomainMatchingRule::IsValidScheme(scheme))
return false;
std::string host_and_port = raw.substr(scheme_pos + 3);
if (host_and_port.empty()) {
if (!SubdomainMatchingRule::IsValidSchemeAndHost(scheme, std::string()))
return false;
rules_.push_back(
std::make_unique<SubdomainMatchingRule>(scheme, std::string(), -1));
return true;
}
std::string host;
int port;
if (!net::ParseHostAndPort(host_and_port, &host, &port) ||
!SubdomainMatchingRule::IsValidSchemeAndHost(scheme, host)) {
return false;
}
// Check if we have an <ip-address>[:port] input and try to canonicalize the
// IP literal.
net::IPAddress ip_address;
if (ip_address.AssignFromIPLiteral(host)) {
port = GetDefaultPortForSchemeIfNoPortInfo(scheme, port);
host = ip_address.ToString();
if (ip_address.IsIPv6())
host = '[' + host + ']';
rules_.push_back(
std::make_unique<SubdomainMatchingRule>(scheme, host, port));
return true;
}
port = GetDefaultPortForSchemeIfNoPortInfo(scheme, port);
rules_.push_back(std::make_unique<SubdomainMatchingRule>(scheme, host, port));
return true;
}
bool OriginMatcher::Matches(const url::Origin& origin) const {
GURL origin_url = origin.GetURL();
// Since we only do kInclude vs kNoMatch, the order doesn't actually matter.
for (auto it = rules_.rbegin(); it != rules_.rend(); ++it) {
net::SchemeHostPortMatcherResult result = (*it)->Evaluate(origin_url);
if (result == net::SchemeHostPortMatcherResult::kInclude)
return true;
}
return false;
}
std::vector<std::string> OriginMatcher::Serialize() const {
std::vector<std::string> result;
result.reserve(rules_.size());
for (const auto& rule : rules_) {
result.push_back(rule->ToString());
}
return result;
}
} // namespace js_injection