Changeset 82028 in webkit
- Timestamp:
- Mar 26, 2011 4:59:34 AM (13 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r82026 r82028 1 2011-03-26 Adam Barth <abarth@webkit.org> 2 3 Reviewed by Eric Seidel. 4 5 Teach Content Security Policy how to parse source-list 6 https://bugs.webkit.org/show_bug.cgi?id=54799 7 8 Test a variety of source-list parsing cases. There's a bunch more 9 cases we could be testing. We'll add more over time. 10 11 * http/tests/security/contentSecurityPolicy/source-list-parsing-expected.txt: Added. 12 * http/tests/security/contentSecurityPolicy/source-list-parsing.html: Added. 13 1 14 2011-03-26 Dan Bernstein <mitz@apple.com> 2 15 -
trunk/Source/WebCore/ChangeLog
r82027 r82028 1 2011-03-26 Adam Barth <abarth@webkit.org> 2 3 Reviewed by Eric Seidel. 4 5 Teach Content Security Policy how to parse source-list 6 https://bugs.webkit.org/show_bug.cgi?id=54799 7 8 This patch is larger than I would like, but I wasn't sure how to make 9 it any smaller while still being reasonably testable. I've left out 10 some features (such as host wildcarding and 'self') so I can add them 11 in later patches with tests. 12 13 Test: http/tests/security/contentSecurityPolicy/source-list-parsing.html 14 15 * bindings/ScriptControllerBase.cpp: 16 * dom/ScriptElement.cpp: 17 * html/parser/HTMLDocumentParser.cpp: 18 * loader/FrameLoader.cpp: 19 - Add include explicitly now that we're not spamming the include 20 everywhere. 21 * dom/Document.cpp: 22 (WebCore::Document::initSecurityContext): 23 - We need to pass the SecurityOrigin object to 24 ContentSecurityPolicy so that it can resolve implicit parts of 25 source patterns, such as the scheme. 26 * dom/Document.h: 27 - Forward declare ContentSecurityPolicy rather than including the 28 header. Technically this could be a separate change, but I was 29 getting annoyed at the world re-builds. 30 * page/ContentSecurityPolicy.cpp: 31 (WebCore::skipExactly): 32 (WebCore::skipUtil): 33 (WebCore::skipWhile): 34 - Clean up these parser helper functions. We might consider moving 35 them to a more general location. They're very helpful for 36 writing secure HTTP header parsers. 37 (WebCore::CSPSource::CSPSource): 38 - New class to represent one source in a source-list. 39 (WebCore::CSPSource::matches): 40 (WebCore::CSPSource::schemeMatches): 41 (WebCore::CSPSource::hostMatches): 42 (WebCore::CSPSource::portMatches): 43 (WebCore::CSPSource::isSchemeOnly): 44 - Currently we represent scheme-only sources using with an empty 45 m_host. Another approach I considered was using another bool, 46 but that seemed slighly messier. 47 (WebCore::CSPSourceList::CSPSourceList): 48 - CSPSourceList doesn't need to ref SecurityOrigin because 49 CSPSourceList is owned by ContentSecurityPolicy, which holds a 50 ref. 51 (WebCore::CSPSourceList::parse): 52 (WebCore::CSPSourceList::matches): 53 (WebCore::CSPSourceList::parseSource): 54 (WebCore::CSPSourceList::parseScheme): 55 (WebCore::CSPSourceList::parseHost): 56 (WebCore::CSPSourceList::parsePort): 57 - A basic "segment and recurse" parser. This parser causes us to 58 take more branches than we need, but I don't think we need to 59 squeeze every last ouch of performance out of this parser. This 60 approach is more simple than some of the other approaches I 61 tried. 62 (WebCore::CSPSourceList::addSourceSelf): 63 (WebCore::CSPDirective::CSPDirective): 64 (WebCore::CSPDirective::allows): 65 (WebCore::ContentSecurityPolicy::ContentSecurityPolicy): 66 (WebCore::ContentSecurityPolicy::parse): 67 (WebCore::ContentSecurityPolicy::parseDirective): 68 (WebCore::ContentSecurityPolicy::addDirective): 69 - I couldn't resist re-writing this parser to use the helper 70 functions and to match the style of the source-list parser. 71 * page/ContentSecurityPolicy.h: 72 (WebCore::ContentSecurityPolicy::create): 73 - Accept a SecurityOrigin context object. 74 1 75 2011-03-26 Patrick Gansterer <paroga@webkit.org> 2 76 -
trunk/Source/WebCore/bindings/ScriptControllerBase.cpp
r79547 r82028 22 22 #include "ScriptController.h" 23 23 24 #include "ContentSecurityPolicy.h" 24 25 #include "DocumentLoader.h" 25 26 #include "Frame.h" -
trunk/Source/WebCore/dom/Document.cpp
r81965 r82028 43 43 #include "Comment.h" 44 44 #include "Console.h" 45 #include "ContentSecurityPolicy.h" 45 46 #include "CookieJar.h" 46 47 #include "CustomEvent.h" … … 4501 4502 m_cookieURL = m_url; 4502 4503 ScriptExecutionContext::setSecurityOrigin(SecurityOrigin::create(m_url, m_frame->loader()->sandboxFlags())); 4503 m_contentSecurityPolicy = ContentSecurityPolicy::create( );4504 m_contentSecurityPolicy = ContentSecurityPolicy::create(securityOrigin()); 4504 4505 4505 4506 if (SecurityOrigin::allowSubstituteDataAccessToLocal()) { -
trunk/Source/WebCore/dom/Document.h
r81220 r82028 33 33 #include "Color.h" 34 34 #include "ContainerNode.h" 35 #include "ContentSecurityPolicy.h"36 35 #include "DOMTimeStamp.h" 37 36 #include "DocumentOrderedMap.h" … … 52 51 namespace WebCore { 53 52 53 class AXObjectCache; 54 54 class Attr; 55 class AXObjectCache;56 55 class CDATASection; 56 class CSSPrimitiveValueCache; 57 class CSSStyleDeclaration; 58 class CSSStyleSelector; 59 class CSSStyleSheet; 57 60 class CachedCSSStyleSheet; 58 61 class CachedResourceLoader; … … 60 63 class CanvasRenderingContext; 61 64 class CharacterData; 62 class CSSPrimitiveValueCache;63 class CSSStyleDeclaration;64 class CSSStyleSelector;65 class CSSStyleSheet;66 65 class Comment; 66 class ContentSecurityPolicy; 67 67 class DOMImplementation; 68 68 class DOMSelection; -
trunk/Source/WebCore/dom/ScriptElement.cpp
r81198 r82028 27 27 #include "CachedScript.h" 28 28 #include "CachedResourceLoader.h" 29 #include "ContentSecurityPolicy.h" 29 30 #include "Document.h" 30 31 #include "DocumentParser.h" -
trunk/Source/WebCore/html/parser/HTMLDocumentParser.cpp
r80861 r82028 27 27 #include "HTMLDocumentParser.h" 28 28 29 #include "ContentSecurityPolicy.h" 29 30 #include "DocumentFragment.h" 30 31 #include "Element.h" -
trunk/Source/WebCore/loader/FrameLoader.cpp
r82010 r82028 42 42 #include "CachedResourceLoader.h" 43 43 #include "Chrome.h" 44 #include "ContentSecurityPolicy.h" 44 45 #include "DOMImplementation.h" 45 46 #include "DOMWindow.h" -
trunk/Source/WebCore/page/ContentSecurityPolicy.cpp
r81425 r82028 26 26 #include "config.h" 27 27 #include "ContentSecurityPolicy.h" 28 28 29 #include "Document.h" 30 #include "NotImplemented.h" 31 #include "SecurityOrigin.h" 29 32 30 33 namespace WebCore { 31 34 32 static bool isDirectiveNameCharacter(UChar c) 33 { 34 return isASCIIAlpha(c) || isASCIIDigit(c) || c == '-'; 35 } 36 37 static bool isDirectiveValueCharacter(UChar c) 35 // Normally WebKit uses "static" for internal linkage, but using "static" for 36 // these functions causes a compile error because these functions are used as 37 // template parameters. 38 namespace { 39 40 bool isDirectiveNameCharacter(UChar c) 41 { 42 return isASCIIAlphanumeric(c) || c == '-'; 43 } 44 45 bool isDirectiveValueCharacter(UChar c) 38 46 { 39 47 return isASCIISpace(c) || (c >= 0x21 && c <= 0x7e); // Whitespace + VCHAR 40 48 } 41 49 42 static void advanceUntil(const UChar*& pos, const UChar* end, UChar delimiter) 43 { 44 while (pos < end) { 45 if (*pos++ == delimiter) 46 return; 47 } 50 bool isSourceCharacter(UChar c) 51 { 52 return !isASCIISpace(c); 53 } 54 55 bool isHostCharacter(UChar c) 56 { 57 return isASCIIAlphanumeric(c) || c == '-'; 58 } 59 60 bool isSchemeContinuationCharacter(UChar c) 61 { 62 return isASCIIAlphanumeric(c) || c == '+' || c == '-' || c == '.'; 63 } 64 65 } // namespace 66 67 static bool skipExactly(const UChar*& position, const UChar* end, UChar delimiter) 68 { 69 if (position < end && *position == delimiter) { 70 ++position; 71 return true; 72 } 73 return false; 74 } 75 76 template<bool characterPredicate(UChar)> 77 static bool skipExactly(const UChar*& position, const UChar* end) 78 { 79 if (position < end && characterPredicate(*position)) { 80 ++position; 81 return true; 82 } 83 return false; 84 } 85 86 static void skipUtil(const UChar*& position, const UChar* end, UChar delimiter) 87 { 88 while (position < end && *position != delimiter) 89 ++position; 90 } 91 92 template<bool characterPredicate(UChar)> 93 static void skipWhile(const UChar*& position, const UChar* end) 94 { 95 while (position < end && characterPredicate(*position)) 96 ++position; 97 } 98 99 class CSPSource { 100 public: 101 CSPSource(const String& scheme, const String& host, int port, bool hostHasWildcard, bool portHasWildcard) 102 : m_scheme(scheme) 103 , m_host(host) 104 , m_port(port) 105 , m_hostHasWildcard(hostHasWildcard) 106 , m_portHasWildcard(portHasWildcard) 107 { 108 } 109 110 bool matches(const KURL& url) const 111 { 112 if (!schemeMatches(url)) 113 return false; 114 if (isSchemeOnly()) 115 return true; 116 return hostMatches(url) && portMatches(url); 117 } 118 119 private: 120 bool schemeMatches(const KURL& url) const 121 { 122 return equalIgnoringCase(url.protocol(), m_scheme); 123 } 124 125 bool hostMatches(const KURL& url) const 126 { 127 if (m_hostHasWildcard) 128 notImplemented(); 129 130 return equalIgnoringCase(url.host(), m_host); 131 } 132 133 bool portMatches(const KURL& url) const 134 { 135 if (m_portHasWildcard) 136 return true; 137 138 // FIXME: Handle explicit default ports correctly. 139 return url.port() == m_port; 140 } 141 142 bool isSchemeOnly() const { return m_host.isEmpty(); } 143 144 String m_scheme; 145 String m_host; 146 int m_port; 147 148 bool m_hostHasWildcard; 149 bool m_portHasWildcard; 150 }; 151 152 class CSPSourceList { 153 public: 154 explicit CSPSourceList(SecurityOrigin*); 155 156 void parse(const String&); 157 bool matches(const KURL&); 158 159 private: 160 void parse(const UChar* begin, const UChar* end); 161 162 bool parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, int& port, bool& hostHasWildcard, bool& portHasWildcard); 163 bool parseScheme(const UChar* begin, const UChar* end, String& scheme); 164 bool parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard); 165 bool parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard); 166 167 void addSourceSelf(); 168 169 SecurityOrigin* m_origin; 170 Vector<CSPSource> m_list; 171 }; 172 173 CSPSourceList::CSPSourceList(SecurityOrigin* origin) 174 : m_origin(origin) 175 { 176 } 177 178 void CSPSourceList::parse(const String& value) 179 { 180 parse(value.characters(), value.characters() + value.length()); 181 } 182 183 bool CSPSourceList::matches(const KURL& url) 184 { 185 for (size_t i = 0; i < m_list.size(); ++i) { 186 if (m_list[i].matches(url)) 187 return true; 188 } 189 return false; 190 } 191 192 // source-list = *WSP [ source *( 1*WSP source ) *WSP ] 193 // / *WSP "'none'" *WSP 194 // 195 void CSPSourceList::parse(const UChar* begin, const UChar* end) 196 { 197 const UChar* position = begin; 198 199 bool isFirstSourceInList = true; 200 while (position < end) { 201 skipWhile<isASCIISpace>(position, end); 202 const UChar* beginSource = position; 203 skipWhile<isSourceCharacter>(position, end); 204 205 if (isFirstSourceInList && equalIgnoringCase("'none'", beginSource, position - beginSource)) 206 return; // We represent 'none' as an empty m_list. 207 isFirstSourceInList = false; 208 209 String scheme, host; 210 int port = 0; 211 bool hostHasWildcard = false; 212 bool portHasWildcard = false; 213 214 if (parseSource(beginSource, position, scheme, host, port, hostHasWildcard, portHasWildcard)) { 215 if (scheme.isEmpty()) 216 scheme = m_origin->protocol(); 217 m_list.append(CSPSource(scheme, host, port, hostHasWildcard, portHasWildcard)); 218 } 219 220 ASSERT(position == end || isASCIISpace(*position)); 221 } 222 } 223 224 // source = scheme ":" 225 // / ( [ scheme "://" ] host [ port ] ) 226 // / "'self'" 227 // 228 bool CSPSourceList::parseSource(const UChar* begin, const UChar* end, 229 String& scheme, String& host, int& port, 230 bool& hostHasWildcard, bool& portHasWildcard) 231 { 232 if (begin == end) 233 return false; 234 235 if (equalIgnoringCase("'self'", begin, end - begin)) { 236 addSourceSelf(); 237 return false; 238 } 239 240 const UChar* position = begin; 241 242 const UChar* beginHost = begin; 243 skipUtil(position, end, ':'); 244 245 if (position == end) { 246 // This must be a host-only source. 247 if (!parseHost(beginHost, position, host, hostHasWildcard)) 248 return false; 249 return true; 250 } 251 252 if (end - position == 1) { 253 ASSERT(*position == ':'); 254 // This must be a scheme-only source. 255 if (!parseScheme(begin, position, scheme)) 256 return false; 257 return true; 258 } 259 260 ASSERT(end - position >= 2); 261 if (position[1] == '/') { 262 if (!parseScheme(begin, position, scheme) 263 || !skipExactly(position, end, ':') 264 || !skipExactly(position, end, '/') 265 || !skipExactly(position, end, '/')) 266 return false; 267 beginHost = position; 268 skipUtil(position, end, ':'); 269 } 270 271 if (position == beginHost) 272 return false; 273 274 if (!parseHost(beginHost, position, host, hostHasWildcard)) 275 return false; 276 277 if (position == end) { 278 port = 0; 279 return true; 280 } 281 282 if (!skipExactly(position, end, ':')) 283 ASSERT_NOT_REACHED(); 284 285 if (!parsePort(position, end, port, portHasWildcard)) 286 return false; 287 288 return true; 289 } 290 291 // ; <scheme> production from RFC 3986 292 // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) 293 // 294 bool CSPSourceList::parseScheme(const UChar* begin, const UChar* end, String& scheme) 295 { 296 ASSERT(begin <= end); 297 ASSERT(scheme.isEmpty()); 298 299 if (begin == end) 300 return false; 301 302 const UChar* position = begin; 303 304 if (!skipExactly<isASCIIAlpha>(position, end)) 305 return false; 306 307 skipWhile<isSchemeContinuationCharacter>(position, end); 308 309 if (position != end) 310 return false; 311 312 scheme = String(begin, end - begin); 313 return true; 314 } 315 316 // host = [ "*." ] 1*host-char *( "." 1*host-char ) 317 // / "*" 318 // host-char = ALPHA / DIGIT / "-" 319 // 320 bool CSPSourceList::parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard) 321 { 322 ASSERT(begin <= end); 323 ASSERT(host.isEmpty()); 324 ASSERT(!hostHasWildcard); 325 326 if (begin == end) 327 return false; 328 329 const UChar* position = begin; 330 331 if (skipExactly(position, end, '*')) { 332 hostHasWildcard = true; 333 334 if (position == end) 335 return true; 336 337 if (!skipExactly(position, end, '.')) 338 return false; 339 } 340 341 const UChar* hostBegin = position; 342 343 while (position < end) { 344 if (!skipExactly<isHostCharacter>(position, end)) 345 return false; 346 347 skipWhile<isHostCharacter>(position, end); 348 349 if (position < end && !skipExactly(position, end, '.')) 350 return false; 351 } 352 353 ASSERT(position == end); 354 host = String(hostBegin, end - hostBegin); 355 return true; 356 } 357 358 // port = ":" ( 1*DIGIT / "*" ) 359 // 360 bool CSPSourceList::parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard) 361 { 362 ASSERT(begin <= end); 363 ASSERT(!port); 364 ASSERT(!portHasWildcard); 365 366 if (begin == end) 367 return false; 368 369 if (end - begin == 1 && *begin == '*') { 370 port = 0; 371 portHasWildcard = true; 372 return true; 373 } 374 375 const UChar* position = begin; 376 skipWhile<isASCIIDigit>(position, end); 377 378 if (position != end) 379 return false; 380 381 bool ok; 382 port = charactersToIntStrict(begin, end - begin, &ok); 383 return ok; 384 } 385 386 void CSPSourceList::addSourceSelf() 387 { 388 // FIXME: Inherit the scheme, host, and port from the current URL. 389 notImplemented(); 48 390 } 49 391 50 392 class CSPDirective { 51 393 public: 52 explicit CSPDirective(const String& value) 53 : m_value(value) 54 { 55 } 56 57 bool allows(const KURL&) 58 { 59 return false; 394 CSPDirective(const String& value, SecurityOrigin* origin) 395 : m_sourceList(origin) 396 { 397 m_sourceList.parse(value); 398 } 399 400 bool allows(const KURL& url) 401 { 402 return m_sourceList.matches(url); 60 403 } 61 404 62 405 private: 63 String m_value;406 CSPSourceList m_sourceList; 64 407 }; 65 408 66 ContentSecurityPolicy::ContentSecurityPolicy( )409 ContentSecurityPolicy::ContentSecurityPolicy(SecurityOrigin* origin) 67 410 : m_havePolicy(false) 411 , m_origin(origin) 68 412 { 69 413 } … … 92 436 } 93 437 438 // policy = directive-list 439 // directive-list = [ directive *( ";" [ directive ] ) ] 440 // 94 441 void ContentSecurityPolicy::parse(const String& policy) 95 442 { … … 99 446 return; 100 447 101 const UChar* pos = policy.characters(); 102 const UChar* end = pos + policy.length(); 103 104 while (pos < end) { 105 Vector<UChar, 32> name; 106 Vector<UChar, 64> value; 107 108 if (!parseDirective(pos, end, name, value)) 109 continue; 110 if (name.isEmpty()) 111 continue; 112 113 // We use a copy here instead of String::adopt because we expect 114 // the name and the value to be relatively short, so the copy will 115 // be cheaper than the extra malloc. 116 emitDirective(String(name), String(value)); 117 } 118 } 119 120 bool ContentSecurityPolicy::parseDirective(const UChar*& pos, const UChar* end, Vector<UChar, 32>& name, Vector<UChar, 64>& value) 121 { 122 ASSERT(pos < end); 448 const UChar* position = policy.characters(); 449 const UChar* end = position + policy.length(); 450 451 while (position < end) { 452 const UChar* directiveBegin = position; 453 skipUtil(position, end, ';'); 454 455 String name, value; 456 if (parseDirective(directiveBegin, position, name, value)) { 457 ASSERT(!name.isEmpty()); 458 addDirective(name, value); 459 } 460 461 ASSERT(position == end || *position == ';'); 462 skipExactly(position, end, ';'); 463 } 464 } 465 466 // directive = *WSP [ directive-name [ WSP directive-value ] ] 467 // directive-name = 1*( ALPHA / DIGIT / "-" ) 468 // directive-value = *( WSP / <VCHAR except ";"> ) 469 // 470 bool ContentSecurityPolicy::parseDirective(const UChar* begin, const UChar* end, String& name, String& value) 471 { 123 472 ASSERT(name.isEmpty()); 124 473 ASSERT(value.isEmpty()); 125 474 126 while (pos < end && isASCIISpace(*pos)) 127 pos++; 128 129 while (pos < end) { 130 UChar currentCharacter = *pos; 131 if (currentCharacter == ';') 132 break; 133 if (isASCIISpace(currentCharacter)) 134 break; 135 if (!isDirectiveNameCharacter(currentCharacter)) { 136 advanceUntil(pos, end, ';'); 137 return false; 138 } 139 name.append(currentCharacter); 140 pos++; 141 } 142 143 while (pos < end && isASCIISpace(*pos)) 144 pos++; 145 146 if (pos < end && *pos == ';') { 147 pos++; 148 return true; 149 } 150 151 while (pos < end) { 152 UChar currentCharacter = *pos; 153 if (currentCharacter == ';') 154 break; 155 if (!isDirectiveValueCharacter(currentCharacter)) { 156 advanceUntil(pos, end, ';'); 157 return false; 158 } 159 value.append(currentCharacter); 160 pos++; 161 } 162 163 if (pos < end && *pos == ';') { 164 pos++; 165 return true; 166 } 167 475 const UChar* position = begin; 476 skipWhile<isASCIISpace>(position, end); 477 478 const UChar* nameBegin = position; 479 skipWhile<isDirectiveNameCharacter>(position, end); 480 481 // The directive-name must be non-empty. 482 if (nameBegin == position) 483 return false; 484 485 name = String(nameBegin, position - nameBegin); 486 487 if (position == end) 488 return true; 489 490 if (!skipExactly<isASCIISpace>(position, end)) 491 return false; 492 493 skipWhile<isASCIISpace>(position, end); 494 495 const UChar* valueBegin = position; 496 skipWhile<isDirectiveValueCharacter>(position, end); 497 498 if (position != end) 499 return false; 500 501 // The directive-value may be empty. 502 if (valueBegin == position) 503 return true; 504 505 value = String(valueBegin, position - valueBegin); 168 506 return true; 169 507 } 170 508 171 void ContentSecurityPolicy:: emitDirective(const String& name, const String& value)509 void ContentSecurityPolicy::addDirective(const String& name, const String& value) 172 510 { 173 511 DEFINE_STATIC_LOCAL(String, scriptSrc, ("script-src")); … … 176 514 177 515 if (!m_scriptSrc && equalIgnoringCase(name, scriptSrc)) 178 m_scriptSrc = adoptPtr(new CSPDirective(value ));179 } 180 181 } 516 m_scriptSrc = adoptPtr(new CSPDirective(value, m_origin.get())); 517 } 518 519 } -
trunk/Source/WebCore/page/ContentSecurityPolicy.h
r81425 r82028 33 33 34 34 class CSPDirective; 35 class SecurityOrigin; 35 36 36 37 class ContentSecurityPolicy : public RefCounted<ContentSecurityPolicy> { 37 38 public: 38 static PassRefPtr<ContentSecurityPolicy> create() { return adoptRef(new ContentSecurityPolicy); } 39 static PassRefPtr<ContentSecurityPolicy> create(SecurityOrigin* origin = 0) 40 { 41 return adoptRef(new ContentSecurityPolicy(origin)); 42 } 39 43 ~ContentSecurityPolicy(); 40 44 … … 46 50 47 51 private: 48 ContentSecurityPolicy();52 explicit ContentSecurityPolicy(SecurityOrigin*); 49 53 50 54 void parse(const String&); 51 bool parseDirective(const UChar* & pos, const UChar* end, Vector<UChar, 32>& name, Vector<UChar, 64>& value);52 void emitDirective(const String& name, const String& value);55 bool parseDirective(const UChar* begin, const UChar* end, String& name, String& value); 56 void addDirective(const String& name, const String& value); 53 57 54 58 bool m_havePolicy; 59 RefPtr<SecurityOrigin> m_origin; 55 60 OwnPtr<CSPDirective> m_scriptSrc; 56 61 };
Note: See TracChangeset
for help on using the changeset viewer.