910e62b5创建于 1月15日历史提交
// 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 "base/strings/to_string.h"

// This file contains the ResponseAnalyzerTests (which test the response
// analyzer's behavior in several parameterized test scenarios) and at the end
// includes the CrossOriginReadBlockingTests, which are more typical unittests.

#include <initializer_list>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/test/task_environment.h"
#include "net/base/mime_sniffer.h"
#include "net/http/http_util.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_test_util.h"
#include "services/network/orb/orb_impl.h"
#include "services/network/orb/orb_mimetypes.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace network::orb {

namespace {

// CORB's verdict on a given scenario. kAllowBecauseOutOfData occurs when one of
// the sniffers still desires more data but the response has run out, or
// net::kMaxBytesToSniff has been reached.
enum class Verdict {
  kAllow,
  kBlock,
  kAllowBecauseOutOfData,
};

constexpr int kVerdictPacketForHeadersBasedVerdict = -1;
constexpr int kVerdictPacketForInconclusiveSniffing = 99999;

// This struct is used to describe each test case in this file.  It's passed as
// a test parameter to each TEST_P test.
struct TestScenario {
  // Attributes to make test failure messages useful.
  const char* description;
  int source_line;

  // Attributes of the HTTP Request.
  const char* target_url;
  const char* initiator_origin;

  // Attributes of the HTTP response.
  const char* response_headers;
  const char* response_content_type;
  MimeType canonical_mime_type;
  // |packets| specifies the response data which may arrive over the course of
  // several writes.
  std::initializer_list<const char*> packets;

  std::string data() const {
    std::string data;
    for (const char* packet : packets) {
      data += packet;
    }
    return data;
  }

  // Whether the resource should seem sensitive (either through the CORS
  // heuristic or the Cache heuristic). This is used for testing that CORB would
  // have protected the resource, were it requested cross-origin.
  bool resource_is_sensitive;

