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