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 EXPECT_EQ(
261 IntegrityAlgorithm::kEd25519,
262 std::max({IntegrityAlgorithm::kEd25519, IntegrityAlgorithm::kEd25519}));
263
264 // Check a mix of algorithms.
265 EXPECT_EQ(IntegrityAlgorithm::kSha384,
266 std::max({IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha384,
267 IntegrityAlgorithm::kSha256}));
268 EXPECT_EQ(IntegrityAlgorithm::kSha512,
269 std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha512,
270 IntegrityAlgorithm::kSha256}));
271 EXPECT_EQ(
272 IntegrityAlgorithm::kEd25519,
273 std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha512,
274 IntegrityAlgorithm::kEd25519, IntegrityAlgorithm::kSha512,
275 IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha512}));
276 }
277
TEST_F(SubresourceIntegrityTest,ParseAlgorithm)278 TEST_F(SubresourceIntegrityTest, ParseAlgorithm) {
279 ExpectAlgorithm("sha256-", IntegrityAlgorithm::kSha256);
280 ExpectAlgorithm("sha384-", IntegrityAlgorithm::kSha384);
281 ExpectAlgorithm("sha512-", IntegrityAlgorithm::kSha512);
282 ExpectAlgorithm("sha-256-", IntegrityAlgorithm::kSha256);
283 ExpectAlgorithm("sha-384-", IntegrityAlgorithm::kSha384);
284 ExpectAlgorithm("sha-512-", IntegrityAlgorithm::kSha512);
285
286 {
287 ScopedSignatureBasedIntegrityForTest signature_based_integrity(true);
288 ExpectAlgorithm("ed25519-", IntegrityAlgorithm::kEd25519);
289 }
290 ScopedSignatureBasedIntegrityForTest signature_based_integrity(false);
291 ExpectAlgorithmFailure("ed25519-", SubresourceIntegrity::kAlgorithmUnknown);
292
293 ExpectAlgorithmFailure("sha1-", SubresourceIntegrity::kAlgorithmUnknown);
294 ExpectAlgorithmFailure("sha-1-", SubresourceIntegrity::kAlgorithmUnknown);
295 ExpectAlgorithmFailure("foobarsha256-",
296 SubresourceIntegrity::kAlgorithmUnknown);
297 ExpectAlgorithmFailure("foobar-", SubresourceIntegrity::kAlgorithmUnknown);
298 ExpectAlgorithmFailure("-", SubresourceIntegrity::kAlgorithmUnknown);
299 ExpectAlgorithmFailure("ed-25519-", SubresourceIntegrity::kAlgorithmUnknown);
300 ExpectAlgorithmFailure("ed25518-", SubresourceIntegrity::kAlgorithmUnknown);
301
302 ExpectAlgorithmFailure("sha256", SubresourceIntegrity::kAlgorithmUnparsable);
303 ExpectAlgorithmFailure("", SubresourceIntegrity::kAlgorithmUnparsable);
304 }
305
TEST_F(SubresourceIntegrityTest,ParseDigest)306 TEST_F(SubresourceIntegrityTest, ParseDigest) {
307 ExpectDigest("abcdefg", "abcdefg");
308 ExpectDigest("abcdefg?", "abcdefg");
309 ExpectDigest("ab+de/g", "ab+de/g");
310 ExpectDigest("ab-de_g", "ab+de/g");
311
312 ExpectDigestFailure("?");
313 ExpectDigestFailure("&&&foobar&&&");
314 ExpectDigestFailure("\x01\x02\x03\x04");
315 }
316
317 //
318 // End-to-end parsing tests.
319 //
320
TEST_F(SubresourceIntegrityTest,Parsing)321 TEST_F(SubresourceIntegrityTest, Parsing) {
322 ExpectParseFailure("not_really_a_valid_anything");
323 ExpectParseFailure("sha256-&&&foobar&&&");
324 ExpectParseFailure("sha256-\x01\x02\x03\x04");
325 ExpectParseFailure("sha256-!!! sha256-!!!");
326
327 ExpectEmptyParseResult("foobar:///sha256-abcdefg");
328 ExpectEmptyParseResult("ni://sha256-abcdefg");
329 ExpectEmptyParseResult("ni:///sha256-abcdefg");
330 ExpectEmptyParseResult("notsha256atall-abcdefg");
331
332 ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
333 "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
334 IntegrityAlgorithm::kSha256);
335
336 ExpectParse("sha-256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
337 "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
338 IntegrityAlgorithm::kSha256);
339
340 ExpectParse(" sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= ",
341 "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
342 IntegrityAlgorithm::kSha256);
343
344 ExpectParse(
345 "sha384-XVVXBGoYw6AJOh9J-Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup_tA1v5GPr",
346 "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
347 IntegrityAlgorithm::kSha384);
348
349 ExpectParse(
350 "sha-384-XVVXBGoYw6AJOh9J_Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup_"
351 "tA1v5GPr",
352 "XVVXBGoYw6AJOh9J/Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
353 IntegrityAlgorithm::kSha384);
354
355 ExpectParse(
356 "sha512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
357 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
358 "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
359 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
360 IntegrityAlgorithm::kSha512);
361
362 ExpectParse(
363 "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
364 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
365 "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
366 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
367 IntegrityAlgorithm::kSha512);
368
369 ExpectParse(
370 "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
371 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/javascript",
372 "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
373 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
374 IntegrityAlgorithm::kSha512);
375
376 ExpectParse(
377 "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
378 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/xhtml+xml",
379 "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
380 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
381 IntegrityAlgorithm::kSha512);
382
383 ExpectParse(
384 "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
385 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?foo=bar?ct=application/xhtml+xml",
386 "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
387 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
388 IntegrityAlgorithm::kSha512);
389
390 ExpectParse(
391 "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
392 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/xhtml+xml?foo=bar",
393 "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
394 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
395 IntegrityAlgorithm::kSha512);
396
397 ExpectParse(
398 "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
399 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?baz=foz?ct=application/"
400 "xhtml+xml?foo=bar",
401 "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
402 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
403 IntegrityAlgorithm::kSha512);
404
405 ExpectParseMultipleHashes("", nullptr, 0);
406 ExpectParseMultipleHashes(" ", nullptr, 0);
407
408 const IntegrityMetadata valid_sha384_and_sha512[] = {
409 IntegrityMetadata(
410 "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
411 IntegrityAlgorithm::kSha384),
412 IntegrityMetadata("tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
413 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
414 IntegrityAlgorithm::kSha512),
415 };
416 ExpectParseMultipleHashes(
417 "sha384-XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr "
418 "sha512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
419 "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
420 valid_sha384_and_sha512, base::size(valid_sha384_and_sha512));
421
422 const IntegrityMetadata valid_sha256_and_sha256[] = {
423 IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
424 IntegrityAlgorithm::kSha256),
425 IntegrityMetadata("deadbeef", IntegrityAlgorithm::kSha256),
426 };
427 ExpectParseMultipleHashes(
428 "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= sha256-deadbeef",
429 valid_sha256_and_sha256, base::size(valid_sha256_and_sha256));
430
431 const IntegrityMetadata valid_sha256_and_invalid_sha256[] = {
432 IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
433 IntegrityAlgorithm::kSha256),
434 };
435 ExpectParseMultipleHashes(
436 "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= sha256-!!!!",
437 valid_sha256_and_invalid_sha256,
438 base::size(valid_sha256_and_invalid_sha256));
439
440 const IntegrityMetadata invalid_sha256_and_valid_sha256[] = {
441 IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
442 IntegrityAlgorithm::kSha256),
443 };
444 ExpectParseMultipleHashes(
445 "sha256-!!! sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
446 invalid_sha256_and_valid_sha256,
447 base::size(invalid_sha256_and_valid_sha256));
448
449 ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar",
450 "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
451 IntegrityAlgorithm::kSha256);
452
453 ExpectParse(
454 "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?baz=foz",
455 "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
456 IntegrityAlgorithm::kSha256);
457
458 ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?",
459 "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
460 IntegrityAlgorithm::kSha256);
461 ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar",
462 "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
463 IntegrityAlgorithm::kSha256);
464 ExpectParse(
465 "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?baz=foz",
466 "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
467 IntegrityAlgorithm::kSha256);
468 ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo",
469 "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
470 IntegrityAlgorithm::kSha256);
471 ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?",
472 "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
473 IntegrityAlgorithm::kSha256);
474 ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo:bar",
475 "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
476 IntegrityAlgorithm::kSha256);
477
478 {
479 ScopedSignatureBasedIntegrityForTest signature_based_integrity(false);
480 ExpectEmptyParseResult("ed25519-xxxx");
481 ExpectEmptyParseResult(
482 "ed25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=");
483 }
484
485 ScopedSignatureBasedIntegrityForTest signature_based_integrity(true);
486 ExpectParse("ed25519-xxxx", "xxxx", IntegrityAlgorithm::kEd25519);
487 ExpectParse("ed25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=",
488 "qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=",
489 IntegrityAlgorithm::kEd25519);
490 ExpectParse("ed25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=?foo=bar",
491 "qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=",
492 IntegrityAlgorithm::kEd25519);
493 ExpectEmptyParseResult("ed-25519-xxx");
494 ExpectEmptyParseResult(
495 "ed-25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=");
496 }
497
TEST_F(SubresourceIntegrityTest,ParsingBase64)498 TEST_F(SubresourceIntegrityTest, ParsingBase64) {
499 ExpectParse(
500 "sha384-XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
501 "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
502 IntegrityAlgorithm::kSha384);
503 }
504
505 // Tests that SubresourceIntegrity::CheckSubresourceIntegrity behaves correctly
506 // when faced with secure or insecure origins, same origin and cross origin
507 // requests, successful and failing CORS checks as well as when the response was
508 // handled by a service worker.
TEST_F(SubresourceIntegrityTest,OriginIntegrity)509 TEST_F(SubresourceIntegrityTest, OriginIntegrity) {
510 using network::mojom::FetchResponseType;
511 using network::mojom::RequestMode;
512 constexpr auto kOk = kIntegritySuccess;
513 constexpr auto kFail = kIntegrityFailure;
514 const KURL& url = sec_url;
515
516 const TestCase cases[] = {
517 // FetchResponseType::kError never arrives because it is a loading error.
518 {url, RequestMode::kNoCors, FetchResponseType::kBasic, kOk},
519 {url, RequestMode::kNoCors, FetchResponseType::kCors, kOk},
520 {url, RequestMode::kNoCors, FetchResponseType::kDefault, kOk},
521 {url, RequestMode::kNoCors, FetchResponseType::kOpaque, kFail},
522 {url, RequestMode::kNoCors, FetchResponseType::kOpaqueRedirect, kFail},
523
524 // FetchResponseType::kError never arrives because it is a loading error.
525 // FetchResponseType::kOpaque and FetchResponseType::kOpaqueResponse
526 // never arrives: even when service worker is involved, it's handled as
527 // an error.
528 {url, RequestMode::kCors, FetchResponseType::kBasic, kOk},
529 {url, RequestMode::kCors, FetchResponseType::kCors, kOk},
530 {url, RequestMode::kCors, FetchResponseType::kDefault, kOk},
531 };
532
533 for (const auto& test : cases) {
534 SCOPED_TRACE(testing::Message()
535 << ", target: " << test.url.BaseAsString()
536 << ", request mode: " << test.request_mode
537 << ", response type: " << test.response_type
538 << ", expected result: "
539 << (test.expectation == kIntegritySuccess ? "integrity"
540 : "failure"));
541
542 // Verify basic sha256, sha384, and sha512 integrity checks.
543 CheckExpectedIntegrity(kSha256Integrity, test);
544 CheckExpectedIntegrity(kSha256IntegrityLenientSyntax, test);
545 CheckExpectedIntegrity(kSha384Integrity, test);
546 CheckExpectedIntegrity(kSha512Integrity, test);
547
548 // Verify multiple hashes in an attribute.
549 CheckExpectedIntegrity(kSha256AndSha384Integrities, test);
550 CheckExpectedIntegrity(kBadSha256AndGoodSha384Integrities, test);
551
552 // Unsupported hash functions should succeed.
553 CheckExpectedIntegrity(kUnsupportedHashFunctionIntegrity, test);
554
555 // Options should be ignored
556 CheckExpectedIntegrity(kSha256IntegrityWithEmptyOption, test);
557 CheckExpectedIntegrity(kSha256IntegrityWithOption, test);
558 CheckExpectedIntegrity(kSha256IntegrityWithOptions, test);
559 CheckExpectedIntegrity(kSha256IntegrityWithMimeOption, test);
560
561 // The following tests are expected to fail in every scenario:
562
563 // The hash label must match the hash value.
564 CheckExpectedIntegrity(kSha384IntegrityLabeledAs256, test,
565 Expectation::kIntegrityFailure);
566
567 // With multiple values, at least one must match, and it must be the
568 // strongest hash algorithm.
569 CheckExpectedIntegrity(kGoodSha256AndBadSha384Integrities, test,
570 Expectation::kIntegrityFailure);
571 CheckExpectedIntegrity(kBadSha256AndBadSha384Integrities, test,
572 Expectation::kIntegrityFailure);
573 }
574 }
575
TEST_F(SubresourceIntegrityTest,FindBestAlgorithm)576 TEST_F(SubresourceIntegrityTest, FindBestAlgorithm) {
577 // Each algorithm is its own best.
578 EXPECT_EQ(IntegrityAlgorithm::kSha256,
579 SubresourceIntegrity::FindBestAlgorithm(
580 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256}})));
581 EXPECT_EQ(IntegrityAlgorithm::kSha384,
582 SubresourceIntegrity::FindBestAlgorithm(
583 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha384}})));
584 EXPECT_EQ(IntegrityAlgorithm::kSha512,
585 SubresourceIntegrity::FindBestAlgorithm(
586 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha512}})));
587 EXPECT_EQ(IntegrityAlgorithm::kEd25519,
588 SubresourceIntegrity::FindBestAlgorithm(
589 IntegrityMetadataSet({{"", IntegrityAlgorithm::kEd25519}})));
590
591 // Test combinations of multiple algorithms.
592 EXPECT_EQ(IntegrityAlgorithm::kSha384,
593 SubresourceIntegrity::FindBestAlgorithm(
594 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
595 {"", IntegrityAlgorithm::kSha384}})));
596 EXPECT_EQ(IntegrityAlgorithm::kSha512,
597 SubresourceIntegrity::FindBestAlgorithm(
598 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
599 {"", IntegrityAlgorithm::kSha512},
600 {"", IntegrityAlgorithm::kSha384}})));
601 EXPECT_EQ(IntegrityAlgorithm::kEd25519,
602 SubresourceIntegrity::FindBestAlgorithm(
603 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
604 {"", IntegrityAlgorithm::kSha512},
605 {"", IntegrityAlgorithm::kEd25519}})));
606 }
607
TEST_F(SubresourceIntegrityTest,GetCheckFunctionForAlgorithm)608 TEST_F(SubresourceIntegrityTest, GetCheckFunctionForAlgorithm) {
609 EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegrityDigest ==
610 SubresourceIntegrity::GetCheckFunctionForAlgorithm(
611 IntegrityAlgorithm::kSha256));
612 EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegrityDigest ==
613 SubresourceIntegrity::GetCheckFunctionForAlgorithm(
614 IntegrityAlgorithm::kSha384));
615 EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegrityDigest ==
616 SubresourceIntegrity::GetCheckFunctionForAlgorithm(
617 IntegrityAlgorithm::kSha512));
618 EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegritySignature ==
619 SubresourceIntegrity::GetCheckFunctionForAlgorithm(
620 IntegrityAlgorithm::kEd25519));
621 }
622
623 } // namespace blink
624