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 "config.h" 6 #include "core/frame/csp/CSPDirectiveList.h" 7 8 #include "core/frame/LocalFrame.h" 9 #include "platform/ParsingUtilities.h" 10 #include "platform/weborigin/KURL.h" 11 #include "wtf/text/WTFString.h" 12 13 namespace WebCore { 14 15 CSPDirectiveList::CSPDirectiveList(ContentSecurityPolicy* policy, ContentSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source) 16 : m_policy(policy) 17 , m_headerType(type) 18 , m_headerSource(source) 19 , m_reportOnly(false) 20 , m_haveSandboxPolicy(false) 21 , m_reflectedXSSDisposition(ReflectedXSSUnset) 22 , m_didSetReferrerPolicy(false) 23 , m_referrerPolicy(ReferrerPolicyDefault) 24 { 25 m_reportOnly = type == ContentSecurityPolicyHeaderTypeReport; 26 } 27 28 PassOwnPtr<CSPDirectiveList> CSPDirectiveList::create(ContentSecurityPolicy* policy, const UChar* begin, const UChar* end, ContentSecurityPolicyHeaderType type, ContentSecurityPolicyHeaderSource source) 29 { 30 OwnPtr<CSPDirectiveList> directives = adoptPtr(new CSPDirectiveList(policy, type, source)); 31 directives->parse(begin, end); 32 33 if (!directives->checkEval(directives->operativeDirective(directives->m_scriptSrc.get()))) { 34 String message = "Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: \"" + directives->operativeDirective(directives->m_scriptSrc.get())->text() + "\".\n"; 35 directives->setEvalDisabledErrorMessage(message); 36 } 37 38 if (directives->isReportOnly() && directives->reportURIs().isEmpty()) 39 policy->reportMissingReportURI(String(begin, end - begin)); 40 41 return directives.release(); 42 } 43 44 void CSPDirectiveList::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL) const 45 { 46 String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage; 47 m_policy->executionContext()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message); 48 m_policy->reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header); 49 } 50 51 void CSPDirectiveList::reportViolationWithLocation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, const String& contextURL, const WTF::OrdinalNumber& contextLine) const 52 { 53 String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage; 54 m_policy->executionContext()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message, contextURL, contextLine.oneBasedInt()); 55 m_policy->reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header); 56 } 57 58 void CSPDirectiveList::reportViolationWithState(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const KURL& blockedURL, ScriptState* scriptState) const 59 { 60 String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage; 61 m_policy->executionContext()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message, scriptState); 62 m_policy->reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header); 63 } 64 65 bool CSPDirectiveList::checkEval(SourceListDirective* directive) const 66 { 67 return !directive || directive->allowEval(); 68 } 69 70 bool CSPDirectiveList::checkInline(SourceListDirective* directive) const 71 { 72 return !directive || (directive->allowInline() && !directive->isHashOrNoncePresent()); 73 } 74 75 bool CSPDirectiveList::checkNonce(SourceListDirective* directive, const String& nonce) const 76 { 77 return !directive || directive->allowNonce(nonce); 78 } 79 80 bool CSPDirectiveList::checkHash(SourceListDirective* directive, const CSPHashValue& hashValue) const 81 { 82 return !directive || directive->allowHash(hashValue); 83 } 84 85 bool CSPDirectiveList::checkSource(SourceListDirective* directive, const KURL& url) const 86 { 87 return !directive || directive->allows(url); 88 } 89 90 bool CSPDirectiveList::checkAncestors(SourceListDirective* directive, LocalFrame* frame) const 91 { 92 if (!frame || !directive) 93 return true; 94 95 for (Frame* current = frame->tree().parent(); current; current = current->tree().parent()) { 96 // FIXME: To make this work for out-of-process iframes, we need to propagate URL information of ancestor frames across processes. 97 if (!current->isLocalFrame() || !directive->allows(toLocalFrame(current)->document()->url())) 98 return false; 99 } 100 return true; 101 } 102 103 bool CSPDirectiveList::checkMediaType(MediaListDirective* directive, const String& type, const String& typeAttribute) const 104 { 105 if (!directive) 106 return true; 107 if (typeAttribute.isEmpty() || typeAttribute.stripWhiteSpace() != type) 108 return false; 109 return directive->allows(type); 110 } 111 112 SourceListDirective* CSPDirectiveList::operativeDirective(SourceListDirective* directive) const 113 { 114 return directive ? directive : m_defaultSrc.get(); 115 } 116 117 SourceListDirective* CSPDirectiveList::operativeDirective(SourceListDirective* directive, SourceListDirective* override) const 118 { 119 return directive ? directive : override; 120 } 121 122 bool CSPDirectiveList::checkEvalAndReportViolation(SourceListDirective* directive, const String& consoleMessage, ScriptState* scriptState) const 123 { 124 if (checkEval(directive)) 125 return true; 126 127 String suffix = String(); 128 if (directive == m_defaultSrc) 129 suffix = " Note that 'script-src' was not explicitly set, so 'default-src' is used as a fallback."; 130 131 reportViolationWithState(directive->text(), ContentSecurityPolicy::ScriptSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", KURL(), scriptState); 132 if (!m_reportOnly) { 133 m_policy->reportBlockedScriptExecutionToInspector(directive->text()); 134 return false; 135 } 136 return true; 137 } 138 139 bool CSPDirectiveList::checkMediaTypeAndReportViolation(MediaListDirective* directive, const String& type, const String& typeAttribute, const String& consoleMessage) const 140 { 141 if (checkMediaType(directive, type, typeAttribute)) 142 return true; 143 144 String message = consoleMessage + "\'" + directive->text() + "\'."; 145 if (typeAttribute.isEmpty()) 146 message = message + " When enforcing the 'plugin-types' directive, the plugin's media type must be explicitly declared with a 'type' attribute on the containing element (e.g. '<object type=\"[TYPE GOES HERE]\" ...>')."; 147 148 reportViolation(directive->text(), ContentSecurityPolicy::PluginTypes, message + "\n", KURL()); 149 return denyIfEnforcingPolicy(); 150 } 151 152 bool CSPDirectiveList::checkInlineAndReportViolation(SourceListDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, bool isScript) const 153 { 154 if (checkInline(directive)) 155 return true; 156 157 String suffix = String(); 158 if (directive->allowInline() && directive->isHashOrNoncePresent()) { 159 // If inline is allowed, but a hash or nonce is present, we ignore 'unsafe-inline'. Throw a reasonable error. 160 suffix = " Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list."; 161 } else { 162 suffix = " Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution."; 163 if (directive == m_defaultSrc) 164 suffix = suffix + " Note also that '" + String(isScript ? "script" : "style") + "-src' was not explicitly set, so 'default-src' is used as a fallback."; 165 } 166 167 reportViolationWithLocation(directive->text(), isScript ? ContentSecurityPolicy::ScriptSrc : ContentSecurityPolicy::StyleSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", KURL(), contextURL, contextLine); 168 169 if (!m_reportOnly) { 170 if (isScript) 171 m_policy->reportBlockedScriptExecutionToInspector(directive->text()); 172 return false; 173 } 174 return true; 175 } 176 177 bool CSPDirectiveList::checkSourceAndReportViolation(SourceListDirective* directive, const KURL& url, const String& effectiveDirective) const 178 { 179 if (checkSource(directive, url)) 180 return true; 181 182 String prefix; 183 if (ContentSecurityPolicy::BaseURI == effectiveDirective) 184 prefix = "Refused to set the document's base URI to '"; 185 else if (ContentSecurityPolicy::ChildSrc == effectiveDirective) 186 prefix = "Refused to create a child context containing '"; 187 else if (ContentSecurityPolicy::ConnectSrc == effectiveDirective) 188 prefix = "Refused to connect to '"; 189 else if (ContentSecurityPolicy::FontSrc == effectiveDirective) 190 prefix = "Refused to load the font '"; 191 else if (ContentSecurityPolicy::FormAction == effectiveDirective) 192 prefix = "Refused to send form data to '"; 193 else if (ContentSecurityPolicy::FrameSrc == effectiveDirective) 194 prefix = "Refused to frame '"; 195 else if (ContentSecurityPolicy::ImgSrc == effectiveDirective) 196 prefix = "Refused to load the image '"; 197 else if (ContentSecurityPolicy::MediaSrc == effectiveDirective) 198 prefix = "Refused to load media from '"; 199 else if (ContentSecurityPolicy::ObjectSrc == effectiveDirective) 200 prefix = "Refused to load plugin data from '"; 201 else if (ContentSecurityPolicy::ScriptSrc == effectiveDirective) 202 prefix = "Refused to load the script '"; 203 else if (ContentSecurityPolicy::StyleSrc == effectiveDirective) 204 prefix = "Refused to load the stylesheet '"; 205 206 String suffix = String(); 207 if (directive == m_defaultSrc) 208 suffix = " Note that '" + effectiveDirective + "' was not explicitly set, so 'default-src' is used as a fallback."; 209 210 reportViolation(directive->text(), effectiveDirective, prefix + url.elidedString() + "' because it violates the following Content Security Policy directive: \"" + directive->text() + "\"." + suffix + "\n", url); 211 return denyIfEnforcingPolicy(); 212 } 213 214 bool CSPDirectiveList::checkAncestorsAndReportViolation(SourceListDirective* directive, LocalFrame* frame) const 215 { 216 if (checkAncestors(directive, frame)) 217 return true; 218 219 reportViolation(directive->text(), "frame-ancestors", "Refused to display '" + frame->document()->url().elidedString() + " in a frame because an ancestor violates the following Content Security Policy directive: \"" + directive->text() + "\".", frame->document()->url()); 220 return denyIfEnforcingPolicy(); 221 } 222 223 bool CSPDirectiveList::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 224 { 225 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute JavaScript URL because it violates the following Content Security Policy directive: ")); 226 if (reportingStatus == ContentSecurityPolicy::SendReport) 227 return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true); 228 229 return checkInline(operativeDirective(m_scriptSrc.get())); 230 } 231 232 bool CSPDirectiveList::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 233 { 234 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute inline event handler because it violates the following Content Security Policy directive: ")); 235 if (reportingStatus == ContentSecurityPolicy::SendReport) 236 return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true); 237 return checkInline(operativeDirective(m_scriptSrc.get())); 238 } 239 240 bool CSPDirectiveList::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 241 { 242 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute inline script because it violates the following Content Security Policy directive: ")); 243 return reportingStatus == ContentSecurityPolicy::SendReport ? 244 checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true) : 245 checkInline(operativeDirective(m_scriptSrc.get())); 246 } 247 248 bool CSPDirectiveList::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const 249 { 250 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to apply inline style because it violates the following Content Security Policy directive: ")); 251 return reportingStatus == ContentSecurityPolicy::SendReport ? 252 checkInlineAndReportViolation(operativeDirective(m_styleSrc.get()), consoleMessage, contextURL, contextLine, false) : 253 checkInline(operativeDirective(m_styleSrc.get())); 254 } 255 256 bool CSPDirectiveList::allowEval(ScriptState* scriptState, ContentSecurityPolicy::ReportingStatus reportingStatus) const 257 { 258 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: ")); 259 260 return reportingStatus == ContentSecurityPolicy::SendReport ? 261 checkEvalAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, scriptState) : 262 checkEval(operativeDirective(m_scriptSrc.get())); 263 } 264 265 bool CSPDirectiveList::allowPluginType(const String& type, const String& typeAttribute, const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 266 { 267 return reportingStatus == ContentSecurityPolicy::SendReport ? 268 checkMediaTypeAndReportViolation(m_pluginTypes.get(), type, typeAttribute, "Refused to load '" + url.elidedString() + "' (MIME type '" + typeAttribute + "') because it violates the following Content Security Policy Directive: ") : 269 checkMediaType(m_pluginTypes.get(), type, typeAttribute); 270 } 271 272 bool CSPDirectiveList::allowScriptFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 273 { 274 return reportingStatus == ContentSecurityPolicy::SendReport ? 275 checkSourceAndReportViolation(operativeDirective(m_scriptSrc.get()), url, ContentSecurityPolicy::ScriptSrc) : 276 checkSource(operativeDirective(m_scriptSrc.get()), url); 277 } 278 279 bool CSPDirectiveList::allowObjectFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 280 { 281 if (url.protocolIsAbout()) 282 return true; 283 return reportingStatus == ContentSecurityPolicy::SendReport ? 284 checkSourceAndReportViolation(operativeDirective(m_objectSrc.get()), url, ContentSecurityPolicy::ObjectSrc) : 285 checkSource(operativeDirective(m_objectSrc.get()), url); 286 } 287 288 bool CSPDirectiveList::allowChildFrameFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 289 { 290 if (url.protocolIsAbout()) 291 return true; 292 293 // 'frame-src' is the only directive which overrides something other than the default sources. 294 // It overrides 'child-src', which overrides the default sources. So, we do this nested set 295 // of calls to 'operativeDirective()' to grab 'frame-src' if it exists, 'child-src' if it 296 // doesn't, and 'defaut-src' if neither are available. 297 // 298 // All of this only applies, of course, if we're in CSP 1.1. In CSP 1.0, 'frame-src' 299 // overrides 'default-src' directly. 300 SourceListDirective* whichDirective = m_policy->experimentalFeaturesEnabled() ? 301 operativeDirective(m_frameSrc.get(), operativeDirective(m_childSrc.get())) : 302 operativeDirective(m_frameSrc.get()); 303 304 return reportingStatus == ContentSecurityPolicy::SendReport ? 305 checkSourceAndReportViolation(whichDirective, url, ContentSecurityPolicy::FrameSrc) : 306 checkSource(whichDirective, url); 307 } 308 309 bool CSPDirectiveList::allowImageFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 310 { 311 return reportingStatus == ContentSecurityPolicy::SendReport ? 312 checkSourceAndReportViolation(operativeDirective(m_imgSrc.get()), url, ContentSecurityPolicy::ImgSrc) : 313 checkSource(operativeDirective(m_imgSrc.get()), url); 314 } 315 316 bool CSPDirectiveList::allowStyleFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 317 { 318 return reportingStatus == ContentSecurityPolicy::SendReport ? 319 checkSourceAndReportViolation(operativeDirective(m_styleSrc.get()), url, ContentSecurityPolicy::StyleSrc) : 320 checkSource(operativeDirective(m_styleSrc.get()), url); 321 } 322 323 bool CSPDirectiveList::allowFontFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 324 { 325 return reportingStatus == ContentSecurityPolicy::SendReport ? 326 checkSourceAndReportViolation(operativeDirective(m_fontSrc.get()), url, ContentSecurityPolicy::FontSrc) : 327 checkSource(operativeDirective(m_fontSrc.get()), url); 328 } 329 330 bool CSPDirectiveList::allowMediaFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 331 { 332 return reportingStatus == ContentSecurityPolicy::SendReport ? 333 checkSourceAndReportViolation(operativeDirective(m_mediaSrc.get()), url, ContentSecurityPolicy::MediaSrc) : 334 checkSource(operativeDirective(m_mediaSrc.get()), url); 335 } 336 337 bool CSPDirectiveList::allowConnectToSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 338 { 339 return reportingStatus == ContentSecurityPolicy::SendReport ? 340 checkSourceAndReportViolation(operativeDirective(m_connectSrc.get()), url, ContentSecurityPolicy::ConnectSrc) : 341 checkSource(operativeDirective(m_connectSrc.get()), url); 342 } 343 344 bool CSPDirectiveList::allowFormAction(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 345 { 346 return reportingStatus == ContentSecurityPolicy::SendReport ? 347 checkSourceAndReportViolation(m_formAction.get(), url, ContentSecurityPolicy::FormAction) : 348 checkSource(m_formAction.get(), url); 349 } 350 351 bool CSPDirectiveList::allowBaseURI(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 352 { 353 return reportingStatus == ContentSecurityPolicy::SendReport ? 354 checkSourceAndReportViolation(m_baseURI.get(), url, ContentSecurityPolicy::BaseURI) : 355 checkSource(m_baseURI.get(), url); 356 } 357 358 bool CSPDirectiveList::allowAncestors(LocalFrame* frame, ContentSecurityPolicy::ReportingStatus reportingStatus) const 359 { 360 return reportingStatus == ContentSecurityPolicy::SendReport ? 361 checkAncestorsAndReportViolation(m_frameAncestors.get(), frame) : 362 checkAncestors(m_frameAncestors.get(), frame); 363 } 364 365 bool CSPDirectiveList::allowChildContextFromSource(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const 366 { 367 return reportingStatus == ContentSecurityPolicy::SendReport ? 368 checkSourceAndReportViolation(operativeDirective(m_childSrc.get()), url, ContentSecurityPolicy::ChildSrc) : 369 checkSource(operativeDirective(m_childSrc.get()), url); 370 } 371 372 bool CSPDirectiveList::allowScriptNonce(const String& nonce) const 373 { 374 return checkNonce(operativeDirective(m_scriptSrc.get()), nonce); 375 } 376 377 bool CSPDirectiveList::allowStyleNonce(const String& nonce) const 378 { 379 return checkNonce(operativeDirective(m_styleSrc.get()), nonce); 380 } 381 382 bool CSPDirectiveList::allowScriptHash(const CSPHashValue& hashValue) const 383 { 384 return checkHash(operativeDirective(m_scriptSrc.get()), hashValue); 385 } 386 387 bool CSPDirectiveList::allowStyleHash(const CSPHashValue& hashValue) const 388 { 389 return checkHash(operativeDirective(m_styleSrc.get()), hashValue); 390 } 391 392 // policy = directive-list 393 // directive-list = [ directive *( ";" [ directive ] ) ] 394 // 395 void CSPDirectiveList::parse(const UChar* begin, const UChar* end) 396 { 397 m_header = String(begin, end - begin); 398 399 if (begin == end) 400 return; 401 402 const UChar* position = begin; 403 while (position < end) { 404 const UChar* directiveBegin = position; 405 skipUntil<UChar>(position, end, ';'); 406 407 String name, value; 408 if (parseDirective(directiveBegin, position, name, value)) { 409 ASSERT(!name.isEmpty()); 410 addDirective(name, value); 411 } 412 413 ASSERT(position == end || *position == ';'); 414 skipExactly<UChar>(position, end, ';'); 415 } 416 } 417 418 // directive = *WSP [ directive-name [ WSP directive-value ] ] 419 // directive-name = 1*( ALPHA / DIGIT / "-" ) 420 // directive-value = *( WSP / <VCHAR except ";"> ) 421 // 422 bool CSPDirectiveList::parseDirective(const UChar* begin, const UChar* end, String& name, String& value) 423 { 424 ASSERT(name.isEmpty()); 425 ASSERT(value.isEmpty()); 426 427 const UChar* position = begin; 428 skipWhile<UChar, isASCIISpace>(position, end); 429 430 // Empty directive (e.g. ";;;"). Exit early. 431 if (position == end) 432 return false; 433 434 const UChar* nameBegin = position; 435 skipWhile<UChar, isCSPDirectiveNameCharacter>(position, end); 436 437 // The directive-name must be non-empty. 438 if (nameBegin == position) { 439 skipWhile<UChar, isNotASCIISpace>(position, end); 440 m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin)); 441 return false; 442 } 443 444 name = String(nameBegin, position - nameBegin); 445 446 if (position == end) 447 return true; 448 449 if (!skipExactly<UChar, isASCIISpace>(position, end)) { 450 skipWhile<UChar, isNotASCIISpace>(position, end); 451 m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin)); 452 return false; 453 } 454 455 skipWhile<UChar, isASCIISpace>(position, end); 456 457 const UChar* valueBegin = position; 458 skipWhile<UChar, isCSPDirectiveValueCharacter>(position, end); 459 460 if (position != end) { 461 m_policy->reportInvalidDirectiveValueCharacter(name, String(valueBegin, end - valueBegin)); 462 return false; 463 } 464 465 // The directive-value may be empty. 466 if (valueBegin == position) 467 return true; 468 469 value = String(valueBegin, position - valueBegin); 470 return true; 471 } 472 473 void CSPDirectiveList::parseReportURI(const String& name, const String& value) 474 { 475 if (!m_reportURIs.isEmpty()) { 476 m_policy->reportDuplicateDirective(name); 477 return; 478 } 479 480 Vector<UChar> characters; 481 value.appendTo(characters); 482 483 const UChar* position = characters.data(); 484 const UChar* end = position + characters.size(); 485 486 while (position < end) { 487 skipWhile<UChar, isASCIISpace>(position, end); 488 489 const UChar* urlBegin = position; 490 skipWhile<UChar, isNotASCIISpace>(position, end); 491 492 if (urlBegin < position) { 493 String url = String(urlBegin, position - urlBegin); 494 m_reportURIs.append(m_policy->completeURL(url)); 495 } 496 } 497 } 498 499 500 template<class CSPDirectiveType> 501 void CSPDirectiveList::setCSPDirective(const String& name, const String& value, OwnPtr<CSPDirectiveType>& directive) 502 { 503 if (directive) { 504 m_policy->reportDuplicateDirective(name); 505 return; 506 } 507 directive = adoptPtr(new CSPDirectiveType(name, value, m_policy)); 508 } 509 510 void CSPDirectiveList::applySandboxPolicy(const String& name, const String& sandboxPolicy) 511 { 512 if (m_reportOnly) { 513 m_policy->reportInvalidInReportOnly(name); 514 return; 515 } 516 if (m_haveSandboxPolicy) { 517 m_policy->reportDuplicateDirective(name); 518 return; 519 } 520 m_haveSandboxPolicy = true; 521 String invalidTokens; 522 m_policy->enforceSandboxFlags(parseSandboxPolicy(sandboxPolicy, invalidTokens)); 523 if (!invalidTokens.isNull()) 524 m_policy->reportInvalidSandboxFlags(invalidTokens); 525 } 526 527 void CSPDirectiveList::parseReflectedXSS(const String& name, const String& value) 528 { 529 if (m_reflectedXSSDisposition != ReflectedXSSUnset) { 530 m_policy->reportDuplicateDirective(name); 531 m_reflectedXSSDisposition = ReflectedXSSInvalid; 532 return; 533 } 534 535 if (value.isEmpty()) { 536 m_reflectedXSSDisposition = ReflectedXSSInvalid; 537 m_policy->reportInvalidReflectedXSS(value); 538 return; 539 } 540 541 Vector<UChar> characters; 542 value.appendTo(characters); 543 544 const UChar* position = characters.data(); 545 const UChar* end = position + characters.size(); 546 547 skipWhile<UChar, isASCIISpace>(position, end); 548 const UChar* begin = position; 549 skipWhile<UChar, isNotASCIISpace>(position, end); 550 551 // value1 552 // ^ 553 if (equalIgnoringCase("allow", begin, position - begin)) { 554 m_reflectedXSSDisposition = AllowReflectedXSS; 555 } else if (equalIgnoringCase("filter", begin, position - begin)) { 556 m_reflectedXSSDisposition = FilterReflectedXSS; 557 } else if (equalIgnoringCase("block", begin, position - begin)) { 558 m_reflectedXSSDisposition = BlockReflectedXSS; 559 } else { 560 m_reflectedXSSDisposition = ReflectedXSSInvalid; 561 m_policy->reportInvalidReflectedXSS(value); 562 return; 563 } 564 565 skipWhile<UChar, isASCIISpace>(position, end); 566 if (position == end && m_reflectedXSSDisposition != ReflectedXSSUnset) 567 return; 568 569 // value1 value2 570 // ^ 571 m_reflectedXSSDisposition = ReflectedXSSInvalid; 572 m_policy->reportInvalidReflectedXSS(value); 573 } 574 575 void CSPDirectiveList::parseReferrer(const String& name, const String& value) 576 { 577 if (m_didSetReferrerPolicy) { 578 m_policy->reportDuplicateDirective(name); 579 m_referrerPolicy = ReferrerPolicyNever; 580 return; 581 } 582 583 m_didSetReferrerPolicy = true; 584 585 if (value.isEmpty()) { 586 m_policy->reportInvalidReferrer(value); 587 m_referrerPolicy = ReferrerPolicyNever; 588 return; 589 } 590 591 Vector<UChar> characters; 592 value.appendTo(characters); 593 594 const UChar* position = characters.data(); 595 const UChar* end = position + characters.size(); 596 597 skipWhile<UChar, isASCIISpace>(position, end); 598 const UChar* begin = position; 599 skipWhile<UChar, isNotASCIISpace>(position, end); 600 601 // value1 602 // ^ 603 if (equalIgnoringCase("always", begin, position - begin)) { 604 m_referrerPolicy = ReferrerPolicyAlways; 605 } else if (equalIgnoringCase("default", begin, position - begin)) { 606 m_referrerPolicy = ReferrerPolicyDefault; 607 } else if (equalIgnoringCase("never", begin, position - begin)) { 608 m_referrerPolicy = ReferrerPolicyNever; 609 } else if (equalIgnoringCase("origin", begin, position - begin)) { 610 m_referrerPolicy = ReferrerPolicyOrigin; 611 } else { 612 m_referrerPolicy = ReferrerPolicyNever; 613 m_policy->reportInvalidReferrer(value); 614 return; 615 } 616 617 skipWhile<UChar, isASCIISpace>(position, end); 618 if (position == end) 619 return; 620 621 // value1 value2 622 // ^ 623 m_referrerPolicy = ReferrerPolicyNever; 624 m_policy->reportInvalidReferrer(value); 625 626 } 627 628 void CSPDirectiveList::addDirective(const String& name, const String& value) 629 { 630 ASSERT(!name.isEmpty()); 631 632 if (equalIgnoringCase(name, ContentSecurityPolicy::DefaultSrc)) { 633 setCSPDirective<SourceListDirective>(name, value, m_defaultSrc); 634 } else if (equalIgnoringCase(name, ContentSecurityPolicy::ScriptSrc)) { 635 setCSPDirective<SourceListDirective>(name, value, m_scriptSrc); 636 m_policy->usesScriptHashAlgorithms(m_scriptSrc->hashAlgorithmsUsed()); 637 } else if (equalIgnoringCase(name, ContentSecurityPolicy::ObjectSrc)) { 638 setCSPDirective<SourceListDirective>(name, value, m_objectSrc); 639 } else if (equalIgnoringCase(name, ContentSecurityPolicy::FrameAncestors)) { 640 setCSPDirective<SourceListDirective>(name, value, m_frameAncestors); 641 } else if (equalIgnoringCase(name, ContentSecurityPolicy::FrameSrc)) { 642 setCSPDirective<SourceListDirective>(name, value, m_frameSrc); 643 } else if (equalIgnoringCase(name, ContentSecurityPolicy::ImgSrc)) { 644 setCSPDirective<SourceListDirective>(name, value, m_imgSrc); 645 } else if (equalIgnoringCase(name, ContentSecurityPolicy::StyleSrc)) { 646 setCSPDirective<SourceListDirective>(name, value, m_styleSrc); 647 m_policy->usesStyleHashAlgorithms(m_styleSrc->hashAlgorithmsUsed()); 648 } else if (equalIgnoringCase(name, ContentSecurityPolicy::FontSrc)) { 649 setCSPDirective<SourceListDirective>(name, value, m_fontSrc); 650 } else if (equalIgnoringCase(name, ContentSecurityPolicy::MediaSrc)) { 651 setCSPDirective<SourceListDirective>(name, value, m_mediaSrc); 652 } else if (equalIgnoringCase(name, ContentSecurityPolicy::ConnectSrc)) { 653 setCSPDirective<SourceListDirective>(name, value, m_connectSrc); 654 } else if (equalIgnoringCase(name, ContentSecurityPolicy::Sandbox)) { 655 applySandboxPolicy(name, value); 656 } else if (equalIgnoringCase(name, ContentSecurityPolicy::ReportURI)) { 657 parseReportURI(name, value); 658 } else if (m_policy->experimentalFeaturesEnabled()) { 659 if (equalIgnoringCase(name, ContentSecurityPolicy::BaseURI)) 660 setCSPDirective<SourceListDirective>(name, value, m_baseURI); 661 else if (equalIgnoringCase(name, ContentSecurityPolicy::ChildSrc)) 662 setCSPDirective<SourceListDirective>(name, value, m_childSrc); 663 else if (equalIgnoringCase(name, ContentSecurityPolicy::FormAction)) 664 setCSPDirective<SourceListDirective>(name, value, m_formAction); 665 else if (equalIgnoringCase(name, ContentSecurityPolicy::PluginTypes)) 666 setCSPDirective<MediaListDirective>(name, value, m_pluginTypes); 667 else if (equalIgnoringCase(name, ContentSecurityPolicy::ReflectedXSS)) 668 parseReflectedXSS(name, value); 669 else if (equalIgnoringCase(name, ContentSecurityPolicy::Referrer)) 670 parseReferrer(name, value); 671 else 672 m_policy->reportUnsupportedDirective(name); 673 } else { 674 m_policy->reportUnsupportedDirective(name); 675 } 676 } 677 678 679 } // namespace WebCore 680