Commit message: None
CVE: CVE-2018-6076
CWE: CWE-79
Vulnerability description: Insufficient encoding of URL fragment identifiers in Blink in Google Chrome prior to 65.0.3325.146 allowed a remote attacker to perform a DOM based XSS attack via a crafted HTML page.

Project: Chrome

base::string16 Elide(const GURL& url,
                     const gfx::FontList& font_list,
                     float available_width,
                     ElisionMethod method) {
  switch (method) {
    case kMethodSimple: {
      url::Parsed parsed;
      return url_formatter::ElideUrlSimple(url, font_list, available_width,
                                           &parsed);
    }
#if !defined(OS_ANDROID)
    case kMethodOriginal:
      return url_formatter::ElideUrl(url, font_list, available_width);
#endif
    default:
      NOTREACHED();
      return base::string16();
  }
}

Project: Chrome

url::Component* GetComponent(url::Parsed* parsed,
                             url::Parsed::ComponentType type) {
  switch (type) {
    case url::Parsed::SCHEME:
      return &parsed->scheme;
    case url::Parsed::USERNAME:
      return &parsed->username;
    case url::Parsed::PASSWORD:
      return &parsed->password;
    case url::Parsed::HOST:
      return &parsed->host;
    case url::Parsed::PORT:
      return &parsed->port;
    case url::Parsed::PATH:
      return &parsed->path;
    case url::Parsed::QUERY:
      return &parsed->query;
    case url::Parsed::REF:
      return &parsed->ref;
    default:
      NOTREACHED();
      return nullptr;
  }
}

Project: Chrome

void RunElisionParsingTest(const std::vector<ParsingTestcase>& testcases) {
  const gfx::FontList font_list;
  for (const auto& testcase : testcases) {
    SCOPED_TRACE(testcase.input + " to " + testcase.output);

    const GURL url(testcase.input);
    const float available_width =
        gfx::GetStringWidthF(base::UTF8ToUTF16(testcase.output), font_list);

    url::Parsed parsed;
    auto elided =
        url_formatter::ElideUrlSimple(url, font_list, available_width, &parsed);
    EXPECT_EQ(base::UTF8ToUTF16(testcase.output), elided);

    url::Parsed expected;
    for (const auto& expectation : testcase.components) {
      url::Component* component = GetComponent(&expected, expectation.type);
      component->begin = expectation.begin;
      component->len = expectation.len;
    }

    const std::vector<url::Parsed::ComponentType> kComponents = {
        url::Parsed::SCHEME, url::Parsed::USERNAME, url::Parsed::PASSWORD,
        url::Parsed::HOST,   url::Parsed::PORT,     url::Parsed::PATH,
        url::Parsed::QUERY,  url::Parsed::REF,
    };
    for (const auto& type : kComponents) {
      EXPECT_EQ(GetComponent(&expected, type)->begin,
                GetComponent(&parsed, type)->begin)
          << " in component " << type;
      EXPECT_EQ(GetComponent(&expected, type)->len,
                GetComponent(&parsed, type)->len)
          << " in component " << type;
    }
  }
}

Project: Chrome

void RunProgressiveElisionTest(
    const std::vector<ProgressiveTestcase>& testcases,
    ElisionMethod method) {
  const gfx::FontList font_list;
  for (const auto& testcase : testcases) {
    SCOPED_TRACE("Eliding " + testcase.input);
    const GURL url(testcase.input);

    ASSERT_FALSE(testcase.output.empty());
    float width = std::max(
        gfx::GetStringWidthF(base::UTF8ToUTF16(testcase.input), font_list),
        gfx::GetStringWidthF(base::UTF8ToUTF16(testcase.output.front()),
                             font_list));

    int mismatches = 0;
    const int kMaxConsecutiveMismatches = 3;
    for (size_t i = 0; i < testcase.output.size(); i++) {
      const auto& expected = testcase.output[i];
      base::string16 expected_utf16 = base::UTF8ToUTF16(expected);
      base::string16 elided = Elide(url, font_list, width, method);
      if (expected_utf16 != elided) {
        if (i > 0 && i < testcase.output.size() - 1 &&
            mismatches < kMaxConsecutiveMismatches) {
          mismatches++;
          continue;
        }
        EXPECT_EQ(expected_utf16, elided);
        break;
      }
      mismatches = 0;
      float new_width = gfx::GetStringWidthF(elided, font_list);
      EXPECT_LE(new_width, std::ceil(width)) << " at " << elided;
      width = new_width - 1.0f;
    }
  }
}