  // Expected result.
  Verdict verdict;
  // The packet number during which the verdict is decided.
  // kVerdictPacketForHeadersBasedVerdict means that the verdict can be decided
  // before the first packet's data is available. |packets.size()| means that
  // the verdict is decided during the end-of-stream call.
  int verdict_packet;
};

inline std::ostream& operator<<(std::ostream& out, const MimeType& value) {
  out << static_cast<int>(value);
  return out;
}

// Stream operator to let GetParam() print a useful result if any tests fail.
::std::ostream& operator<<(::std::ostream& os, const TestScenario& scenario) {
  std::string verdict;
  switch (scenario.verdict) {
    case Verdict::kAllow:
      verdict = "Verdict::kAllow";
      break;
    case Verdict::kBlock:
      verdict = "Verdict::kBlock";
      break;
    case Verdict::kAllowBecauseOutOfData:
      verdict = "Verdict::kAllowBecauseOutOfData";
      break;
  }

  std::string response_headers_formatted;
  base::ReplaceChars(scenario.response_headers, "\n",
                     "\n                          ",
                     &response_headers_formatted);

  std::string packets = "{";
  for (std::string packet : scenario.packets) {
    base::ReplaceChars(packet, "\\", "\\\\", &packet);
    base::ReplaceChars(packet, "\"", "\\\"", &packet);
    base::ReplaceChars(packet, "\n", "\\n", &packet);
    base::ReplaceChars(packet, "\t", "\\t", &packet);
    base::ReplaceChars(packet, "\r", "\\r", &packet);
    if (packets.length() > 1)
      packets += ", ";
    packets += "\"";
    packets += packet;
    packets += "\"";
  }
  packets += "}";

  return os << "\n  description           = " << scenario.description
            << "\n  source_line           = " << scenario.source_line
            << "\n  target_url            = " << scenario.target_url
            << "\n  initiator_origin      = " << scenario.initiator_origin
            << "\n  response_headers      = " << response_headers_formatted
            << "\n  response_content_type = " << scenario.response_content_type
            << "\n  canonical_mime_type   = " << scenario.canonical_mime_type
            << "\n  packets               = " << packets
            << "\n  resource_is_sensitive = "
            << base::ToString(scenario.resource_is_sensitive)
            << "\n  verdict               = " << verdict
            << "\n  verdict_packet        = " << scenario.verdict_packet;
}

// An HTML response with an HTML comment that's longer than the sniffing
// threshold. We don't sniff past net::kMaxBytesToSniff, so these are not
// protected
const char kHTMLWithTooLongComment[] =
    "<!--.............................................................72 chars"
    "................................................................144 chars"
    "................................................................216 chars"
    "................................................................288 chars"
    "................................................................360 chars"
    "................................................................432 chars"
    "................................................................504 chars"
    "................................................................576 chars"
    "................................................................648 chars"
    "................................................................720 chars"
    "................................................................792 chars"
    "................................................................864 chars"
    "................................................................936 chars"
    "...............................................................1008 chars"
    "...............................................................1080 chars"
    "--><html><head>";

// A set of test cases that verify CrossSiteDocumentResourceHandler correctly
// classifies network responses as allowed or blocked. These TestScenarios are
// passed to the TEST_P tests below as test parameters.
const TestScenario kScenarios[] = {

    // Allowed responses (without sniffing):
    {
        "Allowed: Same-origin XHR to HTML",
        __LINE__,
        "http://www.a.com/resource.html",           // target_url
        "http://www.a.com/",                        // initiator_origin
        "HTTP/1.1 200 OK",                          // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kAllow,                       // verdict
        kVerdictPacketForHeadersBasedVerdict,  // verdict_packet
    },
    {
        "Allowed: Same-origin JSON with parser breaker and HTML mime type",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/html",                       // response_content_type
        MimeType::kHtml,                   // canonical_mime_type
        {")]}',\n[true, true, false, \"user@chromium.org\"]"},  // packets
        false,  // resource_is_sensitive
        Verdict::kAllow,                       // verdict
        kVerdictPacketForHeadersBasedVerdict,  // verdict_packet
    },
    {
        "Allowed: Same-origin JSON with parser breaker and JSON mime type",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/json",                       // response_content_type
        MimeType::kJson,                   // canonical_mime_type
        {")]}'\n[true, true, false, \"user@chromium.org\"]"},  // packets
        false,  // resource_is_sensitive
        Verdict::kAllow,                       // verdict
        kVerdictPacketForHeadersBasedVerdict,  // verdict_packet
    },
    {
        "Allowed: Cross-site script without parser breaker",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "application/javascript",          // response_content_type
        MimeType::kOthers,                 // canonical_mime_type
        {"var x=3;"},                      // packets
        false,                             // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Allowed: Cross-site XHR to HTML with CORS for origin",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Allowed: Cross-site XHR to XML with CORS for any",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Access-Control-Allow-Origin: *",  // response_headers
        "application/rss+xml",             // response_content_type
        MimeType::kXml,                    // canonical_mime_type
        {"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"},  // packets
        false,                                  // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        "Allowed: Cross-site XHR to JSON with CORS for null",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Access-Control-Allow-Origin: null",    // response_headers
        "text/json",                            // response_content_type
        MimeType::kJson,                        // canonical_mime_type
        {"{\"x\" : 3}"},                        // packets
        false,                                  // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        // This case won't be reached in practice today, because CORB is only
        // used by certain URLLoaderFactories (e.g. in the NetworkService, when
        // handling http(s) URLs) and is not used for ftp://... URLs.
        "Blocked: Cross-site XHR to HTML over FTP",
        __LINE__,
        "ftp://www.b.com/resource.html",            // target_url
        "http://www.a.com/",                        // initiator_origin
        "HTTP/1.1 200 OK",                          // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kBlock,                            // verdict
        0,                                          // verdict_packet
    },
    {
        // This case won't be reached in practice today, because CORB is only
        // used by certain URLLoaderFactories (e.g. in the NetworkService, when
        // handling http(s) URLs) and is not used for file://... URLs.
        "Blocked: Cross-site XHR to HTML from file://",
        __LINE__,
        "file:///foo/resource.html",                // target_url
        "http://www.a.com/",                        // initiator_origin
        "HTTP/1.1 200 OK",                          // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kBlock,                            // verdict
        0,                                          // verdict_packet
    },
    {
        // Blocked. (Simulating a behavior of a compromised renderer that only
        // pretends to be hosting PDF).
        "Blocked: Cross-site fetch HTML from Flash without CORS",
        __LINE__,
        "http://www.b.com/plugin.html",  // target_url
        "http://www.a.com/",             // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff",          // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kBlock,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    {
        "Allowed: Cross-site fetch HTML from NaCl with CORS response",
        __LINE__,
        "http://www.b.com/plugin.html",  // target_url
        "http://www.a.com/",             // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // first_chunk
        true,                                       // resource_is_sensitive
        Verdict::kAllow,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    {
        "Allowed: JSON object + CORS with parser-breaker labeled as JavaScript",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff\n"
        "Access-Control-Allow-Origin: *",       // response_headers
        "application/javascript",               // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {")]}'\n[true, false]"},                // packets
        false,                                  // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        "Blocked: JSON object labeled as JavaScript with a no-sniff header",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff",      // response_headers
        "application/javascript",               // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {"{ \"key\"", ": true }"},              // packets
        false,                                  // resource_is_sensitive
        Verdict::kBlock,                        // verdict
        1,                                      // verdict_packet
    },
    {
        "Allowed: Empty response with PNG mime type",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "image/png",                       // response_content_type
        MimeType::kOthers,                 // canonical_mime_type
        {},                                // packets
        false,                             // resource_is_sensitive
        Verdict::kAllowBecauseOutOfData,        // verdict
        kVerdictPacketForInconclusiveSniffing,  // verdict_packet
    },
    {
        "Allowed: Empty response with PNG mime type and nosniff header",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff",  // response_headers
        "image/png",                        // response_content_type
        MimeType::kOthers,                  // canonical_mime_type
        {},                                 // packets
        false,                              // resource_is_sensitive
        Verdict::kAllowBecauseOutOfData,        // verdict
        kVerdictPacketForInconclusiveSniffing,  // verdict_packet
    },

    // Allowed responses due to sniffing:
    {
        "Allowed: Cross-site script to JSONP labeled as HTML",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/html",                       // response_content_type
        MimeType::kHtml,                   // canonical_mime_type
        {"foo({\"x\" : 3})"},              // packets
        false,                             // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Allowed: Cross-site script to JavaScript labeled as text",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/plain",                      // response_content_type
        MimeType::kPlain,                  // canonical_mime_type
        {"var x = 3;"},                    // packets
        false,                             // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Allowed: JSON-like JavaScript labeled as text",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/plain",                      // response_content_type
        MimeType::kPlain,                  // canonical_mime_type
        {"{", "    \n", "var x = 3;\n", "console.log('hello');"},  // packets
        false,  // resource_is_sensitive
        Verdict::kAllow,            // verdict
        2,                          // verdict_packet
    },

    {
        "Allowed: JSONP labeled as JSON",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/json",                       // response_content_type
        MimeType::kJson,                   // canonical_mime_type
        {"invoke({ \"key\": true });"},    // packets
        false,                             // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Allowed (for now): JSON array literal labeled as text/plain",
        __LINE__,
        "http://www.b.com/resource.html",      // target_url
        "http://www.a.com/",                   // initiator_origin
        "HTTP/1.1 200 OK",                     // response_headers
        "text/plain",                          // response_content_type
        MimeType::kPlain,                      // canonical_mime_type
        {"[1, 2, {}, true, false, \"yay\"]"},  // packets
        false,                                 // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Allowed: JSON array literal on which a function is called.",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/plain",                      // response_content_type
        MimeType::kPlain,                  // canonical_mime_type
        {"[1, 2, {}, true, false, \"yay\"]", ".map(x => console.log(x))",
         ".map(x => console.log(x));"},  // packets
        false,                           // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Allowed: Cross-site XHR to nonsense labeled as XML",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "application/xml",                 // response_content_type
        MimeType::kXml,                    // canonical_mime_type
        {"Won't sniff as XML"},            // packets
        false,                             // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Allowed: Cross-site XHR to nonsense labeled as JSON",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/json",                       // response_content_type
        MimeType::kJson,                   // canonical_mime_type
        {"Won't sniff as JSON"},           // packets
        false,                             // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Allowed: Cross-site XHR to partial match for <HTML> tag",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/html",                       // response_content_type
        MimeType::kHtml,                   // canonical_mime_type
        {"<htm"},                          // packets
        false,                             // resource_is_sensitive
        Verdict::kAllowBecauseOutOfData,        // verdict
        kVerdictPacketForInconclusiveSniffing,  // verdict_packet
    },
    {
        "Allowed: HTML tag appears only after net::kMaxBytesToSniff",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/html",                       // response_content_type
        MimeType::kHtml,                   // canonical_mime_type
        {kHTMLWithTooLongComment},         // packets
        false,                             // resource_is_sensitive
        Verdict::kAllowBecauseOutOfData,        // verdict
        kVerdictPacketForInconclusiveSniffing,  // verdict_packet
    },
    {
        "Allowed: Empty response with html mime type",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/html",                       // response_content_type
        MimeType::kHtml,                   // canonical_mime_type
        {},                                // packets
        false,                             // resource_is_sensitive
        Verdict::kAllowBecauseOutOfData,        // verdict
        kVerdictPacketForInconclusiveSniffing,  // verdict_packet
    },
    {
        "Allowed: Same-origin XHR to a filesystem URI",
        __LINE__,
        "filesystem:http://www.a.com/file.html",    // target_url
        "http://www.a.com/",                        // initiator_origin
        "HTTP/1.1 200 OK",                          // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kAllow,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    {
        "Allowed: Same-origin XHR to a blob URI",
        __LINE__,
        "blob:http://www.a.com/guid-goes-here",     // target_url
        "http://www.a.com/",                        // initiator_origin
        "HTTP/1.1 200 OK",                          // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kAllow,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },

    // Blocked responses (without sniffing):
    {
        "Blocked: Cross-site XHR to nosniff HTML without CORS",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff",          // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kBlock,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    {
        "Blocked: nosniff + Content-Type: text/html; charset=utf-8",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff",          // response_headers
        "text/html; charset=utf-8",                 // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kBlock,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    {
        "Blocked: Cross-site XHR to nosniff response without CORS",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff",      // response_headers
        "text/html",                            // response_content_type
        MimeType::kHtml,                        // canonical_mime_type
        {"Wouldn't sniff as HTML"},             // packets
        false,                                  // resource_is_sensitive
        Verdict::kBlock,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        "Blocked: Cross-origin, same-site XHR to nosniff HTML without CORS",
        __LINE__,
        "https://foo.site.com/resource.html",  // target_url
        "https://bar.site.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff",          // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kBlock,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    {
        "Blocked: Cross-site JSON with parser breaker/html/nosniff",
        __LINE__,
        "http://a.com/resource.html",  // target_url
        "http://c.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff",  // response_headers
        "text/html",                        // response_content_type
        MimeType::kHtml,                    // canonical_mime_type
        {")]", "}'\n[true, true, false, \"user@chromium.org\"]"},  // packets
        false,                                  // resource_is_sensitive
        Verdict::kBlock,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },

    {
        // This scenario is unusual, since there's no difference between
        // a blocked response and a non-blocked response.
        "Blocked(-ish?): Nosniff header + empty response",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff",      // response_headers
        "text/html",                            // response_content_type
        MimeType::kHtml,                        // canonical_mime_type
        {},                                     // packets
        false,                                  // resource_is_sensitive
        Verdict::kBlock,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },

    // CORB only applies to `no-cors` responses.
    {
        "Allowed: CORB N/A for CORS requests: Cross-site XHR + same-site CORS",
        // Note that initiator_origin is cross-origin, but same-site in relation
        // to the CORS response (the Access-Control-Allow-Origin header).
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://foo.example.com/",         // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Access-Control-Allow-Origin: http://example.com",  // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<hTmL><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Allowed: CORB N/A for CORS requests: Cross-site XHR with wrong CORS",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Access-Control-Allow-Origin: http://example.com",  // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<hTmL><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Allowed: CORB N/A for CORS requests: JSON parser-breaker + wrong CORS",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Access-Control-Allow-Origin: http://example.com\n"
        "X-Content-Type-Options: nosniff",  // response_headers
        "application/javascript",           // response_content_type
        MimeType::kOthers,                  // canonical_mime_type
        {")]}'\n[true, false]"},            // packets
        true,                               // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },

    // Blocked responses due to sniffing:
    {
        "Blocked: Cross-site XHR to HTML without CORS",
        __LINE__,
        "http://www.b.com/resource.html",           // target_url
        "http://www.a.com/",                        // initiator_origin
        "HTTP/1.1 200 OK",                          // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kBlock,            // verdict
        0,                          // verdict_packet
    },
    {
        "Blocked: Cross-site XHR to XML without CORS",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "application/xml",                 // response_content_type
        MimeType::kXml,                    // canonical_mime_type
        {"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"},  // packets
        false,  // resource_is_sensitive
        Verdict::kBlock,            // verdict
        0,                          // verdict_packet
    },
    {
        "Blocked: Cross-site XHR to JSON without CORS",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "application/json",                // response_content_type
        MimeType::kJson,                   // canonical_mime_type
        {"{\"x\" : 3}"},                   // packets
        false,                             // resource_is_sensitive
        Verdict::kBlock,            // verdict
        0,                          // verdict_packet
    },
    {
        "Blocked: slow-arriving JSON labeled as text/plain",
        __LINE__,
        "http://www.b.com/resource.html",             // target_url
        "http://www.a.com/",                          // initiator_origin
        "HTTP/1.1 200 OK",                            // response_headers
        "text/plain",                                 // response_content_type
        MimeType::kPlain,                             // canonical_mime_type
        {"    ", "\t", "{", "\"x\" ", "  ", ": 3}"},  // packets
        false,                                        // resource_is_sensitive
        Verdict::kBlock,            // verdict
        5,                          // verdict_packet
    },
    {
        "Blocked: slow-arriving xml labeled as text/plain",
        __LINE__,
        "http://www.b.com/resource.html",              // target_url
        "http://www.a.com/",                           // initiator_origin
        "HTTP/1.1 200 OK",                             // response_headers
        "text/plain",                                  // response_content_type
        MimeType::kPlain,                              // canonical_mime_type
        {"    ", "\t", "<", "?", "x", "m", "l", ">"},  // packets
        false,                                         // resource_is_sensitive
        Verdict::kBlock,            // verdict
        6,                          // verdict_packet
    },
    {
        "Blocked: slow-arriving html labeled as text/plain",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/plain",                      // response_content_type
        MimeType::kPlain,                  // canonical_mime_type
        {"    <!--", "\t -", "-", "->", "\n", "<", "s", "c", "r", "i", "p",
         "t"},  // packets
        false,  // resource_is_sensitive
        Verdict::kBlock,            // verdict
        11,                         // verdict_packet
    },
    {
        "Blocked: slow-arriving html with commented-out xml tag",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/plain",                      // response_content_type
        MimeType::kPlain,                  // canonical_mime_type
        {"    <!--", " <?xml ", "-->\n", "<", "h", "e", "a", "d"},  // packets
        false,  // resource_is_sensitive
        Verdict::kBlock,            // verdict
        7,                          // verdict_packet
    },
    {
        "Blocked: Cross-site XHR to HTML labeled as text without CORS",
        __LINE__,
        "http://www.b.com/resource.html",           // target_url
        "http://www.a.com/",                        // initiator_origin
        "HTTP/1.1 200 OK",                          // response_headers
        "text/plain",                               // response_content_type
        MimeType::kPlain,                           // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kBlock,            // verdict
        0,                          // verdict_packet
    },
    {
        "Blocked: Cross-site <script> inclusion of HTML w/ DTD without CORS",
        __LINE__,
        "http://www.b.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK",                 // response_headers
        "text/html",                       // response_content_type
        MimeType::kHtml,                   // canonical_mime_type
        {"<!doc", "type html><html itemscope=\"\" ",
         "itemtype=\"http://schema.org/SearchResultsPage\" ",
         "lang=\"en\"><head>"},  // packets
        false,                   // resource_is_sensitive
        Verdict::kBlock,            // verdict
        1,                          // verdict_packet
    },
    {
        "Blocked: Cross-site fetch HTML from NaCl without CORS response",
        __LINE__,
        "http://www.b.com/plugin.html",             // target_url
        "http://www.a.com/",                        // initiator_origin
        "HTTP/1.1 200 OK",                          // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // first_chunk
        false,                                      // resource_is_sensitive
        Verdict::kBlock,            // verdict
        0,                          // verdict_packet
    },
    {
        "Blocked: Cross-site JSON with parser breaker and JSON mime type",
        __LINE__,
        "http://a.com/resource.html",  // target_url
        "http://c.com/",               // initiator_origin
        "HTTP/1.1 200 OK",             // response_headers
        "text/json",                   // response_content_type
        MimeType::kJson,               // canonical_mime_type
        {")]", "}'\n[true, true, false, \"user@chromium.org\"]"},  // packets
        false,  // resource_is_sensitive
        Verdict::kBlock,            // verdict
        1,                          // verdict_packet
    },
    {
        "Blocked: Cross-site JSON with parser breaker/nosniff/other mime type",
        __LINE__,
        "http://a.com/resource.html",  // target_url
        "http://c.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff",  // response_headers
        "application/octet-stream",         // response_content_type
        MimeType::kOthers,                  // canonical_mime_type
        {")]", "}'\n[true, true, false, \"user@chromium.org\"]"},  // packets
        false,  // resource_is_sensitive
        Verdict::kBlock,            // verdict
        1,                          // verdict_packet
    },
    {
        "Blocked: Cross-site JSON with parser breaker and other mime type",
        __LINE__,
        "http://a.com/resource.html",  // target_url
        "http://c.com/",               // initiator_origin
        "HTTP/1.1 200 OK",             // response_headers
        "application/javascript",      // response_content_type
        MimeType::kOthers,             // canonical_mime_type
        {"for(;;)", ";[true, true, false, \"user@chromium.org\"]"},  // packets
        false,  // resource_is_sensitive
        Verdict::kBlock,            // verdict
        1,                          // verdict_packet
    },
    {
        // Test based on wpt/.../corb/.../css-with-json-parser-breaker.css
        "Blocked: Cross-site CSS with parser breaker and text/css mime type",
        __LINE__,
        "http://a.com/resource.css",            // target_url
        "http://c.com/",                        // initiator_origin
        "HTTP/1.1 200 OK",                      // response_headers
        "text/css",                             // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {R"()]}'
            {}
            #header { color: red; } )"},        // packets
        false,                                  // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        // Test based on http/tests/security/resources/xorigincss1.css
        "Blocked: Cross-site HTML/CSS polyglot with text/css mime type",
        __LINE__,
        "http://a.com/resource.css",            // target_url
        "http://c.com/",                        // initiator_origin
        "HTTP/1.1 200 OK",                      // response_headers
        "text/css",                             // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {R"(  <html>{}\n"
              .id3 {
                background-color: yellow;
              }
              </html> )"},                      // packets
        false,                                  // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        "Blocked: Cross-site XHR to a filesystem URI",
        __LINE__,
        "filesystem:http://www.b.com/file.html",    // target_url
        "http://www.a.com/",                        // initiator_origin
        "HTTP/1.1 200 OK",                          // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kBlock,            // verdict
        0,                          // verdict_packet
    },
    {
        "Blocked: Cross-site XHR to a blob URI",
        __LINE__,
        "blob:http://www.b.com/guid-goes-here",     // target_url
        "http://www.a.com/",                        // initiator_origin
        "HTTP/1.1 200 OK",                          // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kBlock,            // verdict
        0,                          // verdict_packet
    },
    // Range response. The product code doesn't currently look at the exact
    // range specified, so we can get away with testing with arbitrary/random
    // values.
    {
        "Blocked-by-ORB: Javascript 206",
        __LINE__,
        "http://www.b.com/script.js",  // target_url
        "http://www.a.com/",           // initiator_origin
        "HTTP/1.1 206 OK\n"
        "Content-Range: bytes 200-1000/67589",  // response_headers
        "application/javascript",               // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {"x = 1;"},                             // packets
        false,                                  // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        // Here the resources is allowed cross-origin from b.com to a.com
        // because of the CORS header. However CORB still blocks c.com from
        // accessing it (so the protection decision should be kBlock).
        "Allowed: text/html 206 media with CORS",
        __LINE__,
        "http://www.b.com/movie.html",  // target_url
        "http://www.a.com/",            // initiator_origin
        "HTTP/1.1 206 OK\n"
        "Content-Range: bytes 200-1000/67589\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "text/html",                             // response_content_type
        MimeType::kHtml,                         // canonical_mime_type
        {"simulated *middle*-of-html content"},  // packets
        true,                                    // resource_is_sensitive
        Verdict::kAllow,                         // verdict
        kVerdictPacketForHeadersBasedVerdict,    // verdict_packet
    },
    {
        "Blocked-by-ORB: text/plain 206 media",
        __LINE__,
        "http://www.b.com/movie.txt",  // target_url
        "http://www.a.com/",           // initiator_origin
        "HTTP/1.1 206 OK\n"
        "Content-Range: bytes 200-1000/67589",  // response_headers
        "text/plain",                           // response_content_type
        MimeType::kPlain,                       // canonical_mime_type
        {"movie content"},                      // packets
        false,                                  // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        "Blocked: text/html 206 media",
        __LINE__,
        "http://www.b.com/movie.html",  // target_url
        "http://www.a.com/",            // initiator_origin
        "HTTP/1.1 206 OK\n"
        "Content-Range: bytes 200-1000/67589",   // response_headers
        "text/html",                             // response_content_type
        MimeType::kHtml,                         // canonical_mime_type
        {"these middle bytes are unsniffable"},  // packets
        false,                                   // resource_is_sensitive
        Verdict::kBlock,                         // verdict
        kVerdictPacketForHeadersBasedVerdict,    // verdict_packet
    },
    {
        "Blocked-by-ORB: application/octet-stream 206 (middle)",
        __LINE__,
        "http://www.b.com/movie.html",  // target_url
        "http://www.a.com/",            // initiator_origin
        "HTTP/1.1 206 OK\n"
        "Content-Range: bytes 200-1000/67589",   // response_headers
        "application/octet-stream",              // response_content_type
        MimeType::kOthers,                       // canonical_mime_type
        {"these middle bytes are unsniffable"},  // packets
        false,                                   // resource_is_sensitive
        Verdict::kAllow,                         // verdict
        kVerdictPacketForHeadersBasedVerdict,    // verdict_packet
    },
    {
        "Allowed: application/octet-stream 206 media - beginning of video",
        __LINE__,
        "http://www.b.com/movie.mp4",  // target_url
        "http://www.a.com/",           // initiator_origin
        "HTTP/1.1 206 OK\n"
        "Content-Range: bytes 0-800/67589",  // response_headers
        "application/octet-stream",          // response_content_type
        MimeType::kOthers,                   // canonical_mime_type
        // Body of test response is based on:
        // 1) net/base/mime_sniffer.cc
        // 2) https://mimesniff.spec.whatwg.org/#signature-for-mp4
        {"....ftypmp4...."},                    // packets
        false,                                  // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        "Allowed: video/mp4 206 media - beginning of resource",
        __LINE__,
        "http://www.b.com/movie.mp4",  // target_url
        "http://www.a.com/",           // initiator_origin
        "HTTP/1.1 206 OK\n"
        "Content-Range: bytes 0-800/67589",  // response_headers
        "video/mp4",                         // response_content_type
        MimeType::kOthers,                   // canonical_mime_type
        // Body of test response is based on:
        // 1) net/base/mime_sniffer.cc
        // 2) https://mimesniff.spec.whatwg.org/#signature-for-mp4
        {"MIME type means this doesn't have to sniff as video"},  // packets
        false,                                  // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        "Allowed: video/mp4 206 media - middle of resource",
        __LINE__,
        "http://www.b.com/movie.mp4",  // target_url
        "http://www.a.com/",           // initiator_origin
        "HTTP/1.1 206 OK\n"
        "Content-Range: bytes 200-1000/67589",   // response_headers
        "video/mp4",                             // response_content_type
        MimeType::kOthers,                       // canonical_mime_type
        {"these middle bytes are unsniffable"},  // packets
        false,                                   // resource_is_sensitive
        Verdict::kAllow,                         // verdict
        kVerdictPacketForHeadersBasedVerdict,    // verdict_packet
    },
    // Responses with no data.
    {
        "Allowed: same-origin 204 response with no data",
        __LINE__,
        "http://a.com/resource.html",              // target_url
        "http://a.com/",                           // initiator_origin
        "HTTP/1.1 204 NO CONTENT",                 // response_headers
        "text/html",                               // response_content_type
        MimeType::kHtml,                           // canonical_mime_type
        {/* empty body doesn't sniff as html */},  // packets
        false,                                     // resource_is_sensitive
        Verdict::kAllow,                       // verdict
        kVerdictPacketForHeadersBasedVerdict,  // verdict_packet
    },
    {
        "Allowed after sniffing: cross-site 204 response with no data",
        __LINE__,
        "http://a.com/resource.html",              // target_url
        "http://b.com/",                           // initiator_origin
        "HTTP/1.1 204 NO CONTENT",                 // response_headers
        "text/html",                               // response_content_type
        MimeType::kHtml,                           // canonical_mime_type
        {/* empty body doesn't sniff as html */},  // packets
        false,                                     // resource_is_sensitive
        Verdict::kAllowBecauseOutOfData,        // verdict
        kVerdictPacketForInconclusiveSniffing,  // verdict_packet
    },

    // Testing the CORB protection logging.
    {
        "Not Sensitive: script without CORS or Cache heuristic",
        __LINE__,
        "http://www.a.com/resource.js",  // target_url
        "http://www.a.com/",             // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Vary: Origin",                         // response_headers
        "application/javascript",               // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {"var x=3;"},                           // packets
        false,                                  // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        "Not Sensitive: vary user-agent is present and should be ignored",
        __LINE__,
        "http://www.a.com/resource.js",  // target_url
        "http://www.a.com/",             // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Vary: Origin, User-Agent",             // response_headers
        "application/javascript",               // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {"var x=3;"},                           // packets
        false,                                  // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        "Not Sensitive: cache-control no-store should be ignored",
        __LINE__,
        "http://www.a.com/resource.js",  // target_url
        "http://www.a.com/",             // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Vary: Origin\n"
        "Cache-Control: No-Store",              // response_headers
        "application/javascript",               // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {"var x=3;"},                           // packets
        false,                                  // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    // Responses with the Access-Control-Allow-Origin header value other than *.
    {
        "Sensitive, Allowed: script with CORS heuristic and range header",
        __LINE__,
        "http://www.a.com/resource.js",  // target_url
        "http://www.a.com/",             // initiator_origin
        "HTTP/1.1 206 OK\n"
        "Vary: Origin\n"
        "Access-Control-Allow-Origin: http://www.a.com/\n"
        "Content-Range: bytes 200-1000/67589",  // response_headers
        "application/javascript",               // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {"var x=3;"},                           // packets
        true,                                   // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        "Sensitive, Blocked: html with CORS heuristic and no sniff",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Vary: Origin\n"
        "X-Content-Type-Options: nosniff\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    {
        "Sensitive, Blocked after sniffing: html with CORS heuristic",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Vary: Origin\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Sensitive, Allowed after sniffing: javascript with CORS heuristic",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Vary: Origin\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "application/javascript",  // response_content_type
        MimeType::kOthers,         // canonical_mime_type
        {"var x=3;"},              // packets
        true,                      // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Sensitive slow-arriving JSON with CORS heurisitic. Only needs "
        "sniffing for the CORP protection statistics.",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "text/json",                                  // response_content_type
        MimeType::kJson,                              // canonical_mime_type
        {"    ", "\t", "{", "\"x\" ", "  ", ": 3}"},  // packets
        true,                                         // resource_is_sensitive
        Verdict::kAllow,            // verdict
        5,                          // verdict_packet
    },

    // Responses with Vary: Origin and Cache-Control: Private headers.
    {
        "Sensitive, Allowed: script with cache heuristic and range header",
        __LINE__,
        "http://www.a.com/resource.js",  // target_url
        "http://www.a.com/",             // initiator_origin
        "HTTP/1.1 206 OK\n"
        "Vary: Origin\n"
        "Cache-Control: Private\n"
        "Content-Range: bytes 200-1000/67589",  // response_headers
        "application/javascript",               // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {"var x=3;"},                           // packets
        true,                                   // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        "Sensitive, Allowed: script with cache heuristic and range "
        "header. Has vary user agent + cache no store which should not "
        "confuse the cache heuristic.",
        __LINE__,
        "http://www.a.com/resource.js",  // target_url
        "http://www.a.com/",             // initiator_origin
        "HTTP/1.1 206 OK\n"
        "Vary: Origin, User-Agent\n"
        "Cache-Control: Private, No-Store\n"
        "Content-Range: bytes 200-1000/67589",  // response_headers
        "application/javascript",               // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {"var x=3;"},                           // packets
        true,                                   // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    {
        "Sensitive, Blocked: html with cache heuristic and no sniff",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff\n"
        "Vary: Origin\n"
        "Cache-Control: Private",                   // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    {
        "Sensitive, Blocked after sniffing: html with cache heuristic",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Vary: Origin\n"
        "Cache-Control: Private",                   // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Sensitive, Allowed after sniffing: javascript with cache heuristic",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Vary: Origin\n"
        "Cache-Control: Private",  // response_headers
        "application/javascript",  // response_content_type
        MimeType::kOthers,         // canonical_mime_type
        {"var x=3;"},              // packets
        true,                      // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Sensitive slow-arriving JSON with cache heurisitic. Only needs "
        "sniffing for the CORP protection statistics.",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Vary: Origin\n"
        "Cache-Control: Private",                     // response_headers
        "text/json",                                  // response_content_type
        MimeType::kJson,                              // canonical_mime_type
        {"    ", "\t", "{", "\"x\" ", "  ", ": 3}"},  // packets
        true,                                         // resource_is_sensitive
        Verdict::kAllow,            // verdict
        5,                          // verdict_packet
    },

    // The next two tests together ensure that when CORB blocks and strips the
    // cache/vary headers from a sensitive response the CORB protection logging
    // still correctly identifies the response as sensitive and reports it.
    //
    // In this first test, the protection logging reports immediately (without
    // sniffing). So we can (somewhat) safely assume the response is being
    // correctly reported as sensitive. Thus this test ensures the testing
    // infrastructure itself is also correctly idenitfying the response as
    // sensitive.
    {
        "Sensitive cache heuristic, both CORB and the protection stats block",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.b.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff\n"
        "Vary: Origin\n"
        "Cache-Control: Private",                   // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kBlock,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    // Here the protection logging only reports after sniffing. Despite this,
    // the resource should still be identified as sensitive.
    {
        "Sensitive cache heuristic, both CORB and the protection stats block",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.b.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Vary: Origin\n"
        "Cache-Control: Private",                   // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kBlock,            // verdict
        0,                          // verdict_packet
    },

    // A test that makes sure we don't double log the CORB protection stats.
    // Because the request is cross-origin + same-site and has a same-site CORP
    // header, CORB needs to sniff. However CORB protection logging makes the
    // request cross-site and so needs no sniffing. We don't want the protection
    // logging to be triggered a second time after the sniffing.
    {
        "Sensitive, CORB and CORB protection stats need sniffing",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.foo.a.com/",           // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Cross-Origin-Resource-Policy: same-site\n"
        "Vary: Origin\n"
        "Cache-Control: Private",                   // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kBlock,            // verdict
        0,                          // verdict_packet
    },

    // Response with an unknown MIME type.
    {
        "Sensitive, Allowed: unknown MIME type with CORS heuristic and range "
        "header",
        __LINE__,
        "http://www.a.com/resource.js",  // target_url
        "http://www.a.com/",             // initiator_origin
        "HTTP/1.1 206 OK\n"
        "Vary: Origin\n"
        "Content-Range: bytes 200-1000/67589\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "unknown/mime_type",                    // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {"var x=3;"},                           // packets
        true,                                   // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },

    // Responses with the accept-ranges header.
    {
        "Sensitive response with an accept-ranges header.",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Accept-Ranges: bytes\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Sensitive response with an accept-ranges header but value |none|.",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Accept-Ranges: none\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Non-sensitive response with an accept-ranges header.",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Accept-Ranges: bytes",                     // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        false,                                      // resource_is_sensitive
        Verdict::kAllow,                       // verdict
        kVerdictPacketForHeadersBasedVerdict,  // verdict_packet
    },
    // Sensitive responses with the accept-ranges header, a protected MIME type
    // and protection decision = kBlock.
    {
        "CORS-heuristic response with an accept-ranges header.",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff\n"
        "Accept-Ranges: bytes\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    {
        "Cache-heuristic response with an accept-ranges header.",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff\n"
        "Accept-Ranges: bytes\n"
        "Cache-Control: private\n"
        "Vary: origin",                             // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    {
        "Cache + CORS heuristics, accept-ranges header says |none|.",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff\n"
        "Accept-Ranges: none\n"
        "Cache-Control: private\n"
        "Access-Control-Allow-Origin: http://www.a.com/\n"
        "Vary: origin",                             // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    // Sensitive responses with the accept-ranges header, a protected MIME type
    // and protection decision = kBlockedAfterSniffing. (These tests are
    // identical to the previous 3, except they lack a nosniff header.)
    {
        "CORS-heuristic response with an accept-ranges header.",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Accept-Ranges: bytes\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Cache-heuristic response with an accept-ranges header.",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Accept-Ranges: bytes\n"
        "Cache-Control: private\n"
        "Vary: origin",                             // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Cache + CORS heuristics, accept-ranges header says |none|.",
        __LINE__,
        "http://www.a.com/resource.html",  // target_url
        "http://www.a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Accept-Ranges: none\n"
        "Cache-Control: private\n"
        "Access-Control-Allow-Origin: http://www.a.com/\n"
        "Vary: origin",                             // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    // A Sensitive response with the accept-ranges header and a protected MIME
    // type but protection decision = kAllow (so the secondary accept-ranges
    // stats should not be reported).
    {
        "Sensitive + accept-ranges header but protection decision = kAllow.",
        __LINE__,
        "http://www.a.com/resource.js",  // target_url
        "http://www.a.com/",             // initiator_origin
        "HTTP/1.1 206 OK\n"
        "X-Content-Type-Options: nosniff\n"
        "Accept-Ranges: bytes\n"
        "Content-Range: bytes 200-1000/67589\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "application/javascript",               // response_content_type
        MimeType::kOthers,                      // canonical_mime_type
        {"var x=3;"},                           // packets
        true,                                   // resource_is_sensitive
        Verdict::kAllow,                        // verdict
        kVerdictPacketForHeadersBasedVerdict,   // verdict_packet
    },
    // Sensitive responses with no data.
    {
        "Sensitive, allowed after sniffing: same-origin 204 with no data",
        __LINE__,
        "http://a.com/resource.html",  // target_url
        "http://a.com/",               // initiator_origin
        "HTTP/1.1 204 NO CONTENT\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "text/html",                               // response_content_type
        MimeType::kHtml,                           // canonical_mime_type
        {/* empty body doesn't sniff as html */},  // packets
        true,                                      // resource_is_sensitive
        Verdict::kAllowBecauseOutOfData,        // verdict
        kVerdictPacketForInconclusiveSniffing,  // verdict_packet
    },

    // These responses confirm we are correctly reporting when a nosniff header
    // is present. This should *only* be reported when a blocked, sensitive,
    // protected mime-type response has a nosniff header.
    //
    // This first response satisfies all criteria except it does not have a
    // nosniff header.
    {
        "Cache heuristic with no nosniff header",
        __LINE__,
        "http://a.com/resource.html",  // target_url
        "http://a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "Cache-Control: private\n"
        "Vary: origin",                             // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    // These next responses have nosniff headers but are missing one of the
    // other criteria.
    {
        "Cache heuristic with nosniff header but not a protected type",
        __LINE__,
        "http://a.com/resource.js",  // target_url
        "http://a.com/",             // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff\n"
        "Cache-Control: private\n"
        "Vary: origin",            // response_headers
        "application/javascript",  // response_content_type
        MimeType::kOthers,         // canonical_mime_type
        {"var x=3;"},              // packets
        true,                      // resource_is_sensitive
        Verdict::kAllow,            // verdict
        0,                          // verdict_packet
    },
    {
        "Cache heuristic with nosniff header and protection decision == kBlock",
        __LINE__,
        "http://a.com/resource.html",  // target_url
        "http://a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff\n"
        "Access-Control-Allow-Origin: *\n"
        "Cache-Control: private\n"
        "Vary: origin",                             // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    // These responses satisfies all criteria and should report its nosniff
    // header.
    {
        "Nosniff header and satisfies the CORS heuristic",
        __LINE__,
        "http://a.com/resource.html",  // target_url
        "http://a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff\n"
        "Access-Control-Allow-Origin: http://www.a.com/",  // response_headers
        "text/html",                                // response_content_type
        MimeType::kHtml,                            // canonical_mime_type
        {"<html><head>this should sniff as HTML"},  // packets
        true,                                       // resource_is_sensitive
        Verdict::kAllow,                            // verdict
        kVerdictPacketForHeadersBasedVerdict,       // verdict_packet
    },
    {
        "Nosniff header and satisfies the Cache heuristic",
        __LINE__,
        "http://a.com/resource.html",  // target_url
        "http://a.com/",               // initiator_origin
        "HTTP/1.1 200 OK\n"
        "X-Content-Type-Options: nosniff\n"
        "Cache-Control: private\n"
        "Vary: origin",                            // response_headers
        "text/html",                               // response_content_type
        MimeType::kHtml,                           // canonical_mime_type
        {/* empty body doesn't sniff as html */},  // packets
        true,                                      // resource_is_sensitive
        Verdict::kAllow,                           // verdict
        kVerdictPacketForHeadersBasedVerdict,      // verdict_packet
    },
};

}  // namespace

// Tests that verify ResponseAnalyzer correctly classifies network responses as
// allowed or blocked.
//
// The various test cases are passed as a list of TestScenario structs.
class ResponseAnalyzerTest : public testing::Test,
                             public testing::WithParamInterface<TestScenario> {
 public:
  ResponseAnalyzerTest()
      : context_(net::CreateTestURLRequestContextBuilder()->Build()) {}

  ResponseAnalyzerTest(const ResponseAnalyzerTest&) = delete;
  ResponseAnalyzerTest& operator=(const ResponseAnalyzerTest&) = delete;

  // Returns a ResourceResponse that matches the TestScenario's parameters.
  mojom::URLResponseHeadPtr CreateResponse(
      const std::string& response_content_type,
      const std::string& raw_response_headers,
      const std::string& initiator_origin) {
    auto response = mojom::URLResponseHead::New();
    std::string formatted_response_headers =
        net::HttpUtil::AssembleRawHeaders(raw_response_headers);
    scoped_refptr<net::HttpResponseHeaders> response_headers =
        base::MakeRefCounted<net::HttpResponseHeaders>(
            formatted_response_headers);

    std::string charset;
    bool had_charset = false;
    response_headers->SetHeader("Content-Type", response_content_type);
    net::HttpUtil::ParseContentType(response_content_type, &response->mime_type,
                                    &charset, &had_charset, nullptr);
    EXPECT_FALSE(response->mime_type.empty())
        << "Invalid MIME type defined in kScenarios.";
    response->headers = response_headers;

    return response;
  }

  // Take and run ResponseAnalyzer on the current scenario. Allow the analyzer
  // to sniff the response body if needed and confirm it correctly decides to
  // block or allow.
  void RunAnalyzerOnScenario(const TestScenario& scenario,
                             const mojom::URLResponseHead& response,
                             std::unique_ptr<ResponseAnalyzer> analyzer,
                             bool verify_when_decision_is_made = true) {
    // Initialize |request| from the parameters.
    std::unique_ptr<net::URLRequest> request = context_->CreateRequest(
        GURL(scenario.target_url), net::DEFAULT_PRIORITY, &delegate_,
        TRAFFIC_ANNOTATION_FOR_TESTS);
    request->set_initiator(
        url::Origin::Create(GURL(scenario.initiator_origin)));

    // Check if this is a CORS request.
    auto request_mode =
        response.headers->GetNormalizedHeader("access-control-allow-origin")
                    .value_or(std::string()) == ""
            ? mojom::RequestMode::kNoCors
            : mojom::RequestMode::kCors;

    // Initialize the `analyzer`.
    //
    // Note that the `analyzer` will be destructed when `analyzer` goes out of
    // scope (the destructor may trigger logging of UMAs that some callers of
    // RunAnalyzerOnScenario attempt to verify).
    ResponseAnalyzer::Decision decision =
        analyzer->Init(request->url(), request->initiator(), request_mode,
                       mojom::RequestDestination::kEmpty, response);

    // This vector holds the packets to be delivered.
    std::vector<const char*> packets_vector(scenario.packets);
    packets_vector.push_back(
        "");  // End-of-stream is marked by an empty packet.

    // If the |verdict_packet| == kVerdictPacketForHeadersBasedVerdict = -1,
    // then the sniffing loop below will be skipped.
    if (scenario.verdict_packet != kVerdictPacketForInconclusiveSniffing &&
        scenario.verdict_packet != kVerdictPacketForHeadersBasedVerdict) {
      EXPECT_LT(scenario.verdict_packet,
                static_cast<int>(packets_vector.size()));
    }

    // Verify that the ResponseAnalyzer asks for sniffing if this is what the
    // testcase expects.
    if (verify_when_decision_is_made) {
      bool expected_to_sniff =
          scenario.verdict_packet != kVerdictPacketForHeadersBasedVerdict;
      if (expected_to_sniff) {
        EXPECT_EQ(decision, ResponseAnalyzer::Decision::kSniffMore);
      } else {
        // If we don't expect to sniff then ResponseAnalyzer should have already
        // made a blockng decision based on the headers.
        if (scenario.verdict == Verdict::kBlock) {
          EXPECT_EQ(decision, ResponseAnalyzer::Decision::kBlock);
        } else {
          EXPECT_EQ(decision, ResponseAnalyzer::Decision::kAllow);
        }
      }
    }

    // Simulate the behaviour of the URLLoader by appending the packets into
    // |data_buffer| and feeding this to |analyzer|.
    bool run_out_of_data_to_sniff = false;
    int actual_verdict_packet = kVerdictPacketForHeadersBasedVerdict;
    if (decision == ResponseAnalyzer::Decision::kSniffMore) {
      std::string data_buffer;
      size_t data_offset = 0;
      for (size_t packet_index = 0; packet_index < packets_vector.size();
           packet_index++) {
        SCOPED_TRACE(testing::Message()
                     << "While delivering packet #" << packet_index);

        // At each iteration of the loop we feed a new packet to |analyzer|,
        // expecting to get a decision at the |verdict_packet|. Since we haven't
        // given the next packet to |analyzer| yet at this point in the loop, it
        // shouldn't have made a decision yet.
        EXPECT_EQ(decision, ResponseAnalyzer::Decision::kSniffMore);

        // Append the next packet of the response body. If appending the entire
        // packet would exceed net::kMaxBytesToSniff we truncate the data.
        size_t bytes_to_append = strlen(packets_vector[packet_index]);
        if (data_offset + bytes_to_append > net::kMaxBytesToSniff)
          bytes_to_append = net::kMaxBytesToSniff - data_offset;
        data_buffer.append(packets_vector[packet_index], bytes_to_append);

        // Hand |analyzer_| the data to sniff.
        decision = analyzer->Sniff(data_buffer);
        data_offset += bytes_to_append;
        if (decision != ResponseAnalyzer::Decision::kSniffMore) {
          actual_verdict_packet = packet_index;
          break;
        }
      }
    }

    // Handle scenarios where no decision can be made before running out of data
    // to sniff.
    if (decision == ResponseAnalyzer::Decision::kSniffMore) {
      actual_verdict_packet = kVerdictPacketForInconclusiveSniffing;
      run_out_of_data_to_sniff = true;
      decision = analyzer->HandleEndOfSniffableResponseBody();

      // HandleEndOfSniffableResponseBody should never return kSniffMore.
      EXPECT_NE(decision, ResponseAnalyzer::Decision::kSniffMore);
    }

    // Confirm the analyzer is blocking or allowing correctly (now that we have
    // performed any needed sniffing).
    if (verify_when_decision_is_made) {
      EXPECT_EQ(scenario.verdict_packet, actual_verdict_packet);
    }
    if (scenario.verdict == Verdict::kBlock) {
      EXPECT_EQ(decision, ResponseAnalyzer::Decision::kBlock);
    } else {
      EXPECT_EQ(decision, ResponseAnalyzer::Decision::kAllow);

      if (verify_when_decision_is_made) {
        // In this case either the |analyzer| has decided to allow the response,
        // or run out of data and so the response will be allowed by default.
        if (scenario.verdict == Verdict::kAllow) {
          EXPECT_FALSE(run_out_of_data_to_sniff);
        } else {
          EXPECT_EQ(Verdict::kAllowBecauseOutOfData, scenario.verdict);
          EXPECT_TRUE(run_out_of_data_to_sniff);
        }
      }
    }
  }

 protected:
  base::test::TaskEnvironment task_environment_;
  std::unique_ptr<net::URLRequestContext> context_;
  net::TestDelegate delegate_;
};  // namespace network

TEST_P(ResponseAnalyzerTest, OpaqueResponseBlocking) {
  TestScenario scenario = GetParam();
  SCOPED_TRACE(testing::Message()
               << "\nScenario at " << __FILE__ << ":" << scenario.source_line);

  // Unlike CORB, ORB blocks all 206 responses, unless there was an earlier
  // request to the same URL and that earlier request was classified (based on
  // the MIME type or sniffing) as an audio-or-video response.
  std::string_view description = scenario.description;
  if (description == "Blocked-by-ORB: text/plain 206 media" ||
      description == "Blocked-by-ORB: Javascript 206" ||
      description == "Blocked-by-ORB: application/octet-stream 206 (middle)") {
    scenario.verdict = Verdict::kBlock;
    scenario.verdict_packet = kVerdictPacketForHeadersBasedVerdict;
  }

  // Initialize |response| from the parameters and record if it looks sensitive
  // or supports range requests. These values are saved because the analyzer
  // will clear the response headers in the event it decides to block.
  auto response =
      CreateResponse(scenario.response_content_type, scenario.response_headers,
                     scenario.initiator_origin);

  // ORB may make the final decision at a different time than CORB.  This
  // makes no difference from functional/observable behavior perspective
  // and skipping this verification makes it easier to share testcases
  // across ORB and CORB.
  //
  // TODO(lukasza): Eventually `verify_when_decision_is_made` for ORB (once
  // CORB is removed at the latest, but possibly earlier than that).
  constexpr bool kVerifyWhenDecisionIsMade = false;

  PerFactoryState per_factory_state;
  auto analyzer =
      std::make_unique<OpaqueResponseBlockingAnalyzer>(&per_factory_state);

  RunAnalyzerOnScenario(scenario, *response, std::move(analyzer),
                        kVerifyWhenDecisionIsMade);
}

INSTANTIATE_TEST_SUITE_P(All,
                         ResponseAnalyzerTest,
                         ::testing::ValuesIn(kScenarios));

// =============================================================================
// The following individual tests check the behaviour of various methods in
// isolation.
// =============================================================================

mojom::URLResponseHeadPtr CreateResponse(std::string raw_headers) {
  scoped_refptr<net::HttpResponseHeaders> headers =
      base::MakeRefCounted<net::HttpResponseHeaders>(
          net::HttpUtil::AssembleRawHeaders(raw_headers));

  auto response = mojom::URLResponseHead::New();
  response->headers = headers;
  return response;
}

TEST(CrossOriginReadBlockingTest, OrbReportsIssuesOnARAResponse) {
  struct {
    std::string header;
    bool expect_report;
  } kTestCases[] = {
      {/*header=*/"", /*expect_report=*/false},
      {"Attribution-Reporting-Register-Source: {}", /*expect_report=*/true},
      {"Attribution-Reporting-Register-Trigger: {}", /*expect_report=*/true},
      {"Attribution-Reporting-Register-OS-Source: {}", /*expect_report=*/true},
      {"Attribution-Reporting-Register-OS-Trigger: {}",
       /*expect_report=*/true},
  };

  for (const auto& test_case : kTestCases) {
    PerFactoryState per_factory_state;
    auto analyzer =
        std::make_unique<OpaqueResponseBlockingAnalyzer>(&per_factory_state);
    auto ara_response = CreateResponse("HTTP/1.1 200 OK\n" + test_case.header);

    // Mark the response as empty s.t. it would normally not report.
    ara_response->content_length = 0;

    analyzer->Init(GURL("https://b.test"),
                   url::Origin::Create(GURL("https://a.test")),
                   mojom::RequestMode::kNoCors,
                   mojom::RequestDestination::kEmpty, *ara_response);
    EXPECT_EQ(analyzer->ShouldReportBlockedResponse(), test_case.expect_report);
  }
}

}  // namespace network::orb