This source file includes following definitions.
- isNonCanonicalCharacter
- canonicalize
- isRequiredForInjection
- isTerminatingCharacter
- isHTMLQuote
- isJSNewline
- startsHTMLCommentAt
- startsSingleLineCommentAt
- startsMultiLineCommentAt
- startsOpeningScriptTagAt
- threadSafeMatch
- hasName
- findAttributeWithName
- isNameOfInlineEventHandler
- isDangerousHTTPEquiv
- decode16BitUnicodeEscapeSequences
- decodeStandardURLEscapeSequences
- fullyDecodeString
- combineXSSProtectionHeaderAndCSP
- isSemicolonSeparatedAttribute
- semicolonSeparatedValueContainsJavaScriptURL
- m_encoding
- initForFragment
- init
- setEncoding
- filterToken
- filterStartToken
- filterEndToken
- filterCharacterToken
- filterScriptToken
- filterObjectToken
- filterParamToken
- filterEmbedToken
- filterAppletToken
- filterFrameToken
- filterMetaToken
- filterBaseToken
- filterFormToken
- filterInputToken
- filterButtonToken
- eraseDangerousAttributesIfInjected
- eraseAttributeIfInjected
- decodedSnippetForName
- decodedSnippetForAttribute
- decodedSnippetForJavaScript
- isContainedInRequest
- isLikelySafeResource
- isSafeToSendToAnotherThread
#include "config.h"
#include "core/html/parser/XSSAuditor.h"
#include "HTMLNames.h"
#include "SVGNames.h"
#include "XLinkNames.h"
#include "core/dom/Document.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/csp/ContentSecurityPolicy.h"
#include "core/html/HTMLParamElement.h"
#include "core/html/parser/HTMLDocumentParser.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "core/html/parser/TextResourceDecoder.h"
#include "core/html/parser/XSSAuditorDelegate.h"
#include "core/loader/DocumentLoader.h"
#include "core/frame/Settings.h"
#include "platform/JSONValues.h"
#include "platform/network/FormData.h"
#include "platform/text/DecodeEscapeSequences.h"
#include "wtf/ASCIICType.h"
#include "wtf/MainThread.h"
namespace {
const char kURLWithUniqueOrigin[] = "data:,";
}
namespace WebCore {
using namespace HTMLNames;
static bool isNonCanonicalCharacter(UChar c)
{
return (c == '\\' || c == '0' || c == '\0' || c == '/' || c >= 127);
}
static String canonicalize(const String& string)
{
return string.removeCharacters(&isNonCanonicalCharacter);
}
static bool isRequiredForInjection(UChar c)
{
return (c == '\'' || c == '"' || c == '<' || c == '>');
}
static bool isTerminatingCharacter(UChar c)
{
return (c == '&' || c == '/' || c == '"' || c == '\'' || c == '<' || c == '>' || c == ',');
}
static bool isHTMLQuote(UChar c)
{
return (c == '"' || c == '\'');
}
static bool isJSNewline(UChar c)
{
return (c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029);
}
static bool startsHTMLCommentAt(const String& string, size_t start)
{
return (start + 3 < string.length() && string[start] == '<' && string[start + 1] == '!' && string[start + 2] == '-' && string[start + 3] == '-');
}
static bool startsSingleLineCommentAt(const String& string, size_t start)
{
return (start + 1 < string.length() && string[start] == '/' && string[start + 1] == '/');
}
static bool startsMultiLineCommentAt(const String& string, size_t start)
{
return (start + 1 < string.length() && string[start] == '/' && string[start + 1] == '*');
}
static bool startsOpeningScriptTagAt(const String& string, size_t start)
{
return start + 6 < string.length() && string[start] == '<'
&& WTF::toASCIILowerUnchecked(string[start + 1]) == 's'
&& WTF::toASCIILowerUnchecked(string[start + 2]) == 'c'
&& WTF::toASCIILowerUnchecked(string[start + 3]) == 'r'
&& WTF::toASCIILowerUnchecked(string[start + 4]) == 'i'
&& WTF::toASCIILowerUnchecked(string[start + 5]) == 'p'
&& WTF::toASCIILowerUnchecked(string[start + 6]) == 't';
}
template<size_t inlineCapacity>
bool threadSafeMatch(const Vector<UChar, inlineCapacity>& vector, const QualifiedName& qname)
{
return equalIgnoringNullity(vector, qname.localName().impl());
}
static bool hasName(const HTMLToken& token, const QualifiedName& name)
{
return threadSafeMatch(token.name(), name);
}
static bool findAttributeWithName(const HTMLToken& token, const QualifiedName& name, size_t& indexOfMatchingAttribute)
{
const String& attrName = name.namespaceURI() == XLinkNames::xlinkNamespaceURI ? "xlink:" + name.localName().string() : name.localName().string();
for (size_t i = 0; i < token.attributes().size(); ++i) {
if (equalIgnoringNullity(token.attributes().at(i).name, attrName)) {
indexOfMatchingAttribute = i;
return true;
}
}
return false;
}
static bool isNameOfInlineEventHandler(const Vector<UChar, 32>& name)
{
const size_t lengthOfShortestInlineEventHandlerName = 5;
if (name.size() < lengthOfShortestInlineEventHandlerName)
return false;
return name[0] == 'o' && name[1] == 'n';
}
static bool isDangerousHTTPEquiv(const String& value)
{
String equiv = value.stripWhiteSpace();
return equalIgnoringCase(equiv, "refresh") || equalIgnoringCase(equiv, "set-cookie");
}
static inline String decode16BitUnicodeEscapeSequences(const String& string)
{
return decodeEscapeSequences<Unicode16BitEscapeSequence>(string, UTF8Encoding());
}
static inline String decodeStandardURLEscapeSequences(const String& string, const WTF::TextEncoding& encoding)
{
return decodeEscapeSequences<URLEscapeSequence>(string, encoding);
}
static String fullyDecodeString(const String& string, const WTF::TextEncoding& encoding)
{
size_t oldWorkingStringLength;
String workingString = string;
do {
oldWorkingStringLength = workingString.length();
workingString = decode16BitUnicodeEscapeSequences(decodeStandardURLEscapeSequences(workingString, encoding));
} while (workingString.length() < oldWorkingStringLength);
workingString.replace('+', ' ');
return workingString;
}
static ReflectedXSSDisposition combineXSSProtectionHeaderAndCSP(ReflectedXSSDisposition xssProtection, ReflectedXSSDisposition reflectedXSS)
{
ReflectedXSSDisposition result = std::max(xssProtection, reflectedXSS);
if (result == ReflectedXSSInvalid || result == FilterReflectedXSS || result == ReflectedXSSUnset)
return FilterReflectedXSS;
return result;
}
static bool isSemicolonSeparatedAttribute(const HTMLToken::Attribute& attribute)
{
return threadSafeMatch(attribute.name, SVGNames::valuesAttr);
}
static bool semicolonSeparatedValueContainsJavaScriptURL(const String& value)
{
Vector<String> valueList;
value.split(';', valueList);
for (size_t i = 0; i < valueList.size(); ++i) {
if (protocolIsJavaScript(valueList[i]))
return true;
}
return false;
}
XSSAuditor::XSSAuditor()
: m_isEnabled(false)
, m_xssProtection(FilterReflectedXSS)
, m_didSendValidCSPHeader(false)
, m_didSendValidXSSProtectionHeader(false)
, m_state(Uninitialized)
, m_scriptTagFoundInRequest(false)
, m_scriptTagNestingLevel(0)
, m_encoding(UTF8Encoding())
{
}
void XSSAuditor::initForFragment()
{
ASSERT(isMainThread());
ASSERT(m_state == Uninitialized);
m_state = FilteringTokens;
ASSERT(!m_isEnabled);
}
void XSSAuditor::init(Document* document, XSSAuditorDelegate* auditorDelegate)
{
ASSERT(isMainThread());
if (m_state != Uninitialized)
return;
m_state = FilteringTokens;
if (Settings* settings = document->settings())
m_isEnabled = settings->xssAuditorEnabled();
if (!m_isEnabled)
return;
m_documentURL = document->url().copy();
if (!document->frame()) {
m_isEnabled = false;
return;
}
if (m_documentURL.isEmpty()) {
m_isEnabled = false;
return;
}
if (m_documentURL.protocolIsData()) {
m_isEnabled = false;
return;
}
if (document->encoding().isValid())
m_encoding = document->encoding();
if (DocumentLoader* documentLoader = document->frame()->loader().documentLoader()) {
DEFINE_STATIC_LOCAL(const AtomicString, XSSProtectionHeader, ("X-XSS-Protection", AtomicString::ConstructFromLiteral));
const AtomicString& headerValue = documentLoader->response().httpHeaderField(XSSProtectionHeader);
String errorDetails;
unsigned errorPosition = 0;
String reportURL;
KURL xssProtectionReportURL;
ReflectedXSSDisposition xssProtectionHeader = parseXSSProtectionHeader(headerValue, errorDetails, errorPosition, reportURL);
m_didSendValidXSSProtectionHeader = xssProtectionHeader != ReflectedXSSUnset && xssProtectionHeader != ReflectedXSSInvalid;
if ((xssProtectionHeader == FilterReflectedXSS || xssProtectionHeader == BlockReflectedXSS) && !reportURL.isEmpty()) {
xssProtectionReportURL = document->completeURL(reportURL);
if (MixedContentChecker::isMixedContent(document->securityOrigin(), xssProtectionReportURL)) {
errorDetails = "insecure reporting URL for secure page";
xssProtectionHeader = ReflectedXSSInvalid;
xssProtectionReportURL = KURL();
}
}
if (xssProtectionHeader == ReflectedXSSInvalid)
document->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Error parsing header X-XSS-Protection: " + headerValue + ": " + errorDetails + " at character position " + String::format("%u", errorPosition) + ". The default protections will be applied.");
ReflectedXSSDisposition cspHeader = document->contentSecurityPolicy()->reflectedXSSDisposition();
m_didSendValidCSPHeader = cspHeader != ReflectedXSSUnset && cspHeader != ReflectedXSSInvalid;
m_xssProtection = combineXSSProtectionHeaderAndCSP(xssProtectionHeader, cspHeader);
if (auditorDelegate)
auditorDelegate->setReportURL(xssProtectionReportURL.copy());
FormData* httpBody = documentLoader->request().httpBody();
if (httpBody && !httpBody->isEmpty())
m_httpBodyAsString = httpBody->flattenToString();
}
setEncoding(m_encoding);
}
void XSSAuditor::setEncoding(const WTF::TextEncoding& encoding)
{
const size_t miniumLengthForSuffixTree = 512;
const int suffixTreeDepth = 5;
if (!encoding.isValid())
return;
m_encoding = encoding;
m_decodedURL = canonicalize(fullyDecodeString(m_documentURL.string(), m_encoding));
if (m_decodedURL.find(isRequiredForInjection) == kNotFound)
m_decodedURL = String();
if (!m_httpBodyAsString.isEmpty()) {
m_decodedHTTPBody = canonicalize(fullyDecodeString(m_httpBodyAsString, m_encoding));
m_httpBodyAsString = String();
if (m_decodedHTTPBody.find(isRequiredForInjection) == kNotFound)
m_decodedHTTPBody = String();
if (m_decodedHTTPBody.length() >= miniumLengthForSuffixTree)
m_decodedHTTPBodySuffixTree = adoptPtr(new SuffixTree<ASCIICodebook>(m_decodedHTTPBody, suffixTreeDepth));
}
if (m_decodedURL.isEmpty() && m_decodedHTTPBody.isEmpty())
m_isEnabled = false;
}
PassOwnPtr<XSSInfo> XSSAuditor::filterToken(const FilterTokenRequest& request)
{
ASSERT(m_state != Uninitialized);
if (!m_isEnabled || m_xssProtection == AllowReflectedXSS)
return nullptr;
bool didBlockScript = false;
if (request.token.type() == HTMLToken::StartTag)
didBlockScript = filterStartToken(request);
else if (m_scriptTagNestingLevel) {
if (request.token.type() == HTMLToken::Character)
didBlockScript = filterCharacterToken(request);
else if (request.token.type() == HTMLToken::EndTag)
filterEndToken(request);
}
if (didBlockScript) {
bool didBlockEntirePage = (m_xssProtection == BlockReflectedXSS);
OwnPtr<XSSInfo> xssInfo = XSSInfo::create(m_documentURL, didBlockEntirePage, m_didSendValidXSSProtectionHeader, m_didSendValidCSPHeader);
return xssInfo.release();
}
return nullptr;
}
bool XSSAuditor::filterStartToken(const FilterTokenRequest& request)
{
m_state = FilteringTokens;
bool didBlockScript = eraseDangerousAttributesIfInjected(request);
if (hasName(request.token, scriptTag)) {
didBlockScript |= filterScriptToken(request);
ASSERT(request.shouldAllowCDATA || !m_scriptTagNestingLevel);
m_scriptTagNestingLevel++;
} else if (hasName(request.token, objectTag))
didBlockScript |= filterObjectToken(request);
else if (hasName(request.token, paramTag))
didBlockScript |= filterParamToken(request);
else if (hasName(request.token, embedTag))
didBlockScript |= filterEmbedToken(request);
else if (hasName(request.token, appletTag))
didBlockScript |= filterAppletToken(request);
else if (hasName(request.token, iframeTag) || hasName(request.token, frameTag))
didBlockScript |= filterFrameToken(request);
else if (hasName(request.token, metaTag))
didBlockScript |= filterMetaToken(request);
else if (hasName(request.token, baseTag))
didBlockScript |= filterBaseToken(request);
else if (hasName(request.token, formTag))
didBlockScript |= filterFormToken(request);
else if (hasName(request.token, inputTag))
didBlockScript |= filterInputToken(request);
else if (hasName(request.token, buttonTag))
didBlockScript |= filterButtonToken(request);
return didBlockScript;
}
void XSSAuditor::filterEndToken(const FilterTokenRequest& request)
{
ASSERT(m_scriptTagNestingLevel);
m_state = FilteringTokens;
if (hasName(request.token, scriptTag)) {
m_scriptTagNestingLevel--;
ASSERT(request.shouldAllowCDATA || !m_scriptTagNestingLevel);
}
}
bool XSSAuditor::filterCharacterToken(const FilterTokenRequest& request)
{
ASSERT(m_scriptTagNestingLevel);
ASSERT(m_state != Uninitialized);
if (m_state == PermittingAdjacentCharacterTokens)
return false;
if ((m_state == SuppressingAdjacentCharacterTokens)
|| (m_scriptTagFoundInRequest && isContainedInRequest(decodedSnippetForJavaScript(request)))) {
request.token.eraseCharacters();
request.token.appendToCharacter(' ');
m_state = SuppressingAdjacentCharacterTokens;
return true;
}
m_state = PermittingAdjacentCharacterTokens;
return false;
}
bool XSSAuditor::filterScriptToken(const FilterTokenRequest& request)
{
ASSERT(request.token.type() == HTMLToken::StartTag);
ASSERT(hasName(request.token, scriptTag));
bool didBlockScript = false;
m_scriptTagFoundInRequest = isContainedInRequest(decodedSnippetForName(request));
if (m_scriptTagFoundInRequest) {
didBlockScript |= eraseAttributeIfInjected(request, srcAttr, blankURL().string(), SrcLikeAttribute);
didBlockScript |= eraseAttributeIfInjected(request, XLinkNames::hrefAttr, blankURL().string(), SrcLikeAttribute);
}
return didBlockScript;
}
bool XSSAuditor::filterObjectToken(const FilterTokenRequest& request)
{
ASSERT(request.token.type() == HTMLToken::StartTag);
ASSERT(hasName(request.token, objectTag));
bool didBlockScript = false;
if (isContainedInRequest(decodedSnippetForName(request))) {
didBlockScript |= eraseAttributeIfInjected(request, dataAttr, blankURL().string(), SrcLikeAttribute);
didBlockScript |= eraseAttributeIfInjected(request, typeAttr);
didBlockScript |= eraseAttributeIfInjected(request, classidAttr);
}
return didBlockScript;
}
bool XSSAuditor::filterParamToken(const FilterTokenRequest& request)
{
ASSERT(request.token.type() == HTMLToken::StartTag);
ASSERT(hasName(request.token, paramTag));
size_t indexOfNameAttribute;
if (!findAttributeWithName(request.token, nameAttr, indexOfNameAttribute))
return false;
const HTMLToken::Attribute& nameAttribute = request.token.attributes().at(indexOfNameAttribute);
if (!HTMLParamElement::isURLParameter(String(nameAttribute.value)))
return false;
return eraseAttributeIfInjected(request, valueAttr, blankURL().string(), SrcLikeAttribute);
}
bool XSSAuditor::filterEmbedToken(const FilterTokenRequest& request)
{
ASSERT(request.token.type() == HTMLToken::StartTag);
ASSERT(hasName(request.token, embedTag));
bool didBlockScript = false;
if (isContainedInRequest(decodedSnippetForName(request))) {
didBlockScript |= eraseAttributeIfInjected(request, codeAttr, String(), SrcLikeAttribute);
didBlockScript |= eraseAttributeIfInjected(request, srcAttr, blankURL().string(), SrcLikeAttribute);
didBlockScript |= eraseAttributeIfInjected(request, typeAttr);
}
return didBlockScript;
}
bool XSSAuditor::filterAppletToken(const FilterTokenRequest& request)
{
ASSERT(request.token.type() == HTMLToken::StartTag);
ASSERT(hasName(request.token, appletTag));
bool didBlockScript = false;
if (isContainedInRequest(decodedSnippetForName(request))) {
didBlockScript |= eraseAttributeIfInjected(request, codeAttr, String(), SrcLikeAttribute);
didBlockScript |= eraseAttributeIfInjected(request, objectAttr);
}
return didBlockScript;
}
bool XSSAuditor::filterFrameToken(const FilterTokenRequest& request)
{
ASSERT(request.token.type() == HTMLToken::StartTag);
ASSERT(hasName(request.token, iframeTag) || hasName(request.token, frameTag));
bool didBlockScript = eraseAttributeIfInjected(request, srcdocAttr, String(), ScriptLikeAttribute);
if (isContainedInRequest(decodedSnippetForName(request)))
didBlockScript |= eraseAttributeIfInjected(request, srcAttr, String(), SrcLikeAttribute);
return didBlockScript;
}
bool XSSAuditor::filterMetaToken(const FilterTokenRequest& request)
{
ASSERT(request.token.type() == HTMLToken::StartTag);
ASSERT(hasName(request.token, metaTag));
return eraseAttributeIfInjected(request, http_equivAttr);
}
bool XSSAuditor::filterBaseToken(const FilterTokenRequest& request)
{
ASSERT(request.token.type() == HTMLToken::StartTag);
ASSERT(hasName(request.token, baseTag));
return eraseAttributeIfInjected(request, hrefAttr);
}
bool XSSAuditor::filterFormToken(const FilterTokenRequest& request)
{
ASSERT(request.token.type() == HTMLToken::StartTag);
ASSERT(hasName(request.token, formTag));
return eraseAttributeIfInjected(request, actionAttr, kURLWithUniqueOrigin);
}
bool XSSAuditor::filterInputToken(const FilterTokenRequest& request)
{
ASSERT(request.token.type() == HTMLToken::StartTag);
ASSERT(hasName(request.token, inputTag));
return eraseAttributeIfInjected(request, formactionAttr, kURLWithUniqueOrigin, SrcLikeAttribute);
}
bool XSSAuditor::filterButtonToken(const FilterTokenRequest& request)
{
ASSERT(request.token.type() == HTMLToken::StartTag);
ASSERT(hasName(request.token, buttonTag));
return eraseAttributeIfInjected(request, formactionAttr, kURLWithUniqueOrigin, SrcLikeAttribute);
}
bool XSSAuditor::eraseDangerousAttributesIfInjected(const FilterTokenRequest& request)
{
DEFINE_STATIC_LOCAL(String, safeJavaScriptURL, ("javascript:void(0)"));
bool didBlockScript = false;
for (size_t i = 0; i < request.token.attributes().size(); ++i) {
const HTMLToken::Attribute& attribute = request.token.attributes().at(i);
bool isInlineEventHandler = isNameOfInlineEventHandler(attribute.name);
String strippedValue = stripLeadingAndTrailingHTMLSpaces(String(attribute.value));
bool valueContainsJavaScriptURL = (!isInlineEventHandler && protocolIsJavaScript(strippedValue)) || (isSemicolonSeparatedAttribute(attribute) && semicolonSeparatedValueContainsJavaScriptURL(strippedValue));
if (!isInlineEventHandler && !valueContainsJavaScriptURL)
continue;
if (!isContainedInRequest(decodedSnippetForAttribute(request, attribute, ScriptLikeAttribute)))
continue;
request.token.eraseValueOfAttribute(i);
if (valueContainsJavaScriptURL)
request.token.appendToAttributeValue(i, safeJavaScriptURL);
didBlockScript = true;
}
return didBlockScript;
}
bool XSSAuditor::eraseAttributeIfInjected(const FilterTokenRequest& request, const QualifiedName& attributeName, const String& replacementValue, AttributeKind treatment)
{
size_t indexOfAttribute = 0;
if (findAttributeWithName(request.token, attributeName, indexOfAttribute)) {
const HTMLToken::Attribute& attribute = request.token.attributes().at(indexOfAttribute);
if (isContainedInRequest(decodedSnippetForAttribute(request, attribute, treatment))) {
if (threadSafeMatch(attributeName, srcAttr) && isLikelySafeResource(String(attribute.value)))
return false;
if (threadSafeMatch(attributeName, http_equivAttr) && !isDangerousHTTPEquiv(String(attribute.value)))
return false;
request.token.eraseValueOfAttribute(indexOfAttribute);
if (!replacementValue.isEmpty())
request.token.appendToAttributeValue(indexOfAttribute, replacementValue);
return true;
}
}
return false;
}
String XSSAuditor::decodedSnippetForName(const FilterTokenRequest& request)
{
return canonicalize(fullyDecodeString(request.sourceTracker.sourceForToken(request.token), m_encoding).substring(0, request.token.name().size() + 1));
}
String XSSAuditor::decodedSnippetForAttribute(const FilterTokenRequest& request, const HTMLToken::Attribute& attribute, AttributeKind treatment)
{
int start = attribute.nameRange.start - request.token.startIndex();
int end = attribute.valueRange.end - request.token.startIndex();
String decodedSnippet = fullyDecodeString(request.sourceTracker.sourceForToken(request.token).substring(start, end - start), m_encoding);
decodedSnippet.truncate(kMaximumFragmentLengthTarget);
if (treatment == SrcLikeAttribute) {
int slashCount = 0;
bool commaSeen = false;
for (size_t currentLength = 0; currentLength < decodedSnippet.length(); ++currentLength) {
UChar currentChar = decodedSnippet[currentLength];
if (currentChar == '?'
|| currentChar == '#'
|| ((currentChar == '/' || currentChar == '\\') && (commaSeen || ++slashCount > 2))
|| (currentChar == '<' && commaSeen)) {
decodedSnippet.truncate(currentLength);
break;
}
if (currentChar == ',')
commaSeen = true;
}
} else if (treatment == ScriptLikeAttribute) {
size_t position = 0;
if ((position = decodedSnippet.find("=")) != kNotFound
&& (position = decodedSnippet.find(isNotHTMLSpace<UChar>, position + 1)) != kNotFound
&& (position = decodedSnippet.find(isTerminatingCharacter, isHTMLQuote(decodedSnippet[position]) ? position + 1 : position)) != kNotFound) {
decodedSnippet.truncate(position);
}
}
return canonicalize(decodedSnippet);
}
String XSSAuditor::decodedSnippetForJavaScript(const FilterTokenRequest& request)
{
String string = request.sourceTracker.sourceForToken(request.token);
size_t startPosition = 0;
size_t endPosition = string.length();
size_t foundPosition = kNotFound;
size_t lastNonSpacePosition = kNotFound;
while (startPosition < endPosition) {
while (startPosition < endPosition && isHTMLSpace<UChar>(string[startPosition]))
startPosition++;
if (request.shouldAllowCDATA)
break;
if (startsHTMLCommentAt(string, startPosition) || startsSingleLineCommentAt(string, startPosition)) {
while (startPosition < endPosition && !isJSNewline(string[startPosition]))
startPosition++;
} else if (startsMultiLineCommentAt(string, startPosition)) {
if (startPosition + 2 < endPosition && (foundPosition = string.find("*/", startPosition + 2)) != kNotFound)
startPosition = foundPosition + 2;
else
startPosition = endPosition;
} else
break;
}
String result;
while (startPosition < endPosition && !result.length()) {
lastNonSpacePosition = kNotFound;
for (foundPosition = startPosition; foundPosition < endPosition; foundPosition++) {
if (!request.shouldAllowCDATA) {
if (startsSingleLineCommentAt(string, foundPosition)
|| startsMultiLineCommentAt(string, foundPosition)
|| startsHTMLCommentAt(string, foundPosition)) {
break;
}
}
if (string[foundPosition] == ',')
break;
if (lastNonSpacePosition != kNotFound && startsOpeningScriptTagAt(string, foundPosition)) {
foundPosition = lastNonSpacePosition;
break;
}
if (foundPosition > startPosition + kMaximumFragmentLengthTarget) {
if (isHTMLSpace<UChar>(string[foundPosition]))
break;
}
if (!isHTMLSpace<UChar>(string[foundPosition]))
lastNonSpacePosition = foundPosition;
}
result = canonicalize(fullyDecodeString(string.substring(startPosition, foundPosition - startPosition), m_encoding));
startPosition = foundPosition + 1;
}
return result;
}
bool XSSAuditor::isContainedInRequest(const String& decodedSnippet)
{
if (decodedSnippet.isEmpty())
return false;
if (m_decodedURL.find(decodedSnippet, 0, false) != kNotFound)
return true;
if (m_decodedHTTPBodySuffixTree && !m_decodedHTTPBodySuffixTree->mightContain(decodedSnippet))
return false;
return m_decodedHTTPBody.find(decodedSnippet, 0, false) != kNotFound;
}
bool XSSAuditor::isLikelySafeResource(const String& url)
{
if (url.isEmpty() || url == blankURL().string())
return true;
if (m_documentURL.host().isEmpty())
return false;
KURL resourceURL(m_documentURL, url);
return (m_documentURL.host() == resourceURL.host() && resourceURL.query().isEmpty());
}
bool XSSAuditor::isSafeToSendToAnotherThread() const
{
return m_documentURL.isSafeToSendToAnotherThread()
&& m_decodedURL.isSafeToSendToAnotherThread()
&& m_decodedHTTPBody.isSafeToSendToAnotherThread()
&& m_httpBodyAsString.isSafeToSendToAnotherThread();
}
}