Project: Chrome

bool IsQueryMatch(
    const std::string& url_query,
    const std::string& key,
    URLQueryElementMatcherCondition::QueryElementType query_element_type,
    const std::string& value,
    URLQueryElementMatcherCondition::QueryValueMatchType query_value_match_type,
    URLQueryElementMatcherCondition::Type match_type) {
  URLMatcherConditionFactory factory;

  URLMatcherCondition m1 = factory.CreateHostSuffixCondition("example.com");
  URLMatcherCondition m2 = factory.CreatePathContainsCondition("foo");
  URLMatcherConditionSet::Conditions conditions;
  conditions.insert(m1);
  conditions.insert(m2);

  URLQueryElementMatcherCondition q1(key,
                                     value,
                                     query_value_match_type,
                                     query_element_type,
                                     match_type,
                                     &factory);
  URLMatcherConditionSet::QueryConditions query_conditions;
  query_conditions.insert(q1);

  std::unique_ptr<URLMatcherSchemeFilter> scheme_filter;
  std::unique_ptr<URLMatcherPortFilter> port_filter;

  scoped_refptr<URLMatcherConditionSet> condition_set(
      new URLMatcherConditionSet(1, conditions, query_conditions,
                                 std::move(scheme_filter),
                                 std::move(port_filter)));

  GURL url("http://www.example.com/foo?" + url_query);

  URLMatcher matcher;
  URLMatcherConditionSet::Vector vector;
  vector.push_back(condition_set);
  matcher.AddConditionSets(vector);

  return matcher.MatchURL(url).size() == 1;
}

Project: Chrome

bool Matches(const URLMatcherCondition& condition, const std::string& text) {
  return text.find(condition.string_pattern()->pattern()) !=
      std::string::npos;
}

Project: Chrome

char CanonicalSchemeChar(base::char16 ch) {
  if (ch >= 0x80)
    return 0;  // Non-ASCII is not supported by schemes.
  return kSchemeCanonical[ch];
}

Project: Chrome

bool CanonicalizePort(const char* spec,
                      const Component& port,
                      int default_port_for_scheme,
                      CanonOutput* output,
                      Component* out_port) {
  return DoPort<char, unsigned char>(spec, port,
                                     default_port_for_scheme,
                                     output, out_port);
}

Project: Chrome

bool CanonicalizePort(const base::char16* spec,
                      const Component& port,
                      int default_port_for_scheme,
                      CanonOutput* output,
                      Component* out_port) {
  return DoPort<base::char16, base::char16>(spec, port, default_port_for_scheme,
                                            output, out_port);
}

Project: Chrome

void CanonicalizeRef(const char* spec,
                     const Component& ref,
                     CanonOutput* output,
                     Component* out_ref) {
  DoCanonicalizeRef<char, unsigned char>(spec, ref, output, out_ref);
}

Project: Chrome

void CanonicalizeRef(const base::char16* spec,
                     const Component& ref,
                     CanonOutput* output,
                     Component* out_ref) {
  DoCanonicalizeRef<base::char16, base::char16>(spec, ref, output, out_ref);
}

Project: Chrome

bool CanonicalizeScheme(const char* spec,
                        const Component& scheme,
                        CanonOutput* output,
                        Component* out_scheme) {
  return DoScheme<char, unsigned char>(spec, scheme, output, out_scheme);
}

Project: Chrome

bool CanonicalizeScheme(const base::char16* spec,
                        const Component& scheme,
                        CanonOutput* output,
                        Component* out_scheme) {
  return DoScheme<base::char16, base::char16>(spec, scheme, output, out_scheme);
}

Project: Chrome

bool CanonicalizeUserInfo(const char* username_source,
                          const Component& username,
                          const char* password_source,
                          const Component& password,
                          CanonOutput* output,
                          Component* out_username,
                          Component* out_password) {
  return DoUserInfo<char, unsigned char>(
      username_source, username, password_source, password,
      output, out_username, out_password);
}

Project: Chrome

bool CanonicalizeUserInfo(const base::char16* username_source,
                          const Component& username,
                          const base::char16* password_source,
                          const Component& password,
                          CanonOutput* output,
                          Component* out_username,
                          Component* out_password) {
  return DoUserInfo<base::char16, base::char16>(
      username_source, username, password_source, password,
      output, out_username, out_password);
}

