Chromium Code Reviews| 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 hash algorithms don't work. | |
| 329 {"'asdf256-yay'", "https://a.com/file", "asdf256-yay", false}, | |
| 330 | |
| 331 // Additional whitelisted hashes in the CSP don't interfere. | |
| 332 {"'sha256-yay' 'sha384-boo'", "https://a.com/file", "sha256-yay", true}, | |
| 333 {"'sha256-yay' 'sha384-boo'", "https://a.com/file", "sha384-boo", true}, | |
| 334 | |
| 335 // All integrity hashes must appear in the CSP (and match). | |
| 336 {"'sha256-yay'", "https://a.com/file", "sha256-yay sha384-boo", false}, | |
| 337 {"'sha384-boo'", "https://a.com/file", "sha256-yay sha384-boo", false}, | |
| 338 {"'sha256-yay' 'sha384-boo'", "https://a.com/file", | |
| 339 "sha256-yay sha384-yay", false}, | |
| 340 {"'sha256-yay' 'sha384-boo'", "https://a.com/file", | |
| 341 "sha256-boo sha384-boo", false}, | |
| 342 {"'sha256-yay' 'sha384-boo'", "https://a.com/file", | |
| 343 "sha256-yay sha384-boo", true}, | |
| 344 | |
| 345 // At least one integrity hash must be present. | |
| 346 {"'sha256-yay'", "https://a.com/file", "", false}, | |
| 347 }; | |
| 348 | |
| 349 for (const auto& test : cases) { | |
| 350 SCOPED_TRACE(testing::Message() | |
| 351 << "List: `" << test.list << "`, URL: `" << test.url << "`"); | |
|
Mike West
2017/04/04 13:59:46
You'll probably want to add `test.integrity` in he
Marc Treib
2017/04/04 14:17:21
Good catch, done.
| |
| 352 KURL resource = KURL(KURL(), test.url); | |
| 353 | |
| 354 IntegrityMetadataSet integrityMetadata; | |
| 355 ASSERT_EQ(SubresourceIntegrity::IntegrityParseValidResult, | |
| 356 SubresourceIntegrity::parseIntegrityAttribute(test.integrity, | |
| 357 integrityMetadata)); | |
| 358 | |
| 359 // Report-only 'script-src' | |
| 360 Member<CSPDirectiveList> directiveList = | |
| 361 createList(String("script-src ") + test.list, | |
| 362 ContentSecurityPolicyHeaderTypeReport); | |
| 363 EXPECT_EQ(test.expected, | |
|
Mike West
2017/04/04 13:59:46
Shouldn't this be `EXPECT_TRUE`, if we're only in
Marc Treib
2017/04/04 14:17:21
Hm, good question. I just copied this from AllowFr
Marc Treib
2017/04/04 14:29:49
I'm confused now. There's ContentSecurityPolicyHea
| |
| 364 directiveList->allowScriptFromSource( | |
| 365 resource, String(), integrityMetadata, ParserInserted, | |
| 366 ResourceRequest::RedirectStatus::NoRedirect, | |
| 367 SecurityViolationReportingPolicy::SuppressReporting)); | |
| 368 | |
| 369 // Enforce 'script-src' | |
| 370 directiveList = createList(String("script-src ") + test.list, | |
| 371 ContentSecurityPolicyHeaderTypeEnforce); | |
| 372 EXPECT_EQ(test.expected, | |
| 373 directiveList->allowScriptFromSource( | |
| 374 resource, String(), integrityMetadata, ParserInserted, | |
| 375 ResourceRequest::RedirectStatus::NoRedirect, | |
| 376 SecurityViolationReportingPolicy::SuppressReporting)); | |
| 377 } | |
| 378 } | |
| 379 | |
| 286 TEST_F(CSPDirectiveListTest, allowRequestWithoutIntegrity) { | 380 TEST_F(CSPDirectiveListTest, allowRequestWithoutIntegrity) { |
| 287 struct TestCase { | 381 struct TestCase { |
| 288 const char* list; | 382 const char* list; |
| 289 const char* url; | 383 const char* url; |
| 290 const WebURLRequest::RequestContext context; | 384 const WebURLRequest::RequestContext context; |
| 291 bool expected; | 385 bool expected; |
| 292 } cases[] = { | 386 } cases[] = { |
| 293 | 387 |
| 294 {"require-sri-for script", "https://example.com/file", | 388 {"require-sri-for script", "https://example.com/file", |
| 295 WebURLRequest::RequestContextScript, false}, | 389 WebURLRequest::RequestContextScript, false}, |
| (...skipping 726 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1022 CSPDirectiveList::getSourceVector(test.directive, policyVector).size(), | 1116 CSPDirectiveList::getSourceVector(test.directive, policyVector).size(), |
| 1023 udpatedTotal); | 1117 udpatedTotal); |
| 1024 EXPECT_EQ(CSPDirectiveList::getSourceVector( | 1118 EXPECT_EQ(CSPDirectiveList::getSourceVector( |
| 1025 ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector) | 1119 ContentSecurityPolicy::DirectiveType::ChildSrc, policyVector) |
| 1026 .size(), | 1120 .size(), |
| 1027 expectedChildSrc); | 1121 expectedChildSrc); |
| 1028 } | 1122 } |
| 1029 } | 1123 } |
| 1030 | 1124 |
| 1031 } // namespace blink | 1125 } // namespace blink |
| OLD | NEW |