#include "config.h"
#include "XSSAuditor.h"
#include <wtf/StdLibExtras.h>
#include "Console.h"
#include "CString.h"
#include "DocumentLoader.h"
#include "DOMWindow.h"
#include "Frame.h"
#include "KURL.h"
#include "ResourceResponseBase.h"
#include "ScriptSourceCode.h"
#include "Settings.h"
#include "TextResourceDecoder.h"
using namespace WTF;
namespace WebCore {
static bool isNonNullControlCharacter(UChar c)
{
return (c > '\0' && c < ' ') || c == 127;
}
XSSAuditor::XSSAuditor(Frame* frame)
: m_frame(frame)
{
}
XSSAuditor::~XSSAuditor()
{
}
bool XSSAuditor::isEnabled() const
{
Settings* settings = m_frame->settings();
return (settings && settings->xssAuditorEnabled());
}
bool XSSAuditor::canEvaluate(const String& sourceCode) const
{
if (!isEnabled())
return true;
if (findInRequest(sourceCode, false)) {
DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute a JavaScript script. Source code of script found within request.\n"));
m_frame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, consoleMessage, 1, String());
return false;
}
return true;
}
bool XSSAuditor::canCreateInlineEventListener(const String&, const String& code) const
{
if (!isEnabled())
return true;
if (findInRequest(code)) {
DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute a JavaScript script. Source code of script found within request.\n"));
m_frame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, consoleMessage, 1, String());
return false;
}
return true;
}
bool XSSAuditor::canLoadExternalScriptFromSrc(const String& url) const
{
if (!isEnabled())
return true;
if (findInRequest(url)) {
DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute a JavaScript script. Source code of script found within request.\n"));
m_frame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, consoleMessage, 1, String());
return false;
}
return true;
}
bool XSSAuditor::canLoadObject(const String& url) const
{
if (!isEnabled())
return true;
if (findInRequest(url, false, false)) {
DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute a JavaScript script. Source code of script found within request"));
m_frame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, consoleMessage, 1, String());
return false;
}
return true;
}
bool XSSAuditor::canSetBaseElementURL(const String& url) const
{
if (!isEnabled())
return true;
KURL baseElementURL(m_frame->document()->url(), url);
if (m_frame->document()->url().baseAsString() != baseElementURL.baseAsString() && findInRequest(url)) {
DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute a JavaScript script. Source code of script found within request"));
m_frame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, consoleMessage, 1, String());
return false;
}
return true;
}
String XSSAuditor::decodeURL(const String& str, const TextEncoding& encoding, bool allowNullCharacters,
bool allowNonNullControlCharacters)
{
String result;
String url = str;
url.replace('+', ' ');
result = decodeURLEscapeSequences(url);
String decodedResult = encoding.decode(result.utf8().data(), result.length());
if (!decodedResult.isEmpty())
result = decodedResult;
if (!allowNullCharacters)
result = StringImpl::createStrippingNullCharacters(result.characters(), result.length());
if (!allowNonNullControlCharacters) {
decodedResult = result.removeCharacters(&isNonNullControlCharacter);
if (!decodedResult.isEmpty())
result = decodedResult;
}
return result;
}
bool XSSAuditor::findInRequest(const String& string, bool matchNullCharacters, bool matchNonNullControlCharacters) const
{
bool result = false;
Frame* parentFrame = m_frame->tree()->parent();
if (parentFrame && m_frame->document()->url() == blankURL())
result = findInRequest(parentFrame, string, matchNullCharacters, matchNonNullControlCharacters);
if (!result)
result = findInRequest(m_frame, string, matchNullCharacters, matchNonNullControlCharacters);
return result;
}
bool XSSAuditor::findInRequest(Frame* frame, const String& string, bool matchNullCharacters, bool matchNonNullControlCharacters) const
{
ASSERT(frame->document());
String pageURL = frame->document()->url().string();
if (!frame->document()->decoder()) {
return false;
}
if (protocolIs(pageURL, "data"))
return false;
if (string.isEmpty())
return false;
if (string.length() < pageURL.length()) {
String decodedPageURL = decodeURL(pageURL, frame->document()->decoder()->encoding(), matchNullCharacters,
matchNonNullControlCharacters);
if (decodedPageURL.find(string, 0, false) != -1)
return true; }
FormData* formDataObj = frame->loader()->documentLoader()->originalRequest().httpBody();
if (formDataObj && !formDataObj->isEmpty()) {
String formData = formDataObj->flattenToString();
if (string.length() < formData.length()) {
String decodedFormData = decodeURL(formData, frame->document()->decoder()->encoding(), matchNullCharacters,
matchNonNullControlCharacters);
if (decodedFormData.find(string, 0, false) != -1)
return true; }
}
return false;
}
}