Project: Chrome

bool DoPort(const CHAR* spec,
            const Component& port,
            int default_port_for_scheme,
            CanonOutput* output,
            Component* out_port) {
  int port_num = ParsePort(spec, port);
  if (port_num == PORT_UNSPECIFIED || port_num == default_port_for_scheme) {
    *out_port = Component();
    return true;  // Leave port empty.
  }

  if (port_num == PORT_INVALID) {
    output->push_back(':');
    out_port->begin = output->length();
    AppendInvalidNarrowString(spec, port.begin, port.end(), output);
    out_port->len = output->length() - out_port->begin;
    return false;
  }

  const int buf_size = 6;
  char buf[buf_size];
  WritePortInt(buf, buf_size, port_num);

  output->push_back(':');
  out_port->begin = output->length();
  for (int i = 0; i < buf_size && buf[i]; i++)
    output->push_back(buf[i]);

  out_port->len = output->length() - out_port->begin;
  return true;
}

Project: Chrome

bool DoScheme(const CHAR* spec,
              const Component& scheme,
              CanonOutput* output,
              Component* out_scheme) {
  if (scheme.len <= 0) {
    *out_scheme = Component(output->length(), 0);
    output->push_back(':');
    return false;
  }

  out_scheme->begin = output->length();

  bool success = true;
  int end = scheme.end();
  for (int i = scheme.begin; i < end; i++) {
    UCHAR ch = static_cast<UCHAR>(spec[i]);
    char replacement = 0;
    if (ch < 0x80) {
      if (i == scheme.begin) {
        if (IsSchemeFirstChar(static_cast<unsigned char>(ch)))
          replacement = kSchemeCanonical[ch];
      } else {
        replacement = kSchemeCanonical[ch];
      }
    }

    if (replacement) {
      output->push_back(replacement);
    } else if (ch == '%') {
      success = false;
      output->push_back('%');
    } else {
      success = false;

      AppendUTF8EscapedChar(spec, &i, end, output);
    }
  }

  out_scheme->len = output->length() - out_scheme->begin;
  output->push_back(':');
  return success;
}

Project: Chrome

inline bool IsRemovableURLWhitespace(int ch) {
  return ch == '\r' || ch == '\n' || ch == '\t';
}

Project: Chrome

inline bool IsSchemeFirstChar(unsigned char c) {
  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

Project: Chrome

const char* RemoveURLWhitespace(const char* input,
                                int input_len,
                                CanonOutputT<char>* buffer,
                                int* output_len,
                                bool* potentially_dangling_markup) {
  return DoRemoveURLWhitespace(input, input_len, buffer, output_len,
                               potentially_dangling_markup);
}

Project: Chrome

const base::char16* RemoveURLWhitespace(const base::char16* input,
                                        int input_len,
                                        CanonOutputT<base::char16>* buffer,
                                        int* output_len,
                                        bool* potentially_dangling_markup) {
  return DoRemoveURLWhitespace(input, input_len, buffer, output_len,
                               potentially_dangling_markup);
}

Project: Chrome

inline void WritePortInt(char* output, int output_len, int port) {
  _itoa_s(port, output, output_len, 10);
}

Project: Chrome

std::string BytesToHexString(unsigned char bytes[16], int length) {
  EXPECT_TRUE(length == 0 || length == 4 || length == 16)
      << "Bad IP address length: " << length;
  std::string result;
  for (int i = 0; i < length; ++i) {
    result.push_back(kHexCharLookup[(bytes[i] >> 4) & 0xf]);
    result.push_back(kHexCharLookup[bytes[i] & 0xf]);
  }
  return result;
}

Project: Chrome

static bool ParsedIsEqual(const Parsed& a, const Parsed& b) {
  return a.scheme.begin == b.scheme.begin && a.scheme.len == b.scheme.len &&
         a.username.begin == b.username.begin && a.username.len == b.username.len &&
         a.password.begin == b.password.begin && a.password.len == b.password.len &&
         a.host.begin == b.host.begin && a.host.len == b.host.len &&
         a.port.begin == b.port.begin && a.port.len == b.port.len &&
         a.path.begin == b.path.begin && a.path.len == b.path.len &&
         a.query.begin == b.query.begin && a.query.len == b.query.len &&
         a.ref.begin == b.ref.begin && a.ref.len == b.ref.len;
}