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