| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "core/frame/csp/CSPDirectiveList.h" | 5 #include "core/frame/csp/CSPDirectiveList.h" |
| 6 | 6 |
| 7 #include "core/frame/SubresourceIntegrity.h" |
| 7 #include "core/frame/csp/ContentSecurityPolicy.h" | 8 #include "core/frame/csp/ContentSecurityPolicy.h" |
| 8 #include "core/frame/csp/SourceListDirective.h" | 9 #include "core/frame/csp/SourceListDirective.h" |
| 9 #include "platform/loader/fetch/ResourceRequest.h" | 10 #include "platform/loader/fetch/ResourceRequest.h" |
| 10 #include "platform/network/ContentSecurityPolicyParsers.h" | 11 #include "platform/network/ContentSecurityPolicyParsers.h" |
| 11 #include "testing/gtest/include/gtest/gtest.h" | 12 #include "testing/gtest/include/gtest/gtest.h" |
| 12 #include "wtf/text/StringOperators.h" | 13 #include "wtf/text/StringOperators.h" |
| 13 #include "wtf/text/WTFString.h" | 14 #include "wtf/text/WTFString.h" |
| 14 | 15 |
| 15 namespace blink { | 16 namespace blink { |
| 16 | 17 |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 158 for (const auto& test : cases) { | 159 for (const auto& test : cases) { |
| 159 SCOPED_TRACE(testing::Message() << "List: `" << test.list << "`, URL: `" | 160 SCOPED_TRACE(testing::Message() << "List: `" << test.list << "`, URL: `" |
| 160 << test.url << "`"); | 161 << test.url << "`"); |
| 161 KURL scriptSrc = KURL(KURL(), test.url); | 162 KURL scriptSrc = KURL(KURL(), test.url); |
| 162 | 163 |
| 163 // Report-only | 164 // Report-only |
| 164 Member<CSPDirectiveList> directiveList = | 165 Member<CSPDirectiveList> directiveList = |
| 165 createList(test.list, ContentSecurityPolicyHeaderTypeReport); | 166 createList(test.list, ContentSecurityPolicyHeaderTypeReport); |
| 166 EXPECT_EQ(test.expected, | 167 EXPECT_EQ(test.expected, |
| 167 directiveList->allowScriptFromSource( | 168 directiveList->allowScriptFromSource( |
| 168 scriptSrc, String(), ParserInserted, | 169 scriptSrc, String(), IntegrityMetadataSet(), ParserInserted, |
| 169 ResourceRequest::RedirectStatus::NoRedirect, | 170 ResourceRequest::RedirectStatus::NoRedirect, |
| 170 SecurityViolationReportingPolicy::SuppressReporting)); | 171 SecurityViolationReportingPolicy::SuppressReporting)); |
| 171 | 172 |
| 172 // Enforce | 173 // Enforce |
| 173 directiveList = | 174 directiveList = |
| 174 createList(test.list, ContentSecurityPolicyHeaderTypeEnforce); | 175 createList(test.list, ContentSecurityPolicyHeaderTypeEnforce); |
| 175 EXPECT_EQ(test.expected, | 176 EXPECT_EQ(test.expected, |
| 176 directiveList->allowScriptFromSource( | 177 directiveList->allowScriptFromSource( |
| 177 scriptSrc, String(), ParserInserted, | 178 scriptSrc, String(), IntegrityMetadataSet(), ParserInserted, |
| 178 ResourceRequest::RedirectStatus::NoRedirect, | 179 ResourceRequest::RedirectStatus::NoRedirect, |
| 179 SecurityViolationReportingPolicy::SuppressReporting)); | 180 SecurityViolationReportingPolicy::SuppressReporting)); |
| 180 } | 181 } |
| 181 } | 182 } |
| 182 | 183 |
| 183 TEST_F(CSPDirectiveListTest, AllowFromSourceWithNonce) { | 184 TEST_F(CSPDirectiveListTest, AllowFromSourceWithNonce) { |
| 184 struct TestCase { | 185 struct TestCase { |
| 185 const char* list; | 186 const char* list; |
| 186 const char* url; | 187 const char* url; |
| 187 const char* nonce; | 188 const char* nonce; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 215 SCOPED_TRACE(testing::Message() << "List: `" << test.list << "`, URL: `" | 216 SCOPED_TRACE(testing::Message() << "List: `" << test.list << "`, URL: `" |
| 216 << test.url << "`"); | 217 << test.url << "`"); |
| 217 KURL resource = KURL(KURL(), test.url); | 218 KURL resource = KURL(KURL(), test.url); |
| 218 | 219 |
| 219 // Report-only 'script-src' | 220 // Report-only 'script-src' |
| 220 Member<CSPDirectiveList> directiveList = | 221 Member<CSPDirectiveList> directiveList = |
| 221 createList(String("script-src ") + test.list, | 222 createList(String("script-src ") + test.list, |
| 222 ContentSecurityPolicyHeaderTypeReport); | 223 ContentSecurityPolicyHeaderTypeReport); |
| 223 EXPECT_EQ(test.expected, | 224 EXPECT_EQ(test.expected, |
| 224 directiveList->allowScriptFromSource( | 225 directiveList->allowScriptFromSource( |
| 225 resource, String(test.nonce), ParserInserted, | 226 resource, String(test.nonce), IntegrityMetadataSet(), |
| 226 ResourceRequest::RedirectStatus::NoRedirect, | 227 ParserInserted, ResourceRequest::RedirectStatus::NoRedirect, |
| 227 SecurityViolationReportingPolicy::SuppressReporting)); | 228 SecurityViolationReportingPolicy::SuppressReporting)); |
| 228 | 229 |
| 229 // Enforce 'script-src' | 230 // Enforce 'script-src' |
| 230 directiveList = createList(String("script-src ") + test.list, | 231 directiveList = createList(String("script-src ") + test.list, |
| 231 ContentSecurityPolicyHeaderTypeEnforce); | 232 ContentSecurityPolicyHeaderTypeEnforce); |
| 232 EXPECT_EQ(test.expected, | 233 EXPECT_EQ(test.expected, |
| 233 directiveList->allowScriptFromSource( | 234 directiveList->allowScriptFromSource( |
| 234 resource, String(test.nonce), ParserInserted, | 235 resource, String(test.nonce), IntegrityMetadataSet(), |
| 235 ResourceRequest::RedirectStatus::NoRedirect, | 236 ParserInserted, ResourceRequest::RedirectStatus::NoRedirect, |
| 236 SecurityViolationReportingPolicy::SuppressReporting)); | 237 SecurityViolationReportingPolicy::SuppressReporting)); |
| 237 | 238 |
| 238 // Report-only 'style-src' | 239 // Report-only 'style-src' |
| 239 directiveList = createList(String("style-src ") + test.list, | 240 directiveList = createList(String("style-src ") + test.list, |
| 240 ContentSecurityPolicyHeaderTypeReport); | 241 ContentSecurityPolicyHeaderTypeReport); |
| 241 EXPECT_EQ(test.expected, | 242 EXPECT_EQ(test.expected, |
| 242 directiveList->allowStyleFromSource( | 243 directiveList->allowStyleFromSource( |
| 243 resource, String(test.nonce), | 244 resource, String(test.nonce), |
| 244 ResourceRequest::RedirectStatus::NoRedirect, | 245 ResourceRequest::RedirectStatus::NoRedirect, |
| 245 SecurityViolationReportingPolicy::SuppressReporting)); | 246 SecurityViolationReportingPolicy::SuppressReporting)); |
| 246 | 247 |
| 247 // Enforce 'style-src' | 248 // Enforce 'style-src' |
| 248 directiveList = createList(String("style-src ") + test.list, | 249 directiveList = createList(String("style-src ") + test.list, |
| 249 ContentSecurityPolicyHeaderTypeEnforce); | 250 ContentSecurityPolicyHeaderTypeEnforce); |
| 250 EXPECT_EQ(test.expected, | 251 EXPECT_EQ(test.expected, |
| 251 directiveList->allowStyleFromSource( | 252 directiveList->allowStyleFromSource( |
| 252 resource, String(test.nonce), | 253 resource, String(test.nonce), |
| 253 ResourceRequest::RedirectStatus::NoRedirect, | 254 ResourceRequest::RedirectStatus::NoRedirect, |
| 254 SecurityViolationReportingPolicy::SuppressReporting)); | 255 SecurityViolationReportingPolicy::SuppressReporting)); |
| 255 | 256 |
| 256 // Report-only 'style-src' | 257 // Report-only 'style-src' |
| 257 directiveList = createList(String("default-src ") + test.list, | 258 directiveList = createList(String("default-src ") + test.list, |
| 258 ContentSecurityPolicyHeaderTypeReport); | 259 ContentSecurityPolicyHeaderTypeReport); |
| 259 EXPECT_EQ(test.expected, | 260 EXPECT_EQ(test.expected, |
| 260 directiveList->allowScriptFromSource( | 261 directiveList->allowScriptFromSource( |
| 261 resource, String(test.nonce), ParserInserted, | 262 resource, String(test.nonce), IntegrityMetadataSet(), |
| 262 ResourceRequest::RedirectStatus::NoRedirect, | 263 ParserInserted, ResourceRequest::RedirectStatus::NoRedirect, |
| 263 SecurityViolationReportingPolicy::SuppressReporting)); | 264 SecurityViolationReportingPolicy::SuppressReporting)); |
| 264 EXPECT_EQ(test.expected, | 265 EXPECT_EQ(test.expected, |
| 265 directiveList->allowStyleFromSource( | 266 directiveList->allowStyleFromSource( |
| 266 resource, String(test.nonce), | 267 resource, String(test.nonce), |
| 267 ResourceRequest::RedirectStatus::NoRedirect, | 268 ResourceRequest::RedirectStatus::NoRedirect, |
| 268 SecurityViolationReportingPolicy::SuppressReporting)); | 269 SecurityViolationReportingPolicy::SuppressReporting)); |
| 269 | 270 |
| 270 // Enforce 'style-src' | 271 // Enforce 'style-src' |
| 271 directiveList = createList(String("default-src ") + test.list, | 272 directiveList = createList(String("default-src ") + test.list, |
| 272 ContentSecurityPolicyHeaderTypeEnforce); | 273 ContentSecurityPolicyHeaderTypeEnforce); |
| 273 EXPECT_EQ(test.expected, | 274 EXPECT_EQ(test.expected, |
| 274 directiveList->allowScriptFromSource( | 275 directiveList->allowScriptFromSource( |
| 275 resource, String(test.nonce), ParserInserted, | 276 resource, String(test.nonce), IntegrityMetadataSet(), |
| 276 ResourceRequest::RedirectStatus::NoRedirect, | 277 ParserInserted, ResourceRequest::RedirectStatus::NoRedirect, |
| 277 SecurityViolationReportingPolicy::SuppressReporting)); | 278 SecurityViolationReportingPolicy::SuppressReporting)); |
| 278 EXPECT_EQ(test.expected, | 279 EXPECT_EQ(test.expected, |
| 279 directiveList->allowStyleFromSource( | 280 directiveList->allowStyleFromSource( |
| 280 resource, String(test.nonce), | 281 resource, String(test.nonce), |
| 281 ResourceRequest::RedirectStatus::NoRedirect, | 282 ResourceRequest::RedirectStatus::NoRedirect, |
| 282 SecurityViolationReportingPolicy::SuppressReporting)); | 283 SecurityViolationReportingPolicy::SuppressReporting)); |
| 283 } | 284 } |
| 284 } | 285 } |
| 285 | 286 |
| 287 TEST_F(CSPDirectiveListTest, AllowScriptFromSourceWithHash) { |
| 288 struct TestCase { |
| 289 const char* list; |
| 290 const char* url; |
| 291 const char* integrity; |
| 292 bool expected; |
| 293 } cases[] = { |
| 294 // Doesn't affect lists without hashes. |
| 295 {"https://example.com", "https://example.com/file", "sha256-yay", true}, |
| 296 {"https://example.com", "https://example.com/file", "sha256-boo", true}, |
| 297 {"https://example.com", "https://example.com/file", "", true}, |
| 298 {"https://example.com", "https://not.example.com/file", "sha256-yay", |
| 299 false}, |
| 300 {"https://example.com", "https://not.example.com/file", "sha256-boo", |
| 301 false}, |
| 302 {"https://example.com", "https://not.example.com/file", "", false}, |
| 303 |
| 304 // Doesn't affect URLs that match the whitelist. |
| 305 {"https://example.com 'sha256-yay'", "https://example.com/file", |
| 306 "sha256-yay", true}, |
| 307 {"https://example.com 'sha256-yay'", "https://example.com/file", |
| 308 "sha256-boo", true}, |
| 309 {"https://example.com 'sha256-yay'", "https://example.com/file", "", |
| 310 true}, |
| 311 |
| 312 // Does affect URLs that don't match the whitelist. |
| 313 {"https://example.com 'sha256-yay'", "https://not.example.com/file", |
| 314 "sha256-yay", true}, |
| 315 {"https://example.com 'sha256-yay'", "https://not.example.com/file", |
| 316 "sha256-boo", false}, |
| 317 {"https://example.com 'sha256-yay'", "https://not.example.com/file", "", |
| 318 false}, |
| 319 |
| 320 // Both algorithm and digest must match. |
| 321 {"'sha256-yay'", "https://a.com/file", "sha384-yay", false}, |
| 322 |
| 323 // Sha-1 is not supported, but -384 and -512 are. |
| 324 {"'sha1-yay'", "https://a.com/file", "sha1-yay", false}, |
| 325 {"'sha384-yay'", "https://a.com/file", "sha384-yay", true}, |
| 326 {"'sha512-yay'", "https://a.com/file", "sha512-yay", true}, |
| 327 |
| 328 // Unknown (or future) hash algorithms don't work. |
| 329 {"'asdf256-yay'", "https://a.com/file", "asdf256-yay", false}, |
| 330 |
| 331 // But they also don't interfere. |
| 332 {"'sha256-yay'", "https://a.com/file", "sha256-yay asdf256-boo", true}, |
| 333 |
| 334 // Additional whitelisted hashes in the CSP don't interfere. |
| 335 {"'sha256-yay' 'sha384-boo'", "https://a.com/file", "sha256-yay", true}, |
| 336 {"'sha256-yay' 'sha384-boo'", "https://a.com/file", "sha384-boo", true}, |
| 337 |
| 338 // All integrity hashes must appear in the CSP (and match). |
| 339 {"'sha256-yay'", "https://a.com/file", "sha256-yay sha384-boo", false}, |
| 340 {"'sha384-boo'", "https://a.com/file", "sha256-yay sha384-boo", false}, |
| 341 {"'sha256-yay' 'sha384-boo'", "https://a.com/file", |
| 342 "sha256-yay sha384-yay", false}, |
| 343 {"'sha256-yay' 'sha384-boo'", "https://a.com/file", |
| 344 "sha256-boo sha384-boo", false}, |
| 345 {"'sha256-yay' 'sha384-boo'", "https://a.com/file", |
| 346 "sha256-yay sha384-boo", true}, |
| 347 |
| 348 // At least one integrity hash must be present. |
| 349 {"'sha256-yay'", "https://a.com/file", "", false}, |
| 350 }; |
| 351 |
| 352 for (const auto& test : cases) { |
| 353 SCOPED_TRACE(testing::Message() |
| 354 << "List: `" << test.list << "`, URL: `" << test.url |
| 355 << "`, Integrity: `" << test.integrity << "`"); |
| 356 KURL resource = KURL(KURL(), test.url); |
| 357 |
| 358 IntegrityMetadataSet integrityMetadata; |
| 359 ASSERT_EQ(SubresourceIntegrity::IntegrityParseValidResult, |
| 360 SubresourceIntegrity::parseIntegrityAttribute(test.integrity, |
| 361 integrityMetadata)); |
| 362 |
| 363 // Report-only 'script-src' |
| 364 Member<CSPDirectiveList> directiveList = |
| 365 createList(String("script-src ") + test.list, |
| 366 ContentSecurityPolicyHeaderTypeReport); |
| 367 EXPECT_EQ(test.expected, |
| 368 directiveList->allowScriptFromSource( |
| 369 resource, String(), integrityMetadata, ParserInserted, |
| 370 ResourceRequest::RedirectStatus::NoRedirect, |
| 371 SecurityViolationReportingPolicy::SuppressReporting)); |
| 372 |
| 373 // Enforce 'script-src' |
| 374 directiveList = createList(String("script-src ") + test.list, |
| 375 ContentSecurityPolicyHeaderTypeEnforce); |
| 376 EXPECT_EQ(test.expected, |
| 377 directiveList->allowScriptFromSource( |
| 378 resource, String(), integrityMetadata, ParserInserted, |
| 379 ResourceRequest::RedirectStatus::NoRedirect, |
| 380 SecurityViolationReportingPolicy::SuppressReporting)); |
| 381 } |
| 382 } |
| 383 |
| 286 TEST_F(CSPDirectiveListTest, allowRequestWithoutIntegrity) { | 384 TEST_F(CSPDirectiveListTest, allowRequestWithoutIntegrity) { |
| 287 struct TestCase { | 385 struct TestCase { |
| 288 const char* list; | 386 const char* list; |
| 289 const char* url; | 387 const char* url; |
| 290 const WebURLRequest::RequestContext context; | 388 const WebURLRequest::RequestContext context; |
| 291 bool expected; | 389 bool expected; |
| 292 } cases[] = { | 390 } cases[] = { |
| 293 | 391 |
| 294 {"require-sri-for script", "https://example.com/file", | 392 {"require-sri-for script", "https://example.com/file", |
| 295 WebURLRequest::RequestContextScript, false}, | 393 WebURLRequest::RequestContextScript, false}, |
| (...skipping 726 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1022 CSPDirectiveList::getSourceVector(test.directive, policyVector).size(), | 1120 CSPDirectiveList::getSourceVector(test.directive, policyVector).size(), |
| 1023 udpatedTotal); | 1121 udpatedTotal); |
| 1024 EXPECT_EQ(CSPDirectiveList::getSourceVector( | 1122 EXPECT_EQ(CSPDirectiveList::getSourceVector( |
| 1025 ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector) | 1123 ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector) |
| 1026 .size(), | 1124 .size(), |
| 1027 expectedChildSrc); | 1125 expectedChildSrc); |
| 1028 } | 1126 } |
| 1029 } | 1127 } |
| 1030 | 1128 |
| 1031 } // namespace blink | 1129 } // namespace blink |
| OLD | NEW |