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