Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(739)

Side by Side Diff: Source/WebCore/html/parser/XSSFilter.cpp

Issue 7011006: Merge 86087 - 2011-05-09 Adam Barth <abarth@webkit.org> (Closed) Base URL: http://svn.webkit.org/repository/webkit/branches/chromium/742/
Patch Set: Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « LayoutTests/http/tests/security/xssAuditor/meta-tag-http-refresh-x-frame-options-expected.txt ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2011 Adam Barth. All Rights Reserved. 2 * Copyright (C) 2011 Adam Barth. All Rights Reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 25 matching lines...) Expand all
36 #include "HTMLParserIdioms.h" 36 #include "HTMLParserIdioms.h"
37 #include "Settings.h" 37 #include "Settings.h"
38 #include "TextEncoding.h" 38 #include "TextEncoding.h"
39 #include "TextResourceDecoder.h" 39 #include "TextResourceDecoder.h"
40 #include <wtf/text/CString.h> 40 #include <wtf/text/CString.h>
41 41
42 namespace WebCore { 42 namespace WebCore {
43 43
44 using namespace HTMLNames; 44 using namespace HTMLNames;
45 45
46 namespace { 46 static bool isNonCanonicalCharacter(UChar c)
47
48 bool isNonCanonicalCharacter(UChar c)
49 { 47 {
50 // We remove all non-ASCII characters, including non-printable ASCII charact ers. 48 // We remove all non-ASCII characters, including non-printable ASCII charact ers.
51 // 49 //
52 // Note, we don't remove backslashes like PHP stripslashes(), which among ot her things converts "\\0" to the \0 character. 50 // Note, we don't remove backslashes like PHP stripslashes(), which among ot her things converts "\\0" to the \0 character.
53 // Instead, we remove backslashes and zeros (since the string "\\0" =(remove backslashes)=> "0"). However, this has the 51 // Instead, we remove backslashes and zeros (since the string "\\0" =(remove backslashes)=> "0"). However, this has the
54 // adverse effect that we remove any legitimate zeros from a string. 52 // adverse effect that we remove any legitimate zeros from a string.
55 // 53 //
56 // For instance: new String("http://localhost:8000") => new String("http://l ocalhost:8"). 54 // For instance: new String("http://localhost:8000") => new String("http://l ocalhost:8").
57 return (c == '\\' || c == '0' || c == '\0' || c >= 127); 55 return (c == '\\' || c == '0' || c == '\0' || c >= 127);
58 } 56 }
59 57
60 String canonicalize(const String& string) 58 static String canonicalize(const String& string)
61 { 59 {
62 return string.removeCharacters(&isNonCanonicalCharacter); 60 return string.removeCharacters(&isNonCanonicalCharacter);
63 } 61 }
64 62
65 bool isRequiredForInjection(UChar c) 63 static bool isRequiredForInjection(UChar c)
66 { 64 {
67 return (c == '\'' || c == '"' || c == '<' || c == '>'); 65 return (c == '\'' || c == '"' || c == '<' || c == '>');
68 } 66 }
69 67
70 bool hasName(const HTMLToken& token, const QualifiedName& name) 68 static bool hasName(const HTMLToken& token, const QualifiedName& name)
71 { 69 {
72 return equalIgnoringNullity(token.name(), static_cast<const String&>(name.lo calName())); 70 return equalIgnoringNullity(token.name(), static_cast<const String&>(name.lo calName()));
73 } 71 }
74 72
75 bool findAttributeWithName(const HTMLToken& token, const QualifiedName& name, si ze_t& indexOfMatchingAttribute) 73 static bool findAttributeWithName(const HTMLToken& token, const QualifiedName& n ame, size_t& indexOfMatchingAttribute)
76 { 74 {
77 for (size_t i = 0; i < token.attributes().size(); ++i) { 75 for (size_t i = 0; i < token.attributes().size(); ++i) {
78 if (equalIgnoringNullity(token.attributes().at(i).m_name, name.localName ())) { 76 if (equalIgnoringNullity(token.attributes().at(i).m_name, name.localName ())) {
79 indexOfMatchingAttribute = i; 77 indexOfMatchingAttribute = i;
80 return true; 78 return true;
81 } 79 }
82 } 80 }
83 return false; 81 return false;
84 } 82 }
85 83
86 bool isNameOfInlineEventHandler(const Vector<UChar, 32>& name) 84 static bool isNameOfInlineEventHandler(const Vector<UChar, 32>& name)
87 { 85 {
88 const size_t lengthOfShortestInlineEventHandlerName = 5; // To wit: oncut. 86 const size_t lengthOfShortestInlineEventHandlerName = 5; // To wit: oncut.
89 if (name.size() < lengthOfShortestInlineEventHandlerName) 87 if (name.size() < lengthOfShortestInlineEventHandlerName)
90 return false; 88 return false;
91 return name[0] == 'o' && name[1] == 'n'; 89 return name[0] == 'o' && name[1] == 'n';
92 } 90 }
93 91
94 bool containsJavaScriptURL(const Vector<UChar, 32>& value) 92 static bool isDangerousHTTPEquiv(const String& value)
93 {
94 String equiv = value.stripWhiteSpace();
95 return equalIgnoringCase(equiv, "refresh") || equalIgnoringCase(equiv, "set- cookie");
96 }
97
98 static bool containsJavaScriptURL(const Vector<UChar, 32>& value)
95 { 99 {
96 static const char javaScriptScheme[] = "javascript:"; 100 static const char javaScriptScheme[] = "javascript:";
97 static const size_t lengthOfJavaScriptScheme = sizeof(javaScriptScheme) - 1; 101 static const size_t lengthOfJavaScriptScheme = sizeof(javaScriptScheme) - 1;
98 102
99 size_t i; 103 size_t i;
100 for (i = 0; i < value.size(); ++i) { 104 for (i = 0; i < value.size(); ++i) {
101 if (!isHTMLSpace(value[i])) 105 if (!isHTMLSpace(value[i]))
102 break; 106 break;
103 } 107 }
104 108
105 if (value.size() - i < lengthOfJavaScriptScheme) 109 if (value.size() - i < lengthOfJavaScriptScheme)
106 return false; 110 return false;
107 111
108 return equalIgnoringCase(value.data() + i, javaScriptScheme, lengthOfJavaScr iptScheme); 112 return equalIgnoringCase(value.data() + i, javaScriptScheme, lengthOfJavaScr iptScheme);
109 } 113 }
110 114
111 String decodeURL(const String& string, const TextEncoding& encoding) 115 static String decodeURL(const String& string, const TextEncoding& encoding)
112 { 116 {
113 String workingString = string; 117 String workingString = string;
114 workingString.replace('+', ' '); 118 workingString.replace('+', ' ');
115 workingString = decodeURLEscapeSequences(workingString); 119 workingString = decodeURLEscapeSequences(workingString);
116 CString workingStringUTF8 = workingString.utf8(); 120 CString workingStringUTF8 = workingString.utf8();
117 String decodedString = encoding.decode(workingStringUTF8.data(), workingStri ngUTF8.length()); 121 String decodedString = encoding.decode(workingStringUTF8.data(), workingStri ngUTF8.length());
118 // FIXME: Is this check necessary? 122 // FIXME: Is this check necessary?
119 if (decodedString.isEmpty()) 123 if (decodedString.isEmpty())
120 return canonicalize(workingString); 124 return canonicalize(workingString);
121 return canonicalize(decodedString); 125 return canonicalize(decodedString);
122 } 126 }
123 127
124 }
125
126 XSSFilter::XSSFilter(HTMLDocumentParser* parser) 128 XSSFilter::XSSFilter(HTMLDocumentParser* parser)
127 : m_parser(parser) 129 : m_parser(parser)
128 , m_isEnabled(false) 130 , m_isEnabled(false)
129 , m_xssProtection(XSSProtectionEnabled) 131 , m_xssProtection(XSSProtectionEnabled)
130 , m_state(Uninitialized) 132 , m_state(Uninitialized)
131 { 133 {
132 ASSERT(m_parser); 134 ASSERT(m_parser);
133 if (Frame* frame = parser->document()->frame()) { 135 if (Frame* frame = parser->document()->frame()) {
134 if (Settings* settings = frame->settings()) 136 if (Settings* settings = frame->settings())
135 m_isEnabled = settings->xssAuditorEnabled(); 137 m_isEnabled = settings->xssAuditorEnabled();
(...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after
413 } 415 }
414 416
415 bool XSSFilter::eraseAttributeIfInjected(HTMLToken& token, const QualifiedName& attributeName, const String& replacementValue) 417 bool XSSFilter::eraseAttributeIfInjected(HTMLToken& token, const QualifiedName& attributeName, const String& replacementValue)
416 { 418 {
417 size_t indexOfAttribute; 419 size_t indexOfAttribute;
418 if (findAttributeWithName(token, attributeName, indexOfAttribute)) { 420 if (findAttributeWithName(token, attributeName, indexOfAttribute)) {
419 const HTMLToken::Attribute& attribute = token.attributes().at(indexOfAtt ribute); 421 const HTMLToken::Attribute& attribute = token.attributes().at(indexOfAtt ribute);
420 if (isContainedInRequest(snippetForAttribute(token, attribute))) { 422 if (isContainedInRequest(snippetForAttribute(token, attribute))) {
421 if (attributeName == srcAttr && isSameOriginResource(String(attribut e.m_value.data(), attribute.m_value.size()))) 423 if (attributeName == srcAttr && isSameOriginResource(String(attribut e.m_value.data(), attribute.m_value.size())))
422 return false; 424 return false;
425 if (attributeName == http_equivAttr && !isDangerousHTTPEquiv(String( attribute.m_value.data(), attribute.m_value.size())))
426 return false;
423 token.eraseValueOfAttribute(indexOfAttribute); 427 token.eraseValueOfAttribute(indexOfAttribute);
424 if (!replacementValue.isEmpty()) 428 if (!replacementValue.isEmpty())
425 token.appendToAttributeValue(indexOfAttribute, replacementValue) ; 429 token.appendToAttributeValue(indexOfAttribute, replacementValue) ;
426 return true; 430 return true;
427 } 431 }
428 } 432 }
429 return false; 433 return false;
430 } 434 }
431 435
432 String XSSFilter::snippetForRange(const HTMLToken& token, int start, int end) 436 String XSSFilter::snippetForRange(const HTMLToken& token, int start, int end)
(...skipping 30 matching lines...) Expand all
463 // probably not an XSS attack, so we reduce false positives by allowing the 467 // probably not an XSS attack, so we reduce false positives by allowing the
464 // request. If the resource has a query string, we're more suspicious, 468 // request. If the resource has a query string, we're more suspicious,
465 // however, because that's pretty rare and the attacker might be able to 469 // however, because that's pretty rare and the attacker might be able to
466 // trick a server-side script into doing something dangerous with the query 470 // trick a server-side script into doing something dangerous with the query
467 // string. 471 // string.
468 KURL resourceURL(m_parser->document()->url(), url); 472 KURL resourceURL(m_parser->document()->url(), url);
469 return (m_parser->document()->url().host() == resourceURL.host() && resource URL.query().isEmpty()); 473 return (m_parser->document()->url().host() == resourceURL.host() && resource URL.query().isEmpty());
470 } 474 }
471 475
472 } 476 }
OLDNEW
« no previous file with comments | « LayoutTests/http/tests/security/xssAuditor/meta-tag-http-refresh-x-frame-options-expected.txt ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698