Home | History | Annotate | Download | only in browser
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h"
      6 
      7 #include <string.h>
      8 #include <algorithm>
      9 #include <map>
     10 #include <vector>
     11 
     12 #include "base/base64.h"
     13 #include "base/md5.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/strings/string_number_conversions.h"
     16 #include "base/strings/string_split.h"
     17 #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h"
     18 #include "components/data_reduction_proxy/common/data_reduction_proxy_headers_test_utils.h"
     19 #include "net/http/http_response_headers.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 
     22 #if defined(OS_ANDROID)
     23 #include "base/android/jni_android.h"
     24 #include "net/android/network_library.h"
     25 #endif
     26 
     27 namespace {
     28 
     29 // Calcuates MD5 hash value for a string and then base64 encode it. Testcases
     30 // contain expected fingerprint in plain text, which needs to be encoded before
     31 // comparison.
     32 std::string GetEncoded(const std::string& input) {
     33   base::MD5Digest digest;
     34   base::MD5Sum(input.c_str(), input.size(), &digest);
     35   std::string base64encoded;
     36   base::Base64Encode(std::string((char*)digest.a,
     37                      ARRAYSIZE_UNSAFE(digest.a)), &base64encoded);
     38   return base64encoded;
     39 }
     40 
     41 // Replaces all contents within "[]" by corresponding base64 encoded MD5 value.
     42 // It can handle nested case like: [[abc]def]. This helper function transforms
     43 // fingerprint in plain text to actual encoded fingerprint.
     44 void ReplaceWithEncodedString(std::string* input) {
     45   size_t start, end, temp;
     46   while (true) {
     47     start = input->find("[");
     48     if (start == std::string::npos) break;
     49     while (true) {
     50       temp = input->find("[", start + 1);
     51       end = input->find("]", start + 1);
     52       if (end != std::string::npos && end < temp)
     53         break;
     54 
     55       start = temp;
     56     }
     57     std::string need_to_encode = input->substr(start + 1, end - start - 1);
     58     *input = input->substr(0, start) + GetEncoded(need_to_encode) +
     59              input->substr(end + 1);
     60   }
     61 }
     62 
     63 // Returns a vector contains all the values from a comma-separated string.
     64 // Some testcases contain string representation of a vector, this helper
     65 // function generates a vector from a input string.
     66 std::vector<std::string> StringsToVector(const std::string& values) {
     67   std::vector<std::string> ret;
     68   if (values.empty())
     69     return ret;
     70   size_t now = 0;
     71   size_t next;
     72   while ((next = values.find(",", now)) != std::string::npos) {
     73     ret.push_back(values.substr(now, next - now));
     74     now = next + 1;
     75   }
     76   return ret;
     77 }
     78 
     79 void InitEnv() {
     80 #if defined(OS_ANDROID)
     81   JNIEnv* env = base::android::AttachCurrentThread();
     82   static bool inited = false;
     83   if (!inited) {
     84     net::android::RegisterNetworkLibrary(env);
     85     inited = true;
     86   }
     87 #endif
     88 }
     89 
     90 }  // namespace
     91 
     92 namespace data_reduction_proxy {
     93 
     94 class DataReductionProxyTamperDetectionTest : public testing::Test {
     95 
     96 };
     97 
     98 // Tests function ValidateChromeProxyHeader.
     99 TEST_F(DataReductionProxyTamperDetectionTest, ChromeProxy) {
    100   // |received_fingerprint| is not the actual fingerprint from data reduction
    101   // proxy, instead, the base64 encoded field is in plain text (within "[]")
    102   // and needs to be encoded first.
    103   struct {
    104     std::string label;
    105     std::string raw_header;
    106     std::string received_fingerprint;
    107     bool expected_tampered_with;
    108   } test[] = {
    109     {
    110       "Checks sorting.",
    111       "HTTP/1.1 200 OK\n"
    112       "Chrome-Proxy: c,b,a,3,2,1,fcp=f\n",
    113       "[1,2,3,a,b,c,]",
    114       false,
    115     },
    116     {
    117       "Checks Chrome-Proxy's fingerprint removing.",
    118       "HTTP/1.1 200 OK\n"
    119       "Chrome-Proxy: a,b,c,d,e,3,2,1,fcp=f\n",
    120       "[1,2,3,a,b,c,d,e,]",
    121       false,
    122     },
    123     {
    124       "Checks no Chrome-Proxy header case (should not happen).",
    125       "HTTP/1.1 200 OK\n",
    126       "[]",
    127       false,
    128     },
    129     {
    130       "Checks empty Chrome-Proxy header case (should not happen).",
    131       "HTTP/1.1 200 OK\n"
    132       "Chrome-Proxy:    \n",
    133       "[,]",
    134       false,
    135     },
    136     {
    137       "Checks Chrome-Proxy header with its fingerprint only case.",
    138       "HTTP/1.1 200 OK\n"
    139       "Chrome-Proxy: fcp=f\n",
    140       "[]",
    141       false,
    142     },
    143     {
    144       "Checks empty Chrome-Proxy header case, with extra ',' and ' '",
    145       "HTTP/1.1 200 OK\n"
    146       "Chrome-Proxy: fcp=f  ,  \n",
    147       "[]",
    148       false,
    149     },
    150     {
    151       "Changed no value to empty value.",
    152       "HTTP/1.1 200 OK\n"
    153       "Chrome-Proxy: fcp=f\n",
    154       "[,]",
    155       true,
    156     },
    157     {
    158       "Changed header values.",
    159       "HTTP/1.1 200 OK\n"
    160       "Chrome-Proxy: a,b=2,c,d=1,fcp=f\n",
    161       "[a,b=3,c,d=1,]",
    162       true,
    163     },
    164     {
    165       "Changed order of header values.",
    166       "HTTP/1.1 200 OK\n"
    167       "Chrome-Proxy: c,b,a,fcp=1\n",
    168       "[c,b,a,]",
    169       true,
    170     },
    171     {
    172       "Checks Chrome-Proxy header with extra ' '.",
    173       "HTTP/1.1 200 OK\n"
    174       "Chrome-Proxy: a  , b   , c,   d,  fcp=f\n",
    175       "[a,b,c,d,]",
    176       false
    177     },
    178     {
    179       "Check Chrome-Proxy header with multiple lines and ' '.",
    180       "HTTP/1.1 200 OK\n"
    181       "Chrome-Proxy:     a    ,   c   , d, fcp=f    \n"
    182       "Chrome-Proxy:    b \n",
    183       "[a,b,c,d,]",
    184       false
    185     },
    186     {
    187       "Checks Chrome-Proxy header with multiple lines, at different positions",
    188       "HTTP/1.1 200 OK\n"
    189       "Chrome-Proxy:     a   \n"
    190       "Chrome-Proxy:    c \n"
    191       "Content-Type: 1\n"
    192       "Cache-Control: 2\n"
    193       "ETag: 3\n"
    194       "Chrome-Proxy:    b  \n"
    195       "Connection: 4\n"
    196       "Expires: 5\n"
    197       "Chrome-Proxy:    fcp=f \n"
    198       "Via: \n"
    199       "Content-Length: 12345\n",
    200       "[a,b,c,]",
    201       false
    202     },
    203     {
    204       "Checks Chrome-Proxy header with multiple same values.",
    205       "HTTP/1.1 200 OK\n"
    206       "Chrome-Proxy:     a   \n"
    207       "Chrome-Proxy:    b\n"
    208       "Chrome-Proxy:    c\n"
    209       "Chrome-Proxy:    d,   fcp=f    \n"
    210       "Chrome-Proxy:     a   \n",
    211       "[a,a,b,c,d,]",
    212       false
    213     },
    214     {
    215       "Changed Chrome-Proxy header with multiple lines..",
    216       "HTTP/1.1 200 OK\n"
    217       "Chrome-Proxy: a\n"
    218       "Chrome-Proxy: a\n"
    219       "Chrome-Proxy: b\n"
    220       "Chrome-Proxy: c,fcp=f\n",
    221       "[a,b,c,]",
    222       true,
    223     },
    224     {
    225       "Checks case whose received fingerprint is empty.",
    226       "HTTP/1.1 200 OK\n"
    227       "Chrome-Proxy: a,b,c,fcp=1\n",
    228       "[]",
    229       true,
    230     },
    231     {
    232       "Checks case whose received fingerprint cannot be base64 decoded.",
    233       "HTTP/1.1 200 OK\n"
    234       "Chrome-Proxy: a,b,c,fcp=1\n",
    235       "not_base64_encoded",
    236       true,
    237     },
    238   };
    239 
    240   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
    241     ReplaceWithEncodedString(&test[i].received_fingerprint);
    242 
    243     std::string raw_headers(test[i].raw_header);
    244     HeadersToRaw(&raw_headers);
    245     scoped_refptr<net::HttpResponseHeaders> headers(
    246         new net::HttpResponseHeaders(raw_headers));
    247 
    248     DataReductionProxyTamperDetection tamper_detection(headers.get(), true, 0);
    249 
    250     bool tampered = tamper_detection.ValidateChromeProxyHeader(
    251         test[i].received_fingerprint);
    252 
    253     EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
    254   }
    255 }
    256 
    257 // Tests function ValidateViaHeader.
    258 TEST_F(DataReductionProxyTamperDetectionTest, Via) {
    259   struct {
    260     std::string label;
    261     std::string raw_header;
    262     std::string received_fingerprint;
    263     bool expected_tampered_with;
    264     bool expected_has_chrome_proxy_via_header;
    265   } test[] = {
    266     {
    267       "Checks the case that Chrome-Compression-Proxy occurs at the last.",
    268       "HTTP/1.1 200 OK\n"
    269       "Via: a, b, c, 1.1 Chrome-Compression-Proxy\n",
    270       "",
    271       false,
    272       true,
    273     },
    274     {
    275       "Checks when there is intermediary.",
    276       "HTTP/1.1 200 OK\n"
    277       "Via: a, b,  c,   1.1 Chrome-Compression-Proxy,    xyz\n",
    278       "",
    279       true,
    280       true,
    281     },
    282     {
    283       "Checks the case of empty Via header.",
    284       "HTTP/1.1 200 OK\n"
    285       "Via:  \n",
    286       "",
    287       true,
    288       false,
    289     },
    290     {
    291       "Checks the case that only the data reduction proxy's Via header occurs.",
    292       "HTTP/1.1 200 OK\n"
    293       "Via:  1.1 Chrome-Compression-Proxy    \n",
    294       "",
    295       false,
    296       true,
    297     },
    298     {
    299       "Checks the case that there are ' ', i.e., empty value after the data"
    300       " reduction proxy's Via header.",
    301       "HTTP/1.1 200 OK\n"
    302       "Via:  1.1 Chrome-Compression-Proxy  ,  , \n",
    303       "",
    304       false,
    305       true,
    306     },
    307     {
    308       "Checks the case when there is no Via header",
    309       "HTTP/1.1 200 OK\n",
    310       "",
    311       true,
    312       false,
    313     },
    314     // Same to above test cases, but with deprecated data reduciton proxy Via
    315     // header.
    316     {
    317       "Checks the case that Chrome Compression Proxy occurs at the last.",
    318       "HTTP/1.1 200 OK\n"
    319       "Via: a, b, c, 1.1 Chrome Compression Proxy\n",
    320       "",
    321       false,
    322       true,
    323     },
    324     {
    325       "Checks when there is intermediary.",
    326       "HTTP/1.1 200 OK\n"
    327       "Via: a, b,  c,   1.1 Chrome Compression Proxy,    xyz\n",
    328       "",
    329       true,
    330       true,
    331     },
    332     {
    333       "Checks the case of empty Via header.",
    334       "HTTP/1.1 200 OK\n"
    335       "Via:  \n",
    336       "",
    337       true,
    338       false,
    339     },
    340     {
    341       "Checks the case that only the data reduction proxy's Via header occurs.",
    342       "HTTP/1.1 200 OK\n"
    343       "Via:  1.1 Chrome Compression Proxy    \n",
    344       "",
    345       false,
    346       true,
    347     },
    348     {
    349       "Checks the case that there are ' ', i.e., empty value after the data"
    350       "reduction proxy's Via header.",
    351       "HTTP/1.1 200 OK\n"
    352       "Via:  1.1 Chrome Compression Proxy  ,  , \n",
    353       "",
    354       false,
    355       true,
    356     },
    357     {
    358       "Checks the case when there is no Via header",
    359       "HTTP/1.1 200 OK\n",
    360       "",
    361       true,
    362       false,
    363     },
    364   };
    365 
    366   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
    367     std::string raw_headers(test[i].raw_header);
    368     HeadersToRaw(&raw_headers);
    369     scoped_refptr<net::HttpResponseHeaders> headers(
    370         new net::HttpResponseHeaders(raw_headers));
    371 
    372     DataReductionProxyTamperDetection tamper_detection(headers.get(), true, 0);
    373 
    374     bool has_chrome_proxy_via_header;
    375     bool tampered = tamper_detection.ValidateViaHeader(
    376         test[i].received_fingerprint, &has_chrome_proxy_via_header);
    377 
    378     EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
    379     EXPECT_EQ(test[i].expected_has_chrome_proxy_via_header,
    380               has_chrome_proxy_via_header) << test[i].label;
    381   }
    382 }
    383 
    384 // Tests function ValidateOtherHeaders.
    385 TEST_F(DataReductionProxyTamperDetectionTest, OtherHeaders) {
    386   // For following testcases, |received_fingerprint| is not the actual
    387   // fingerprint from data reduction proxy, instead, the base64 encoded field
    388   // is in plain text (within "[]") and needs to be encoded first. For example,
    389   // "[12345;]|content-length" needs to be encoded to
    390   // "Base64Encoded(MD5(12345;))|content-length" before calling the checking
    391   // function.
    392   struct {
    393     std::string label;
    394     std::string raw_header;
    395     std::string received_fingerprint;
    396     bool expected_tampered_with;
    397   } test[] = {
    398     {
    399       "Checks the case that only one header is requested.",
    400       "HTTP/1.1 200 OK\n"
    401       "Content-Length: 12345\n",
    402       "[12345,;]|content-length",
    403       false
    404     },
    405     {
    406       "Checks the case that there is only one requested header and it does not"
    407       "exist.",
    408       "HTTP/1.1 200 OK\n",
    409       "[;]|non_exist_header",
    410       false
    411     },
    412     {
    413       "Checks the case of multiple headers are requested.",
    414       "HTTP/1.1 200 OK\n"
    415       "Content-Type: 1\n"
    416       "Cache-Control: 2\n"
    417       "ETag: 3\n"
    418       "Connection: 4\n"
    419       "Expires: 5\n",
    420       "[1,;2,;3,;4,;5,;]|content-type|cache-control|etag|connection|expires",
    421       false
    422     },
    423     {
    424       "Checks the case that one header has multiple values.",
    425       "HTTP/1.1 200 OK\n"
    426       "Content-Type: aaa1,    bbb1, ccc1\n"
    427       "Cache-Control: aaa2\n",
    428       "[aaa1,bbb1,ccc1,;aaa2,;]|content-type|cache-control",
    429       false
    430     },
    431     {
    432       "Checks the case that one header has multiple lines.",
    433       "HTTP/1.1 200 OK\n"
    434       "Content-Type: aaa1,   ccc1\n"
    435       "Content-Type: xxx1,    bbb1, ccc1\n"
    436       "Cache-Control: aaa2\n",
    437       "[aaa1,bbb1,ccc1,ccc1,xxx1,;aaa2,;]|content-type|cache-control",
    438       false
    439     },
    440     {
    441       "Checks the case that more than one headers have multiple values.",
    442       "HTTP/1.1 200 OK\n"
    443       "Content-Type: aaa1,   ccc1\n"
    444       "Cache-Control: ccc2    , bbb2\n"
    445       "Content-Type:  bbb1, ccc1\n"
    446       "Cache-Control: aaa2   \n",
    447       "[aaa1,bbb1,ccc1,ccc1,;aaa2,bbb2,ccc2,;]|content-type|cache-control",
    448       false
    449     },
    450     {
    451       "Checks the case that one of the requested headers is missing (Expires).",
    452       "HTTP/1.1 200 OK\n"
    453       "Content-Type: aaa1,   ccc1\n",
    454       "[aaa1,ccc1,;;]|content-type|expires",
    455       false
    456     },
    457     {
    458       "Checks the case that some of the requested headers have empty value.",
    459       "HTTP/1.1 200 OK\n"
    460       "Content-Type:   \n"
    461       "Cache-Control: \n",
    462       "[,;,;]|content-type|cache-control",
    463       false
    464     },
    465     {
    466       "Checks the case that all the requested headers are missing.",
    467       "HTTP/1.1 200 OK\n",
    468       "[;;]|content-type|expires",
    469       false
    470     },
    471     {
    472       "Checks the case that some headers are missing, some of them are empty.",
    473       "HTTP/1.1 200 OK\n"
    474       "Cache-Control: \n",
    475       "[;,;]|content-type|cache-control",
    476       false
    477     },
    478     {
    479       "Checks the case there is no requested header (header list is empty).",
    480       "HTTP/1.1 200 OK\n"
    481       "Chrome-Proxy: aut=aauutthh,bbbypas=0,aaxxx=xxx,bbbloc=1\n"
    482       "Content-Type: 1\n"
    483       "Cache-Control: 2\n",
    484       "[]",
    485       false
    486     },
    487     {
    488       "Checks tampered requested header values.",
    489       "HTTP/1.1 200 OK\n"
    490       "Content-Type: aaa1,   ccc1\n"
    491       "Cache-Control: ccc2    , bbb2\n",
    492       "[aaa1,bbb1,;bbb2,ccc2,;]|content-type|cache-control",
    493       true
    494     },
    495   };
    496 
    497   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
    498     ReplaceWithEncodedString(&test[i].received_fingerprint);
    499 
    500     std::string raw_headers(test[i].raw_header);
    501     HeadersToRaw(&raw_headers);
    502     scoped_refptr<net::HttpResponseHeaders> headers(
    503         new net::HttpResponseHeaders(raw_headers));
    504 
    505     DataReductionProxyTamperDetection tamper_detection(headers.get(), true, 0);
    506 
    507     bool tampered = tamper_detection.ValidateOtherHeaders(
    508         test[i].received_fingerprint);
    509 
    510     EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
    511   }
    512 }
    513 
    514 // Tests function ValidateContentLengthHeader.
    515 TEST_F(DataReductionProxyTamperDetectionTest, ContentLength) {
    516   struct {
    517     std::string label;
    518     std::string raw_header;
    519     std::string received_fingerprint;
    520     bool expected_tampered_with;
    521   } test[] = {
    522     {
    523       "Checks the case fingerprint matches received response.",
    524       "HTTP/1.1 200 OK\n"
    525       "Content-Length: 12345\n",
    526       "12345",
    527       false,
    528     },
    529     {
    530       "Checks case that response got modified.",
    531       "HTTP/1.1 200 OK\n"
    532       "Content-Length: 12345\n",
    533       "125",
    534       true,
    535     },
    536     {
    537       "Checks the case that the data reduction proxy has not sent"
    538       "Content-Length header.",
    539       "HTTP/1.1 200 OK\n"
    540       "Content-Length: 12345\n",
    541       "",
    542       false,
    543     },
    544     {
    545       "Checks the case that the data reduction proxy sends invalid"
    546       "Content-Length header.",
    547       "HTTP/1.1 200 OK\n"
    548       "Content-Length: 12345\n",
    549       "aaa",
    550       false,
    551     },
    552     {
    553       "Checks the case that the data reduction proxy sends invalid"
    554       "Content-Length header.",
    555       "HTTP/1.1 200 OK\n"
    556       "Content-Length: aaa\n",
    557       "aaa",
    558       false,
    559     },
    560     {
    561       "Checks the case that Content-Length header is missing at the Chromium"
    562       "client side.",
    563       "HTTP/1.1 200 OK\n",
    564       "123",
    565       false,
    566     },
    567     {
    568       "Checks the case that Content-Length header are missing at both end.",
    569       "HTTP/1.1 200 OK\n",
    570       "",
    571       false,
    572     },
    573   };
    574 
    575   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
    576     std::string raw_headers(test[i].raw_header);
    577     HeadersToRaw(&raw_headers);
    578     scoped_refptr<net::HttpResponseHeaders> headers(
    579         new net::HttpResponseHeaders(raw_headers));
    580 
    581     DataReductionProxyTamperDetection tamper_detection(headers.get(), true, 0);
    582 
    583     bool tampered = tamper_detection.ValidateContentLengthHeader(
    584         test[i].received_fingerprint);
    585 
    586     EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
    587   }
    588 }
    589 
    590 // Tests ValuesToSortedString function.
    591 TEST_F(DataReductionProxyTamperDetectionTest, ValuesToSortedString) {
    592   struct {
    593     std::string label;
    594     std::string input_values;
    595     std::string expected_output_string;
    596   } test[] = {
    597     {
    598       "Checks the correctness of sorting.",
    599       "3,2,1,",
    600       "1,2,3,",
    601     },
    602     {
    603       "Checks the case that there is an empty input vector.",
    604       "",
    605       "",
    606     },
    607     {
    608       "Checks the case that there is an empty string in the input vector.",
    609       ",",
    610       ",",
    611     },
    612   };
    613 
    614   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
    615     std::vector<std::string> input_values =
    616         StringsToVector(test[i].input_values);
    617     std::string output_string =
    618         DataReductionProxyTamperDetection::ValuesToSortedString(&input_values);
    619     EXPECT_EQ(output_string, test[i].expected_output_string) << test[i].label;
    620   }
    621 }
    622 
    623 // Tests GetHeaderValues function.
    624 TEST_F(DataReductionProxyTamperDetectionTest, GetHeaderValues) {
    625   struct {
    626     std::string label;
    627     std::string raw_header;
    628     std::string header_name;
    629     std::string expected_output_values;
    630   } test[] = {
    631     {
    632       "Checks the correctness of getting single line header.",
    633       "HTTP/1.1 200 OK\n"
    634       "test: 1, 2, 3\n",
    635       "test",
    636       "1,2,3,",
    637     },
    638     {
    639       "Checks the correctness of getting multiple lines header.",
    640       "HTTP/1.1 200 OK\n"
    641       "test: 1, 2, 3\n"
    642       "test: 4, 5, 6\n"
    643       "test: 7, 8, 9\n",
    644       "test",
    645       "1,2,3,4,5,6,7,8,9,",
    646     },
    647     {
    648       "Checks the correctness of getting missing header.",
    649       "HTTP/1.1 200 OK\n",
    650       "test",
    651       "",
    652     },
    653     {
    654       "Checks the correctness of getting empty header.",
    655       "HTTP/1.1 200 OK\n"
    656       "test:   \n",
    657       "test",
    658       ",",
    659     },
    660   };
    661 
    662   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
    663     std::string raw_headers(test[i].raw_header);
    664     HeadersToRaw(&raw_headers);
    665     scoped_refptr<net::HttpResponseHeaders> headers(
    666         new net::HttpResponseHeaders(raw_headers));
    667 
    668     std::vector<std::string> expected_output_values =
    669         StringsToVector(test[i].expected_output_values);
    670 
    671     std::vector<std::string> output_values =
    672         DataReductionProxyTamperDetection::GetHeaderValues(headers.get(),
    673                                                            test[i].header_name);
    674     EXPECT_EQ(expected_output_values, output_values) << test[i].label;
    675   }
    676 }
    677 
    678 // Tests main function DetectAndReport.
    679 TEST_F(DataReductionProxyTamperDetectionTest, DetectAndReport) {
    680   struct {
    681     std::string label;
    682     std::string raw_header;
    683     bool expected_tampered_with;
    684   } test[] = {
    685     {
    686       "Check no fingerprint added case.",
    687       "HTTP/1.1 200 OK\n"
    688       "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
    689       "Content-Length: 12345\n"
    690       "Chrome-Proxy: bypass=0\n",
    691       false,
    692     },
    693     {
    694       "Check the case Chrome-Proxy fingerprint doesn't match.",
    695       "HTTP/1.1 200 OK\n"
    696       "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
    697       "Content-Length: 12345\n"
    698       "header1: header_1\n"
    699       "header2: header_2\n"
    700       "header3: header_3\n"
    701       "Chrome-Proxy: fcl=12345, "
    702       "foh=[header_1,;header_2,;header_3,;]|header1|header2|header3,fvia=0,"
    703       "fcp=abc\n",
    704       true,
    705     },
    706     {
    707       "Check the case response matches the fingerprint completely.",
    708       "HTTP/1.1 200 OK\n"
    709       "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
    710       "Content-Length: 12345\n"
    711       "header1: header_1\n"
    712       "header2: header_2\n"
    713       "header3: header_3\n"
    714       "Chrome-Proxy: fcl=12345, "
    715       "foh=[header_1,;header_2,;header_3,;]|header1|header2|header3,"
    716       "fvia=0, fcp=[fcl=12345,foh=[header_1,;header_2,;header_3,;]"
    717       "|header1|header2|header3,fvia=0,]\n",
    718       false,
    719     },
    720     {
    721       "Check the case that Content-Length doesn't match.",
    722       "HTTP/1.1 200 OK\n"
    723       "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
    724       "Content-Length: 0\n"
    725       "header1: header_1\n"
    726       "header2: header_2\n"
    727       "header3: header_3\n"
    728       "Chrome-Proxy: fcl=12345, "
    729       "foh=[header_1,;header_2,;header_3,;]|header1|header2|header3, fvia=0, "
    730       "fcp=[fcl=12345,foh=[header_1,;header_2,;header_3,;]|"
    731       "header1|header2|header3,fvia=0,]\n",
    732       true,
    733     },
    734   };
    735 
    736   InitEnv();
    737 
    738   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
    739     std::string raw_headers(test[i].raw_header);
    740     ReplaceWithEncodedString(&raw_headers);
    741     HeadersToRaw(&raw_headers);
    742     scoped_refptr<net::HttpResponseHeaders> headers(
    743         new net::HttpResponseHeaders(raw_headers));
    744 
    745     EXPECT_EQ(
    746         test[i].expected_tampered_with,
    747         DataReductionProxyTamperDetection::DetectAndReport(headers.get(), true))
    748         << test[i].label;
    749   }
    750 }
    751 
    752 } // namespace
    753