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 "third_party/blink/renderer/platform/loader/subresource_integrity.h"
6 
7 #include "base/memory/scoped_refptr.h"
8 #include "base/stl_util.h"
9 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/blink/renderer/platform/crypto.h"
12 #include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
13 #include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
14 #include "third_party/blink/renderer/platform/loader/fetch/resource.h"
15 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
16 #include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
17 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
18 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
19 #include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
20 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
21 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
22 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
23 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
24 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
25 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
26 #include "third_party/blink/renderer/platform/wtf/vector.h"
27 
28 #include <algorithm>
29 
30 namespace blink {
31 
32 static const char kBasicScript[] = "alert('test');";
33 static const char kSha256Integrity[] =
34     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=";
35 static const char kSha256IntegrityLenientSyntax[] =
36     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=";
37 static const char kSha256IntegrityWithEmptyOption[] =
38     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?";
39 static const char kSha256IntegrityWithOption[] =
40     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?foo=bar";
41 static const char kSha256IntegrityWithOptions[] =
42     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?foo=bar?baz=foz";
43 static const char kSha256IntegrityWithMimeOption[] =
44     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?ct=application/"
45     "javascript";
46 static const char kSha384Integrity[] =
47     "sha384-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
48 static const char kSha512Integrity[] =
49     "sha512-TXkJw18PqlVlEUXXjeXbGetop1TKB3wYQIp1_"
50     "ihxCOFGUfG9TYOaA1MlkpTAqSV6yaevLO8Tj5pgH1JmZ--ItA==";
51 static const char kSha384IntegrityLabeledAs256[] =
52     "sha256-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
53 static const char kSha256AndSha384Integrities[] =
54     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4= "
55     "sha384-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
56 static const char kBadSha256AndGoodSha384Integrities[] =
57     "sha256-deadbeef "
58     "sha384-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
59 static const char kGoodSha256AndBadSha384Integrities[] =
60     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4= sha384-deadbeef";
61 static const char kBadSha256AndBadSha384Integrities[] =
62     "sha256-deadbeef sha384-deadbeef";
63 static const char kUnsupportedHashFunctionIntegrity[] =
64     "sha1-JfLW308qMPKfb4DaHpUBEESwuPc=";
65 
66 class SubresourceIntegrityTest : public testing::Test {
67  public:
SubresourceIntegrityTest()68   SubresourceIntegrityTest()
69       : sec_url("https://example.test:443"),
70         insec_url("http://example.test:80"),
71         context(MakeGarbageCollected<MockFetchContext>()) {}
72 
73  protected:
Features() const74   SubresourceIntegrity::IntegrityFeatures Features() const {
75     return RuntimeEnabledFeatures::SignatureBasedIntegrityEnabledByRuntimeFlag()
76                ? SubresourceIntegrity::IntegrityFeatures::kSignatures
77                : SubresourceIntegrity::IntegrityFeatures::kDefault;
78   }
79 
ExpectAlgorithm(const String & text,IntegrityAlgorithm expected_algorithm)80   void ExpectAlgorithm(const String& text,
81                        IntegrityAlgorithm expected_algorithm) {
82     Vector<UChar> characters;
83     text.AppendTo(characters);
84     const UChar* position = characters.data();
85     const UChar* end = characters.end();
86     IntegrityAlgorithm algorithm;
87 
88     EXPECT_EQ(SubresourceIntegrity::kAlgorithmValid,
89               SubresourceIntegrity::ParseAttributeAlgorithm(
90                   position, end, Features(), algorithm));
91     EXPECT_EQ(expected_algorithm, algorithm);
92     EXPECT_EQ(end, position);
93   }
94 
ExpectAlgorithmFailure(const String & text,SubresourceIntegrity::AlgorithmParseResult expected_result)95   void ExpectAlgorithmFailure(
96       const String& text,
97       SubresourceIntegrity::AlgorithmParseResult expected_result) {
98     Vector<UChar> characters;
99     text.AppendTo(characters);
100     const UChar* position = characters.data();
101     const UChar* begin = characters.data();
102     const UChar* end = characters.end();
103     IntegrityAlgorithm algorithm;
104 
105     EXPECT_EQ(expected_result, SubresourceIntegrity::ParseAttributeAlgorithm(
106                                    position, end, Features(), algorithm));
107     EXPECT_EQ(begin, position);
108   }
109 
ExpectDigest(const String & text,const char * expected_digest)110   void ExpectDigest(const String& text, const char* expected_digest) {
111     Vector<UChar> characters;
112     text.AppendTo(characters);
113     const UChar* position = characters.data();
114     const UChar* end = characters.end();
115     String digest;
116 
117     EXPECT_TRUE(SubresourceIntegrity::ParseDigest(position, end, digest));
118     EXPECT_EQ(expected_digest, digest);
119   }
120 
ExpectDigestFailure(const String & text)121   void ExpectDigestFailure(const String& text) {
122     Vector<UChar> characters;
123     text.AppendTo(characters);
124     const UChar* position = characters.data();
125     const UChar* end = characters.end();
126     String digest;
127 
128     EXPECT_FALSE(SubresourceIntegrity::ParseDigest(position, end, digest));
129     EXPECT_TRUE(digest.IsEmpty());
130   }
131 
ExpectParse(const char * integrity_attribute,const char * expected_digest,IntegrityAlgorithm expected_algorithm)132   void ExpectParse(const char* integrity_attribute,
133                    const char* expected_digest,
134                    IntegrityAlgorithm expected_algorithm) {
135     IntegrityMetadataSet metadata_set;
136 
137     EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
138               SubresourceIntegrity::ParseIntegrityAttribute(
139                   integrity_attribute, Features(), metadata_set));
140     EXPECT_EQ(1u, metadata_set.size());
141     if (metadata_set.size() > 0) {
142       IntegrityMetadata metadata = *metadata_set.begin();
143       EXPECT_EQ(expected_digest, metadata.Digest());
144       EXPECT_EQ(expected_algorithm, metadata.Algorithm());
145     }
146   }
147 
ExpectParseMultipleHashes(const char * integrity_attribute,const IntegrityMetadata expected_metadata_array[],size_t expected_metadata_array_size)148   void ExpectParseMultipleHashes(
149       const char* integrity_attribute,
150       const IntegrityMetadata expected_metadata_array[],
151       size_t expected_metadata_array_size) {
152     IntegrityMetadataSet expected_metadata_set;
153     for (size_t i = 0; i < expected_metadata_array_size; i++) {
154       expected_metadata_set.insert(expected_metadata_array[i].ToPair());
155     }
156     IntegrityMetadataSet metadata_set;
157     EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
158               SubresourceIntegrity::ParseIntegrityAttribute(
159                   integrity_attribute, Features(), metadata_set));
160     EXPECT_TRUE(
161         IntegrityMetadata::SetsEqual(expected_metadata_set, metadata_set));
162   }
163 
ExpectParseFailure(const char * integrity_attribute)164   void ExpectParseFailure(const char* integrity_attribute) {
165     IntegrityMetadataSet metadata_set;
166 
167     EXPECT_EQ(SubresourceIntegrity::kIntegrityParseNoValidResult,
168               SubresourceIntegrity::ParseIntegrityAttribute(
169                   integrity_attribute, Features(), metadata_set));
170   }
171 
ExpectEmptyParseResult(const char * integrity_attribute)172   void ExpectEmptyParseResult(const char* integrity_attribute) {
173     IntegrityMetadataSet metadata_set;
174 
175     EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
176               SubresourceIntegrity::ParseIntegrityAttribute(
177                   integrity_attribute, Features(), metadata_set));
178     EXPECT_EQ(0u, metadata_set.size());
179   }
180 
181   enum ServiceWorkerMode {
182     kNoServiceWorker,
183     kSWOpaqueResponse,
184     kSWClearResponse
185   };
186 
187   enum Expectation { kIntegritySuccess, kIntegrityFailure };
188 
189   struct TestCase {
190     const KURL url;
191     network::mojom::RequestMode request_mode;
192     network::mojom::FetchResponseType response_type;
193     const Expectation expectation;
194   };
195 
CheckExpectedIntegrity(const char * integrity,const TestCase & test)196   void CheckExpectedIntegrity(const char* integrity, const TestCase& test) {
197     CheckExpectedIntegrity(integrity, test, test.expectation);
198   }
199 
200   // Allows to overwrite the test expectation for cases that are always expected
201   // to fail:
CheckExpectedIntegrity(const char * integrity,const TestCase & test,Expectation expectation)202   void CheckExpectedIntegrity(const char* integrity,
203                               const TestCase& test,
204                               Expectation expectation) {
205     IntegrityMetadataSet metadata_set;
206     EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
207               SubresourceIntegrity::ParseIntegrityAttribute(
208                   String(integrity), Features(), metadata_set));
209 
210     SubresourceIntegrity::ReportInfo report_info;
211     EXPECT_EQ(expectation == kIntegritySuccess,
212               SubresourceIntegrity::CheckSubresourceIntegrity(
213                   metadata_set, kBasicScript, strlen(kBasicScript), test.url,
214                   *CreateTestResource(test.url, test.request_mode,
215                                       test.response_type),
216                   report_info));
217   }
218 
CreateTestResource(const KURL & url,network::mojom::RequestMode request_mode,network::mojom::FetchResponseType response_type)219   Resource* CreateTestResource(
220       const KURL& url,
221       network::mojom::RequestMode request_mode,
222       network::mojom::FetchResponseType response_type) {
223     ResourceRequest request;
224     request.SetUrl(url);
225     request.SetMode(request_mode);
226     request.SetRequestorOrigin(SecurityOrigin::CreateUniqueOpaque());
227     Resource* resource =
228         RawResource::CreateForTest(request, ResourceType::kRaw);
229 
230     ResourceResponse response(url);
231     response.SetHttpStatusCode(200);
232     response.SetType(response_type);
233 
234     resource->SetResponse(response);
235     return resource;
236   }
237 
238   KURL sec_url;
239   KURL insec_url;
240 
241   Persistent<MockFetchContext> context;
242 };
243 
244 // Test the prioritization (i.e. selecting the "strongest" algorithm.
245 // This effectively tests the definition of IntegrityAlgorithm in
246 // IntegrityMetadata. The test is here, because SubresourceIntegrity is the
247 // class that relies on this working as expected.)
TEST_F(SubresourceIntegrityTest,Prioritization)248 TEST_F(SubresourceIntegrityTest, Prioritization) {
249   // Check that each algorithm is it's own "strongest".
250   EXPECT_EQ(
251       IntegrityAlgorithm::kSha256,
252       std::max({IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha256}));
253   EXPECT_EQ(
254       IntegrityAlgorithm::kSha384,
255       std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha384}));
256 
257   EXPECT_EQ(
258       IntegrityAlgorithm::kSha512,
259       std::max({IntegrityAlgorithm::kSha512, IntegrityAlgorithm::kSha512}));
260 
261   // Check a mix of algorithms.
262   EXPECT_EQ(IntegrityAlgorithm::kSha384,
263             std::max({IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha384,
264                       IntegrityAlgorithm::kSha256}));
265   EXPECT_EQ(IntegrityAlgorithm::kSha512,
266             std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha512,
267                       IntegrityAlgorithm::kSha256}));
268 }
269 
TEST_F(SubresourceIntegrityTest,ParseAlgorithm)270 TEST_F(SubresourceIntegrityTest, ParseAlgorithm) {
271   ExpectAlgorithm("sha256-", IntegrityAlgorithm::kSha256);
272   ExpectAlgorithm("sha384-", IntegrityAlgorithm::kSha384);
273   ExpectAlgorithm("sha512-", IntegrityAlgorithm::kSha512);
274   ExpectAlgorithm("sha-256-", IntegrityAlgorithm::kSha256);
275   ExpectAlgorithm("sha-384-", IntegrityAlgorithm::kSha384);
276   ExpectAlgorithm("sha-512-", IntegrityAlgorithm::kSha512);
277 
278   ScopedSignatureBasedIntegrityForTest signature_based_integrity(false);
279 
280   ExpectAlgorithmFailure("sha1-", SubresourceIntegrity::kAlgorithmUnknown);
281   ExpectAlgorithmFailure("sha-1-", SubresourceIntegrity::kAlgorithmUnknown);
282   ExpectAlgorithmFailure("foobarsha256-",
283                          SubresourceIntegrity::kAlgorithmUnknown);
284   ExpectAlgorithmFailure("foobar-", SubresourceIntegrity::kAlgorithmUnknown);
285   ExpectAlgorithmFailure("-", SubresourceIntegrity::kAlgorithmUnknown);
286 
287   ExpectAlgorithmFailure("sha256", SubresourceIntegrity::kAlgorithmUnparsable);
288   ExpectAlgorithmFailure("", SubresourceIntegrity::kAlgorithmUnparsable);
289 }
290 
TEST_F(SubresourceIntegrityTest,ParseDigest)291 TEST_F(SubresourceIntegrityTest, ParseDigest) {
292   ExpectDigest("abcdefg", "abcdefg");
293   ExpectDigest("abcdefg?", "abcdefg");
294   ExpectDigest("ab+de/g", "ab+de/g");
295   ExpectDigest("ab-de_g", "ab+de/g");
296 
297   ExpectDigestFailure("?");
298   ExpectDigestFailure("&&&foobar&&&");
299   ExpectDigestFailure("\x01\x02\x03\x04");
300 }
301 
302 //
303 // End-to-end parsing tests.
304 //
305 
TEST_F(SubresourceIntegrityTest,Parsing)306 TEST_F(SubresourceIntegrityTest, Parsing) {
307   ExpectParseFailure("not_really_a_valid_anything");
308   ExpectParseFailure("sha256-&&&foobar&&&");
309   ExpectParseFailure("sha256-\x01\x02\x03\x04");
310   ExpectParseFailure("sha256-!!! sha256-!!!");
311 
312   ExpectEmptyParseResult("foobar:///sha256-abcdefg");
313   ExpectEmptyParseResult("ni://sha256-abcdefg");
314   ExpectEmptyParseResult("ni:///sha256-abcdefg");
315   ExpectEmptyParseResult("notsha256atall-abcdefg");
316 
317   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
318               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
319               IntegrityAlgorithm::kSha256);
320 
321   ExpectParse("sha-256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
322               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
323               IntegrityAlgorithm::kSha256);
324 
325   ExpectParse("     sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=     ",
326               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
327               IntegrityAlgorithm::kSha256);
328 
329   ExpectParse(
330       "sha384-XVVXBGoYw6AJOh9J-Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup_tA1v5GPr",
331       "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
332       IntegrityAlgorithm::kSha384);
333 
334   ExpectParse(
335       "sha-384-XVVXBGoYw6AJOh9J_Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup_"
336       "tA1v5GPr",
337       "XVVXBGoYw6AJOh9J/Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
338       IntegrityAlgorithm::kSha384);
339 
340   ExpectParse(
341       "sha512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
342       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
343       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
344       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
345       IntegrityAlgorithm::kSha512);
346 
347   ExpectParse(
348       "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
349       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
350       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
351       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
352       IntegrityAlgorithm::kSha512);
353 
354   ExpectParse(
355       "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
356       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/javascript",
357       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
358       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
359       IntegrityAlgorithm::kSha512);
360 
361   ExpectParse(
362       "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
363       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/xhtml+xml",
364       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
365       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
366       IntegrityAlgorithm::kSha512);
367 
368   ExpectParse(
369       "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
370       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?foo=bar?ct=application/xhtml+xml",
371       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
372       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
373       IntegrityAlgorithm::kSha512);
374 
375   ExpectParse(
376       "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
377       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/xhtml+xml?foo=bar",
378       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
379       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
380       IntegrityAlgorithm::kSha512);
381 
382   ExpectParse(
383       "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
384       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?baz=foz?ct=application/"
385       "xhtml+xml?foo=bar",
386       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
387       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
388       IntegrityAlgorithm::kSha512);
389 
390   ExpectParseMultipleHashes("", nullptr, 0);
391   ExpectParseMultipleHashes("    ", nullptr, 0);
392 
393   const IntegrityMetadata valid_sha384_and_sha512[] = {
394       IntegrityMetadata(
395           "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
396           IntegrityAlgorithm::kSha384),
397       IntegrityMetadata("tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
398                         "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
399                         IntegrityAlgorithm::kSha512),
400   };
401   ExpectParseMultipleHashes(
402       "sha384-XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr "
403       "sha512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
404       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
405       valid_sha384_and_sha512, base::size(valid_sha384_and_sha512));
406 
407   const IntegrityMetadata valid_sha256_and_sha256[] = {
408       IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
409                         IntegrityAlgorithm::kSha256),
410       IntegrityMetadata("deadbeef", IntegrityAlgorithm::kSha256),
411   };
412   ExpectParseMultipleHashes(
413       "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= sha256-deadbeef",
414       valid_sha256_and_sha256, base::size(valid_sha256_and_sha256));
415 
416   const IntegrityMetadata valid_sha256_and_invalid_sha256[] = {
417       IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
418                         IntegrityAlgorithm::kSha256),
419   };
420   ExpectParseMultipleHashes(
421       "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= sha256-!!!!",
422       valid_sha256_and_invalid_sha256,
423       base::size(valid_sha256_and_invalid_sha256));
424 
425   const IntegrityMetadata invalid_sha256_and_valid_sha256[] = {
426       IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
427                         IntegrityAlgorithm::kSha256),
428   };
429   ExpectParseMultipleHashes(
430       "sha256-!!! sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
431       invalid_sha256_and_valid_sha256,
432       base::size(invalid_sha256_and_valid_sha256));
433 
434   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar",
435               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
436               IntegrityAlgorithm::kSha256);
437 
438   ExpectParse(
439       "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?baz=foz",
440       "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
441       IntegrityAlgorithm::kSha256);
442 
443   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?",
444               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
445               IntegrityAlgorithm::kSha256);
446   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar",
447               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
448               IntegrityAlgorithm::kSha256);
449   ExpectParse(
450       "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?baz=foz",
451       "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
452       IntegrityAlgorithm::kSha256);
453   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo",
454               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
455               IntegrityAlgorithm::kSha256);
456   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?",
457               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
458               IntegrityAlgorithm::kSha256);
459   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo:bar",
460               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
461               IntegrityAlgorithm::kSha256);
462 }
463 
TEST_F(SubresourceIntegrityTest,ParsingBase64)464 TEST_F(SubresourceIntegrityTest, ParsingBase64) {
465   ExpectParse(
466       "sha384-XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
467       "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
468       IntegrityAlgorithm::kSha384);
469 }
470 
471 // Tests that SubresourceIntegrity::CheckSubresourceIntegrity behaves correctly
472 // when faced with secure or insecure origins, same origin and cross origin
473 // requests, successful and failing CORS checks as well as when the response was
474 // handled by a service worker.
TEST_F(SubresourceIntegrityTest,OriginIntegrity)475 TEST_F(SubresourceIntegrityTest, OriginIntegrity) {
476   using network::mojom::FetchResponseType;
477   using network::mojom::RequestMode;
478   constexpr auto kOk = kIntegritySuccess;
479   constexpr auto kFail = kIntegrityFailure;
480   const KURL& url = sec_url;
481 
482   const TestCase cases[] = {
483       // FetchResponseType::kError never arrives because it is a loading error.
484       {url, RequestMode::kNoCors, FetchResponseType::kBasic, kOk},
485       {url, RequestMode::kNoCors, FetchResponseType::kCors, kOk},
486       {url, RequestMode::kNoCors, FetchResponseType::kDefault, kOk},
487       {url, RequestMode::kNoCors, FetchResponseType::kOpaque, kFail},
488       {url, RequestMode::kNoCors, FetchResponseType::kOpaqueRedirect, kFail},
489 
490       // FetchResponseType::kError never arrives because it is a loading error.
491       // FetchResponseType::kOpaque and FetchResponseType::kOpaqueResponse
492       // never arrives: even when service worker is involved, it's handled as
493       // an error.
494       {url, RequestMode::kCors, FetchResponseType::kBasic, kOk},
495       {url, RequestMode::kCors, FetchResponseType::kCors, kOk},
496       {url, RequestMode::kCors, FetchResponseType::kDefault, kOk},
497   };
498 
499   for (const auto& test : cases) {
500     SCOPED_TRACE(testing::Message()
501                  << ", target: " << test.url.BaseAsString()
502                  << ", request mode: " << test.request_mode
503                  << ", response type: " << test.response_type
504                  << ", expected result: "
505                  << (test.expectation == kIntegritySuccess ? "integrity"
506                                                            : "failure"));
507 
508     // Verify basic sha256, sha384, and sha512 integrity checks.
509     CheckExpectedIntegrity(kSha256Integrity, test);
510     CheckExpectedIntegrity(kSha256IntegrityLenientSyntax, test);
511     CheckExpectedIntegrity(kSha384Integrity, test);
512     CheckExpectedIntegrity(kSha512Integrity, test);
513 
514     // Verify multiple hashes in an attribute.
515     CheckExpectedIntegrity(kSha256AndSha384Integrities, test);
516     CheckExpectedIntegrity(kBadSha256AndGoodSha384Integrities, test);
517 
518     // Unsupported hash functions should succeed.
519     CheckExpectedIntegrity(kUnsupportedHashFunctionIntegrity, test);
520 
521     // Options should be ignored
522     CheckExpectedIntegrity(kSha256IntegrityWithEmptyOption, test);
523     CheckExpectedIntegrity(kSha256IntegrityWithOption, test);
524     CheckExpectedIntegrity(kSha256IntegrityWithOptions, test);
525     CheckExpectedIntegrity(kSha256IntegrityWithMimeOption, test);
526 
527     // The following tests are expected to fail in every scenario:
528 
529     // The hash label must match the hash value.
530     CheckExpectedIntegrity(kSha384IntegrityLabeledAs256, test,
531                            Expectation::kIntegrityFailure);
532 
533     // With multiple values, at least one must match, and it must be the
534     // strongest hash algorithm.
535     CheckExpectedIntegrity(kGoodSha256AndBadSha384Integrities, test,
536                            Expectation::kIntegrityFailure);
537     CheckExpectedIntegrity(kBadSha256AndBadSha384Integrities, test,
538                            Expectation::kIntegrityFailure);
539   }
540 }
541 
TEST_F(SubresourceIntegrityTest,FindBestAlgorithm)542 TEST_F(SubresourceIntegrityTest, FindBestAlgorithm) {
543   // Each algorithm is its own best.
544   EXPECT_EQ(IntegrityAlgorithm::kSha256,
545             SubresourceIntegrity::FindBestAlgorithm(
546                 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256}})));
547   EXPECT_EQ(IntegrityAlgorithm::kSha384,
548             SubresourceIntegrity::FindBestAlgorithm(
549                 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha384}})));
550   EXPECT_EQ(IntegrityAlgorithm::kSha512,
551             SubresourceIntegrity::FindBestAlgorithm(
552                 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha512}})));
553 
554   // Test combinations of multiple algorithms.
555   EXPECT_EQ(IntegrityAlgorithm::kSha384,
556             SubresourceIntegrity::FindBestAlgorithm(
557                 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
558                                       {"", IntegrityAlgorithm::kSha384}})));
559   EXPECT_EQ(IntegrityAlgorithm::kSha512,
560             SubresourceIntegrity::FindBestAlgorithm(
561                 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
562                                       {"", IntegrityAlgorithm::kSha512},
563                                       {"", IntegrityAlgorithm::kSha384}})));
564 }
565 
566 }  // namespace blink
567