Changeset 249810 in webkit
- Timestamp:
- Sep 12, 2019 8:33:31 AM (5 years ago)
- Location:
- trunk
- Files:
-
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r249808 r249810 1 2019-09-12 Adrian Perez de Castro <aperez@igalia.com> 2 3 [GTK][WPE] webkit_settings_set_user_agent() allows content forbidden in HTTP headers 4 https://bugs.webkit.org/show_bug.cgi?id=201077 5 6 Reviewed by Carlos Garcia Campos. 7 8 Add a function to validate whether a string contains a valid value 9 which can be used in a HTTP User-Agent header. 10 11 Covered by new WebCore API test HTTPParsers.ValidateUserAgentValues. 12 13 * platform/glib/UserAgentGLib.cpp: 14 (WebCore::standardUserAgent): Assert that the returned string is a valid User-Agent. 15 (WebCore::standardUserAgentForURL): Ditto. 16 * platform/network/HTTPParsers.cpp: Added a series of helper functions which skip over 17 characters of a string, which can be used to scan over the different elements of an 18 User-Agent value; all of them receive the position from the input string where to start 19 scanning, updating it to the position right after the scanned item (this follow the 20 convention already in use by other functions in the source file). Each of them has 21 been annotated with the RFC number and section which contains the definition of the 22 scanned item, and the corresponding BNF rules to make the code easier to follow. 23 (WebCore::skipWhile): Added. 24 (WebCore::isVisibleCharacter): Added. 25 (WebCore::isOctectInFieldContentCharacter): Added. 26 (WebCore::isCommentTextCharacter): Added. 27 (WebCore::isHTTPTokenCharacter): Added. 28 (WebCore::isValidHTTPToken): Refactored to use the new isHTTPTokenCharacter() 29 helper function instead of having the test inside the loop. 30 (WebCore::skipCharacter): Added. 31 (WebCore::skipQuotedPair): Added. 32 (WebCore::skipComment): Added. 33 (WebCore::skipHTTPToken): Added. 34 (WebCore::skipUserAgentProduct): Added. 35 (WebCore::isValidUserAgentHeaderValue): Added. 36 * platform/network/HTTPParsers.h: Add prototype for isValidUserAgentHeaderValue(). 37 1 38 2019-09-12 Mark Lam <mark.lam@apple.com> 2 39 -
trunk/Source/WebCore/platform/glib/UserAgentGLib.cpp
r247427 r249810 126 126 // wrong can cause sites to load the wrong JavaScript, CSS, or custom fonts. In some cases 127 127 // sites won't load resources at all. 128 if (applicationName.isEmpty())129 return standardUserAgentStatic();130 128 131 String finalApplicationVersion = applicationVersion; 132 if (finalApplicationVersion.isEmpty()) 133 finalApplicationVersion = versionForUAString(); 134 135 return standardUserAgentStatic() + ' ' + applicationName + '/' + finalApplicationVersion; 129 String userAgent; 130 if (applicationName.isEmpty()) { 131 userAgent = standardUserAgentStatic(); 132 } else { 133 String finalApplicationVersion = applicationVersion; 134 if (finalApplicationVersion.isEmpty()) 135 finalApplicationVersion = versionForUAString(); 136 userAgent = standardUserAgentStatic() + ' ' + applicationName + '/' + finalApplicationVersion; 137 } 138 ASSERT(isValidUserAgentHeaderValue(userAgent)); 139 return userAgent; 136 140 } 137 141 … … 140 144 auto quirks = UserAgentQuirks::quirksForURL(url); 141 145 // The null string means we don't need a specific UA for the given URL. 142 return quirks.isEmpty() ? String() : buildUserAgentString(quirks); 146 if (quirks.isEmpty()) 147 return String(); 148 149 String userAgent(buildUserAgentString(quirks)); 150 ASSERT(isValidUserAgentHeaderValue(userAgent)); 151 return userAgent; 143 152 } 144 153 -
trunk/Source/WebCore/platform/network/HTTPParsers.cpp
r249792 r249810 98 98 } 99 99 100 // True if characters which satisfy the predicate are present, incrementing 101 // "pos" to the next character which does not satisfy the predicate. 102 // Note: might return pos == str.length(). 103 static inline bool skipWhile(const String& str, unsigned& pos, const WTF::Function<bool(const UChar)>& predicate) 104 { 105 const unsigned start = pos; 106 const unsigned len = str.length(); 107 while (pos < len && predicate(str[pos])) 108 ++pos; 109 return pos != start; 110 } 111 100 112 // See RFC 7230, Section 3.1.2. 101 113 bool isValidReasonPhrase(const String& value) … … 134 146 || c == '<' || c == '=' || c == '>' || c == '?' || c == '@' || c == '[' || c == '\\' 135 147 || c == ']' || c == '{' || c == '}'); 148 } 149 150 // See RFC 7230, Section 3.2.6. 151 static inline bool isVisibleCharacter(const UChar c) 152 { 153 // VCHAR = %x21-7E 154 return (c >= 0x21 && c <= 0x7E); 155 } 156 157 // See RFC 7230, Section 3.2.6. 158 static inline bool isOctectInFieldContentCharacter(const UChar c) 159 { 160 // obs-text = %x80-FF 161 return (c >= 0x80 && c <= 0xFF); 162 } 163 164 // See RFC 7230, Section 3.2.6. 165 static bool isCommentTextCharacter(const UChar c) 166 { 167 // ctext = HTAB / SP 168 // / %x21-27 ; '!'-''' 169 // / %x2A-5B ; '*'-'[' 170 // / %x5D-7E ; ']'-'~' 171 // / obs-text 172 return (c == '\t' || c == ' ' 173 || (c >= 0x21 && c <= 0x27) 174 || (c >= 0x2A && c <= 0x5B) 175 || (c >= 0x5D && c <= 0x7E) 176 || isOctectInFieldContentCharacter(c)); 136 177 } 137 178 … … 175 216 176 217 // See RFC 7230, Section 3.2.6. 218 static inline bool isHTTPTokenCharacter(const UChar c) 219 { 220 // Any VCHAR, except delimiters 221 return c > 0x20 && c < 0x7F && !isDelimiterCharacter(c); 222 } 223 224 // See RFC 7230, Section 3.2.6. 177 225 bool isValidHTTPToken(const String& value) 178 226 { … … 181 229 auto valueStringView = StringView(value); 182 230 for (UChar c : valueStringView.codeUnits()) { 183 if (c <= 0x20 || c >= 0x7F 184 || c == '(' || c == ')' || c == '<' || c == '>' || c == '@' 185 || c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' 186 || c == '/' || c == '[' || c == ']' || c == '?' || c == '=' 187 || c == '{' || c == '}') 188 return false; 231 if (!isHTTPTokenCharacter(c)) 232 return false; 189 233 } 190 234 return true; 235 } 236 237 // True if the character at the given position satisifies a predicate, incrementing "pos" by one. 238 // Note: Might return pos == str.length() 239 static inline bool skipCharacter(const String& value, unsigned& pos, WTF::Function<bool(const UChar)>&& predicate) 240 { 241 if (pos < value.length() && predicate(value[pos])) { 242 ++pos; 243 return true; 244 } 245 return false; 246 } 247 248 // True if the "expected" character is at the given position, incrementing "pos" by one. 249 // Note: Might return pos == str.length() 250 static inline bool skipCharacter(const String& value, unsigned& pos, const UChar expected) 251 { 252 return skipCharacter(value, pos, [expected](const UChar c) { 253 return c == expected; 254 }); 255 } 256 257 // True if a quoted pair is present, incrementing "pos" to the position after the quoted pair. 258 // Note: Might return pos == str.length() 259 // See RFC 7230, Section 3.2.6. 260 static constexpr auto QuotedPairStartCharacter = '\\'; 261 static bool skipQuotedPair(const String& value, unsigned& pos) 262 { 263 // quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) 264 if (!skipCharacter(value, pos, QuotedPairStartCharacter)) 265 return false; 266 267 return skipCharacter(value, pos, '\t') 268 || skipCharacter(value, pos, ' ') 269 || skipCharacter(value, pos, isVisibleCharacter) 270 || skipCharacter(value, pos, isOctectInFieldContentCharacter); 271 } 272 273 // True if a comment is present, incrementing "pos" to the position after the comment. 274 // Note: Might return pos == str.length() 275 // See RFC 7230, Section 3.2.6. 276 static constexpr auto CommentStartCharacter = '('; 277 static constexpr auto CommentEndCharacter = ')'; 278 static bool skipComment(const String& value, unsigned& pos) 279 { 280 // comment = "(" *( ctext / quoted-pair / comment ) ")" 281 // ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text 282 if (!skipCharacter(value, pos, CommentStartCharacter)) 283 return false; 284 285 const unsigned end = value.length(); 286 while (pos < end && value[pos] != CommentEndCharacter) { 287 switch (value[pos]) { 288 case CommentStartCharacter: 289 if (!skipComment(value, pos)) 290 return false; 291 break; 292 case QuotedPairStartCharacter: 293 if (!skipQuotedPair(value, pos)) 294 return false; 295 break; 296 default: 297 if (!skipWhile(value, pos, isCommentTextCharacter)) 298 return false; 299 } 300 } 301 return skipCharacter(value, pos, CommentEndCharacter); 302 } 303 304 // True if an HTTP header token is present, incrementing "pos" to the position after it. 305 // Note: Might return pos == str.length() 306 // See RFC 7230, Section 3.2.6. 307 static bool skipHTTPToken(const String& value, unsigned& pos) 308 { 309 return skipWhile(value, pos, isHTTPTokenCharacter); 310 } 311 312 // True if a product specifier (as in an User-Agent header) is present, incrementing "pos" to the position after it. 313 // Note: Might return pos == str.length() 314 // See RFC 7231, Section 5.5.3. 315 static bool skipUserAgentProduct(const String& value, unsigned& pos) 316 { 317 // product = token ["/" product-version] 318 // product-version = token 319 if (!skipHTTPToken(value, pos)) 320 return false; 321 if (skipCharacter(value, pos, '/')) 322 return skipHTTPToken(value, pos); 323 return true; 324 } 325 326 // See RFC 7231, Section 5.5.3 327 bool isValidUserAgentHeaderValue(const String& value) 328 { 329 // User-Agent = product *( RWS ( product / comment ) ) 330 unsigned pos = 0; 331 if (!skipUserAgentProduct(value, pos)) 332 return false; 333 334 while (pos < value.length()) { 335 if (!skipWhiteSpace(value, pos)) 336 return false; 337 if (value[pos] == CommentStartCharacter) { 338 if (!skipComment(value, pos)) 339 return false; 340 } else { 341 if (!skipUserAgentProduct(value, pos)) 342 return false; 343 } 344 } 345 346 return pos == value.length(); 191 347 } 192 348 -
trunk/Source/WebCore/platform/network/HTTPParsers.h
r249792 r249810 73 73 bool isValidAcceptHeaderValue(const String&); 74 74 bool isValidLanguageHeaderValue(const String&); 75 WEBCORE_EXPORT bool isValidUserAgentHeaderValue(const String&); 75 76 bool isValidHTTPToken(const String&); 76 77 Optional<WallTime> parseHTTPDate(const String&); -
trunk/Source/WebKit/ChangeLog
r249808 r249810 1 2019-09-12 Adrian Perez de Castro <aperez@igalia.com> 2 3 [GTK][WPE] webkit_settings_set_user_agent() allows content forbidden in HTTP headers 4 https://bugs.webkit.org/show_bug.cgi?id=201077 5 6 Reviewed by Carlos Garcia Campos. 7 8 * UIProcess/API/glib/WebKitSettings.cpp: 9 (webkit_settings_set_user_agent): Check the passed string using the new 10 WebCore::isValidUserAgentHeaderValue() function, and return early without 11 changing the setting if the string is not usable in the User-Agent HTTP 12 header. 13 1 14 2019-09-12 Mark Lam <mark.lam@apple.com> 2 15 -
trunk/Source/WebKit/UIProcess/API/glib/WebKitSettings.cpp
r246353 r249810 36 36 #include "WebPageProxy.h" 37 37 #include "WebPreferences.h" 38 #include <WebCore/HTTPParsers.h> 38 39 #include <WebCore/PlatformScreen.h> 39 40 #include <WebCore/TextEncodingRegistry.h> … … 3044 3045 3045 3046 WebKitSettingsPrivate* priv = settings->priv; 3046 CString newUserAgent = (!userAgent || !strlen(userAgent)) ? WebCore::standardUserAgent("").utf8() : userAgent; 3047 3048 String userAgentString; 3049 if (userAgent && *userAgent) { 3050 userAgentString = String::fromUTF8(userAgent); 3051 g_return_if_fail(WebCore::isValidUserAgentHeaderValue(userAgentString)); 3052 } else 3053 userAgentString = WebCore::standardUserAgent(""); 3054 3055 CString newUserAgent = userAgentString.utf8(); 3047 3056 if (newUserAgent == priv->userAgent) 3048 3057 return; -
trunk/Tools/ChangeLog
r249808 r249810 1 2019-09-12 Adrian Perez de Castro <aperez@igalia.com> 2 3 [GTK][WPE] webkit_settings_set_user_agent() allows content forbidden in HTTP headers 4 https://bugs.webkit.org/show_bug.cgi?id=201077 5 6 Reviewed by Carlos Garcia Campos. 7 8 * TestWebKitAPI/CMakeLists.txt: Add missing HTTPParsers.cpp to be built into TestWebCore. 9 * TestWebKitAPI/Tests/WebCore/HTTPParsers.cpp: 10 (TestWebKitAPI::TEST): Add tests for WebCore::isValidUserAgentHeaderValue(). 11 1 12 2019-09-12 Mark Lam <mark.lam@apple.com> 2 13 -
trunk/Tools/TestWebKitAPI/CMakeLists.txt
r249589 r249810 131 131 Tests/WebCore/GridPosition.cpp 132 132 Tests/WebCore/HTMLParserIdioms.cpp 133 Tests/WebCore/HTTPParsers.cpp 133 134 Tests/WebCore/IntPoint.cpp 134 135 Tests/WebCore/IntRect.cpp -
trunk/Tools/TestWebKitAPI/Tests/WebCore/HTTPParsers.cpp
r233739 r249810 57 57 } 58 58 59 TEST(HTTPParsers, ValidateUserAgentValues) 60 { 61 EXPECT_TRUE(isValidUserAgentHeaderValue("Safari")); 62 EXPECT_TRUE(isValidUserAgentHeaderValue("Safari WebKit")); 63 EXPECT_TRUE(isValidUserAgentHeaderValue("Safari/10.0")); 64 EXPECT_TRUE(isValidUserAgentHeaderValue("Safari WebKit/163")); 65 EXPECT_TRUE(isValidUserAgentHeaderValue("Safari/10.0 WebKit")); 66 EXPECT_TRUE(isValidUserAgentHeaderValue("Safari/10.0 WebKit/163")); 67 EXPECT_TRUE(isValidUserAgentHeaderValue("Safari/10.0 WebKit/163 (Mozilla; like Gecko)")); 68 EXPECT_TRUE(isValidUserAgentHeaderValue("Safari (comment (nested comment))")); 69 EXPECT_TRUE(isValidUserAgentHeaderValue("Safari () (<- Empty comment)")); 70 EXPECT_TRUE(isValidUserAgentHeaderValue("Safari (left paren \\( as quoted pair)")); 71 EXPECT_TRUE(isValidUserAgentHeaderValue("!#$%&'*+-.^_`|~ (non-alphanumeric token characters)")); 72 EXPECT_TRUE(isValidUserAgentHeaderValue("0123456789 (numeric token characters)")); 73 EXPECT_TRUE(isValidUserAgentHeaderValue("a (single character token)")); 74 75 EXPECT_FALSE(isValidUserAgentHeaderValue(" ")); 76 EXPECT_FALSE(isValidUserAgentHeaderValue(" Safari (leading whitespace)")); 77 EXPECT_FALSE(isValidUserAgentHeaderValue("Safari (trailing whitespace) ")); 78 EXPECT_FALSE(isValidUserAgentHeaderValue("\nSafari (leading newline)")); 79 EXPECT_FALSE(isValidUserAgentHeaderValue("Safari (trailing newline)\n")); 80 EXPECT_FALSE(isValidUserAgentHeaderValue("Safari/ (no version token after slash)")); 81 EXPECT_FALSE(isValidUserAgentHeaderValue("Safari (unterminated comment")); 82 EXPECT_FALSE(isValidUserAgentHeaderValue("Safari unopened commanent)")); 83 EXPECT_FALSE(isValidUserAgentHeaderValue("\x1B (contains control character)")); 84 EXPECT_FALSE(isValidUserAgentHeaderValue("Safari/\n10.0 (embeded newline)")); 85 EXPECT_FALSE(isValidUserAgentHeaderValue("WPE\\ WebKit (quoted pair in token)")); 86 EXPECT_FALSE(isValidUserAgentHeaderValue("/123 (missing product token)")); 87 } 88 59 89 } // namespace TestWebKitAPI
Note: See TracChangeset
for help on using the changeset viewer.