From 0e97c527aec3d6824b1290bd1443193f50eefc0c Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Thu, 27 Oct 2016 12:34:19 -0400 Subject: [PATCH] Allow CefV8Context::Eval to bypass CSP (issue #2024) --- include/capi/cef_v8_capi.h | 8 +- include/cef_v8.h | 12 +- libcef/renderer/frame_impl.cc | 4 +- libcef/renderer/v8_impl.cc | 28 ++--- libcef/renderer/v8_impl.h | 2 + libcef/renderer/webkit_glue.cc | 47 +++++++ libcef/renderer/webkit_glue.h | 10 ++ libcef_dll/cpptoc/v8context_cpptoc.cc | 7 +- libcef_dll/ctocpp/v8context_ctocpp.cc | 6 +- libcef_dll/ctocpp/v8context_ctocpp.h | 3 +- .../renderer/performance_test_tests.cc | 2 +- tests/unittests/test_handler.cc | 43 +++++-- tests/unittests/test_handler.h | 35 +++++- tests/unittests/v8_unittest.cc | 119 +++++++++++++++--- 14 files changed, 274 insertions(+), 52 deletions(-) diff --git a/include/capi/cef_v8_capi.h b/include/capi/cef_v8_capi.h index 4bc4025d0..7b85b2774 100644 --- a/include/capi/cef_v8_capi.h +++ b/include/capi/cef_v8_capi.h @@ -123,14 +123,16 @@ typedef struct _cef_v8context_t { struct _cef_v8context_t* that); /// - // Evaluates the specified JavaScript code using this context's global object. + // Execute a string of JavaScript code in this V8 context. The |script_url| + // parameter is the URL where the script in question can be found, if any. The + // |start_line| parameter is the base line number to use for error reporting. // On success |retval| will be set to the return value, if any, and the // function will return true (1). On failure |exception| will be set to the // exception, if any, and the function will return false (0). /// int (CEF_CALLBACK *eval)(struct _cef_v8context_t* self, - const cef_string_t* code, struct _cef_v8value_t** retval, - struct _cef_v8exception_t** exception); + const cef_string_t* code, const cef_string_t* script_url, int start_line, + struct _cef_v8value_t** retval, struct _cef_v8exception_t** exception); } cef_v8context_t; diff --git a/include/cef_v8.h b/include/cef_v8.h index 070d4391d..a0f64d074 100644 --- a/include/cef_v8.h +++ b/include/cef_v8.h @@ -206,13 +206,17 @@ class CefV8Context : public virtual CefBase { virtual bool IsSame(CefRefPtr that) =0; /// - // Evaluates the specified JavaScript code using this context's global object. - // On success |retval| will be set to the return value, if any, and the - // function will return true. On failure |exception| will be set to the + // Execute a string of JavaScript code in this V8 context. The |script_url| + // parameter is the URL where the script in question can be found, if any. + // The |start_line| parameter is the base line number to use for error + // reporting. On success |retval| will be set to the return value, if any, and + // the function will return true. On failure |exception| will be set to the // exception, if any, and the function will return false. /// - /*--cef()--*/ + /*--cef(optional_param=script_url)--*/ virtual bool Eval(const CefString& code, + const CefString& script_url, + int start_line, CefRefPtr& retval, CefRefPtr& exception) =0; }; diff --git a/libcef/renderer/frame_impl.cc b/libcef/renderer/frame_impl.cc index 692545293..694c0419a 100644 --- a/libcef/renderer/frame_impl.cc +++ b/libcef/renderer/frame_impl.cc @@ -162,8 +162,8 @@ void CefFrameImpl::ExecuteJavaScript(const CefString& jsCode, if (jsCode.empty()) return; - if (startLine < 0) - startLine = 0; + if (startLine < 1) + startLine = 1; if (frame_) { GURL gurl = GURL(scriptUrl.ToString()); diff --git a/libcef/renderer/v8_impl.cc b/libcef/renderer/v8_impl.cc index 1f925d1f3..75efa4171 100644 --- a/libcef/renderer/v8_impl.cc +++ b/libcef/renderer/v8_impl.cc @@ -1014,8 +1014,13 @@ bool CefV8ContextImpl::IsSame(CefRefPtr that) { } bool CefV8ContextImpl::Eval(const CefString& code, + const CefString& script_url, + int start_line, CefRefPtr& retval, CefRefPtr& exception) { + retval = NULL; + exception = NULL; + CEF_V8_REQUIRE_VALID_HANDLE_RETURN(false); if (webkit_glue::IsScriptForbidden()) @@ -1030,33 +1035,28 @@ bool CefV8ContextImpl::Eval(const CefString& code, v8::HandleScope handle_scope(isolate); v8::Local context = GetV8Context(); v8::Context::Scope context_scope(context); - v8::Local obj = context->Global(); - // Retrieve the eval function. - v8::Local val = obj->Get(v8::String::NewFromUtf8(isolate, "eval")); - if (val.IsEmpty() || !val->IsFunction()) - return false; - - v8::Local func = v8::Local::Cast(val); - v8::Local code_val = GetV8String(isolate, code); + const blink::WebString& source = code.ToString16(); + const blink::WebString& source_url = script_url.ToString16(); v8::TryCatch try_catch(isolate); try_catch.SetVerbose(true); - retval = NULL; - exception = NULL; - v8::MaybeLocal func_rv = - webkit_glue::CallV8Function(context, func, obj, 1, &code_val, - handle_->isolate()); + webkit_glue::ExecuteV8ScriptAndReturnValue(source, source_url, start_line, + context, isolate, try_catch, + blink::AccessControlStatus::NotSharableCrossOrigin); if (try_catch.HasCaught()) { exception = new CefV8ExceptionImpl(context, try_catch.Message()); return false; } else if (!func_rv.IsEmpty()) { retval = new CefV8ValueImpl(isolate, context, func_rv.ToLocalChecked()); + return true; } - return true; + + NOTREACHED(); + return false; } v8::Local CefV8ContextImpl::GetV8Context() { diff --git a/libcef/renderer/v8_impl.h b/libcef/renderer/v8_impl.h index 6161d9673..433ebaaf7 100644 --- a/libcef/renderer/v8_impl.h +++ b/libcef/renderer/v8_impl.h @@ -182,6 +182,8 @@ class CefV8ContextImpl : public CefV8Context { bool Exit() override; bool IsSame(CefRefPtr that) override; bool Eval(const CefString& code, + const CefString& script_url, + int start_line, CefRefPtr& retval, CefRefPtr& exception) override; diff --git a/libcef/renderer/webkit_glue.cc b/libcef/renderer/webkit_glue.cc index 813e78e5e..02f8ff2ac 100644 --- a/libcef/renderer/webkit_glue.cc +++ b/libcef/renderer/webkit_glue.cc @@ -25,12 +25,14 @@ MSVC_PUSH_WARNING_LEVEL(0); #include "third_party/WebKit/public/web/WebViewClient.h" #include "third_party/WebKit/Source/bindings/core/v8/ScriptController.h" +#include "third_party/WebKit/Source/bindings/core/v8/ScriptSourceCode.h" #include "third_party/WebKit/Source/bindings/core/v8/V8Binding.h" #include "third_party/WebKit/Source/core/dom/Document.h" #include "third_party/WebKit/Source/core/dom/Element.h" #include "third_party/WebKit/Source/core/dom/Node.h" #include "third_party/WebKit/Source/core/editing/serializers/Serialization.h" #include "third_party/WebKit/Source/core/frame/LocalFrame.h" +#include "third_party/WebKit/Source/core/frame/Settings.h" #include "third_party/WebKit/Source/web/WebLocalFrameImpl.h" #include "third_party/WebKit/Source/web/WebViewImpl.h" MSVC_POP_WARNING(); @@ -184,6 +186,51 @@ v8::MaybeLocal CallV8Function(v8::Local context, return func_rv; } +v8::MaybeLocal ExecuteV8ScriptAndReturnValue( + const blink::WebString& source, + const blink::WebString& source_url, + int start_line, + v8::Local context, + v8::Isolate* isolate, + v8::TryCatch& tryCatch, + blink::AccessControlStatus accessControlStatus) { + // Based on ScriptController::executeScriptAndReturnValue + DCHECK(isolate); + + if (start_line < 1) + start_line = 1; + + const blink::KURL kurl = source_url.isEmpty() ? + blink::KURL() : blink::KURL(blink::ParsedURLString, source_url); + + const blink::ScriptSourceCode ssc = blink::ScriptSourceCode(source, kurl, + WTF::TextPosition(WTF::OrdinalNumber::fromOneBasedInt(start_line), + WTF::OrdinalNumber::fromZeroBasedInt(0))); + + v8::MaybeLocal result; + + blink::LocalFrame* frame = + toLocalFrame(blink::toFrameIfNotDetached(context)); + DCHECK(frame); + + if (frame) { + blink::V8CacheOptions v8CacheOptions(blink::V8CacheOptionsDefault); + if (frame && frame->settings()) + v8CacheOptions = frame->settings()->v8CacheOptions(); + + v8::Local script; + if (!blink::v8Call(blink::V8ScriptRunner::compileScript(ssc, isolate, + accessControlStatus, v8CacheOptions), script, tryCatch)) { + return result; + } + + result = blink::V8ScriptRunner::runCompiledScript(isolate, script, + blink::toExecutionContext(context)); + } + + return result; +} + bool IsScriptForbidden() { return blink::ScriptForbiddenScope::isScriptForbidden(); } diff --git a/libcef/renderer/webkit_glue.h b/libcef/renderer/webkit_glue.h index 9e178218a..e3a79792b 100644 --- a/libcef/renderer/webkit_glue.h +++ b/libcef/renderer/webkit_glue.h @@ -11,6 +11,7 @@ #include #include "include/internal/cef_types.h" +#include "third_party/WebKit/Source/core/fetch/AccessControlStatus.h" #include "v8/include/v8.h" namespace blink { @@ -52,6 +53,15 @@ v8::MaybeLocal CallV8Function(v8::Local context, v8::Local args[], v8::Isolate* isolate); +v8::MaybeLocal ExecuteV8ScriptAndReturnValue( + const blink::WebString& source, + const blink::WebString& source_url, + int start_line, + v8::Local context, + v8::Isolate* isolate, + v8::TryCatch& tryCatch, + blink::AccessControlStatus accessControlStatus); + bool IsScriptForbidden(); } // webkit_glue diff --git a/libcef_dll/cpptoc/v8context_cpptoc.cc b/libcef_dll/cpptoc/v8context_cpptoc.cc index 21f9fb0bf..5b6470c31 100644 --- a/libcef_dll/cpptoc/v8context_cpptoc.cc +++ b/libcef_dll/cpptoc/v8context_cpptoc.cc @@ -178,8 +178,8 @@ int CEF_CALLBACK v8context_is_same(struct _cef_v8context_t* self, } int CEF_CALLBACK v8context_eval(struct _cef_v8context_t* self, - const cef_string_t* code, struct _cef_v8value_t** retval, - struct _cef_v8exception_t** exception) { + const cef_string_t* code, const cef_string_t* script_url, int start_line, + struct _cef_v8value_t** retval, struct _cef_v8exception_t** exception) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING DCHECK(self); @@ -197,6 +197,7 @@ int CEF_CALLBACK v8context_eval(struct _cef_v8context_t* self, DCHECK(exception); if (!exception) return 0; + // Unverified params: script_url // Translate param: retval; type: refptr_same_byref CefRefPtr retvalPtr; @@ -212,6 +213,8 @@ int CEF_CALLBACK v8context_eval(struct _cef_v8context_t* self, // Execute bool _retval = CefV8ContextCppToC::Get(self)->Eval( CefString(code), + CefString(script_url), + start_line, retvalPtr, exceptionPtr); diff --git a/libcef_dll/ctocpp/v8context_ctocpp.cc b/libcef_dll/ctocpp/v8context_ctocpp.cc index 904e50f07..d97329b76 100644 --- a/libcef_dll/ctocpp/v8context_ctocpp.cc +++ b/libcef_dll/ctocpp/v8context_ctocpp.cc @@ -172,7 +172,8 @@ bool CefV8ContextCToCpp::IsSame(CefRefPtr that) { } bool CefV8ContextCToCpp::Eval(const CefString& code, - CefRefPtr& retval, CefRefPtr& exception) { + const CefString& script_url, int start_line, CefRefPtr& retval, + CefRefPtr& exception) { cef_v8context_t* _struct = GetStruct(); if (CEF_MEMBER_MISSING(_struct, eval)) return false; @@ -183,6 +184,7 @@ bool CefV8ContextCToCpp::Eval(const CefString& code, DCHECK(!code.empty()); if (code.empty()) return false; + // Unverified params: script_url // Translate param: retval; type: refptr_same_byref cef_v8value_t* retvalStruct = NULL; @@ -198,6 +200,8 @@ bool CefV8ContextCToCpp::Eval(const CefString& code, // Execute int _retval = _struct->eval(_struct, code.GetStruct(), + script_url.GetStruct(), + start_line, &retvalStruct, &exceptionStruct); diff --git a/libcef_dll/ctocpp/v8context_ctocpp.h b/libcef_dll/ctocpp/v8context_ctocpp.h index 4c5cd421a..2d065aa77 100644 --- a/libcef_dll/ctocpp/v8context_ctocpp.h +++ b/libcef_dll/ctocpp/v8context_ctocpp.h @@ -38,7 +38,8 @@ class CefV8ContextCToCpp bool Enter() OVERRIDE; bool Exit() OVERRIDE; bool IsSame(CefRefPtr that) OVERRIDE; - bool Eval(const CefString& code, CefRefPtr& retval, + bool Eval(const CefString& code, const CefString& script_url, int start_line, + CefRefPtr& retval, CefRefPtr& exception) OVERRIDE; }; diff --git a/tests/cefclient/renderer/performance_test_tests.cc b/tests/cefclient/renderer/performance_test_tests.cc index 5181c2fb5..ccd387b5f 100644 --- a/tests/cefclient/renderer/performance_test_tests.cc +++ b/tests/cefclient/renderer/performance_test_tests.cc @@ -291,7 +291,7 @@ PERF_TEST_FUNC(V8ContextEval) { CefRefPtr exception; PERF_ITERATIONS_START() - context->Eval(jsCode, retval, exception); + context->Eval(jsCode, CefString(), 0, retval, exception); PERF_ITERATIONS_END() } diff --git a/tests/unittests/test_handler.cc b/tests/unittests/test_handler.cc index 65008e456..296fa89ad 100644 --- a/tests/unittests/test_handler.cc +++ b/tests/unittests/test_handler.cc @@ -260,6 +260,20 @@ void TestHandler::OnBeforeClose(CefRefPtr browser) { browser_count_--; } +namespace { + +CefResponse::HeaderMap ToCefHeaderMap( + const ResourceContent::HeaderMap& headerMap) { + CefResponse::HeaderMap result; + ResourceContent::HeaderMap::const_iterator it = headerMap.begin(); + for (; it != headerMap.end(); ++it) { + result.insert(std::pair(it->first, it->second)); + } + return result; +} + +} // namespace + CefRefPtr TestHandler::GetResourceHandler( CefRefPtr browser, CefRefPtr frame, @@ -280,9 +294,10 @@ CefRefPtr TestHandler::GetResourceHandler( // Return the previously mapped resource CefRefPtr stream = CefStreamReader::CreateForData( - static_cast(const_cast(it->second.first.c_str())), - it->second.first.length()); - return new CefStreamResourceHandler(it->second.second, stream); + static_cast(const_cast(it->second.content().c_str())), + it->second.content().length()); + return new CefStreamResourceHandler(200, "OK", it->second.mimeType(), + ToCefHeaderMap(it->second.headerMap()), stream); } } @@ -401,10 +416,25 @@ void TestHandler::CloseBrowser(CefRefPtr browser, void TestHandler::AddResource(const std::string& url, const std::string& content, - const std::string& mimeType) { + const std::string& mime_type) { + ResourceContent::HeaderMap headerMap = ResourceContent::HeaderMap(); + ResourceContent rc = ResourceContent(content, mime_type, headerMap); + AddResourceEx(url, rc); +} + +void TestHandler::AddResource(const std::string& url, + const std::string& content, + const std::string& mime_type, + const ResourceContent::HeaderMap& header_map) { + ResourceContent rc = ResourceContent(content, mime_type, header_map); + AddResourceEx(url, rc); +} + +void TestHandler::AddResourceEx(const std::string& url, + const ResourceContent& content) { if (!CefCurrentlyOn(TID_IO)) { CefPostTask(TID_IO, - base::Bind(&TestHandler::AddResource, this, url, content, mimeType)); + base::Bind(&TestHandler::AddResourceEx, this, url, content)); return; } @@ -414,8 +444,7 @@ void TestHandler::AddResource(const std::string& url, if (idx > 0) urlStr = urlStr.substr(0, idx); - resource_map_.insert( - std::make_pair(urlStr, std::make_pair(content, mimeType))); + resource_map_.insert(std::make_pair(urlStr, content)); } void TestHandler::ClearResources() { diff --git a/tests/unittests/test_handler.h b/tests/unittests/test_handler.h index a88870d66..96fca1e15 100644 --- a/tests/unittests/test_handler.h +++ b/tests/unittests/test_handler.h @@ -32,6 +32,28 @@ class TrackCallback { bool gotit_; }; +class ResourceContent { + public: + typedef std::multimap HeaderMap; + + ResourceContent(const std::string& content, + const std::string& mime_type, + const HeaderMap& header_map) + : content_(content), + mime_type_(mime_type), + header_map_(header_map) + {} + + const std::string& content() const { return content_; } + const std::string& mimeType() const { return mime_type_; } + const HeaderMap& headerMap() const { return header_map_; } + + private: + std::string content_; + std::string mime_type_; + HeaderMap header_map_; +}; + // Base implementation of CefClient for unit tests. Add new interfaces as needed // by test cases. @@ -235,7 +257,15 @@ class TestHandler : public CefClient, void AddResource(const std::string& url, const std::string& content, - const std::string& mimeType); + const std::string& mime_type); + + void AddResource(const std::string& url, + const std::string& content, + const std::string& mime_type, + const ResourceContent::HeaderMap& header_map); + + void AddResourceEx(const std::string& url, const ResourceContent& content); + void ClearResources(); void SetSignalCompletionWhenAllBrowsersClose(bool val) { @@ -274,8 +304,7 @@ class TestHandler : public CefClient, // Map of resources that can be automatically loaded. Only accessed on the // IO thread. - typedef std::map > - ResourceMap; + typedef std::map ResourceMap; ResourceMap resource_map_; // If true test completion will be signaled when all browsers have closed. diff --git a/tests/unittests/v8_unittest.cc b/tests/unittests/v8_unittest.cc index 8cdc159b1..1b9314d0c 100644 --- a/tests/unittests/v8_unittest.cc +++ b/tests/unittests/v8_unittest.cc @@ -31,6 +31,10 @@ const char kV8BindingTestUrl[] = "http://tests/V8Test.BindingTest"; const char kV8ContextParentTestUrl[] = "http://tests/V8Test.ContextParentTest"; const char kV8ContextChildTestUrl[] = "http://tests/V8Test.ContextChildTest"; const char kV8NavTestUrl[] = "http://tests/V8Test.NavTest"; +const char kV8ContextEvalCspBypassUnsafeEval[] = + "http://tests/V8Test.ContextEvalCspBypassUnsafeEval"; +const char kV8ContextEvalCspBypassSandbox[] = + "http://tests/V8Test.ContextEvalCspBypassSandbox"; const char kV8OnUncaughtExceptionTestUrl[] = "http://tests/V8Test.OnUncaughtException"; const char kV8TestMsg[] = "V8Test.Test"; @@ -71,6 +75,8 @@ enum V8TestMode { V8TEST_FUNCTION_HANDLER_EMPTY_STRING, V8TEST_CONTEXT_EVAL, V8TEST_CONTEXT_EVAL_EXCEPTION, + V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL, + V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX, V8TEST_CONTEXT_ENTERED, V8TEST_CONTEXT_INVALID, V8TEST_BINDING, @@ -213,6 +219,12 @@ class V8RendererTest : public ClientAppRenderer::Delegate, case V8TEST_CONTEXT_EVAL_EXCEPTION: RunContextEvalExceptionTest(); break; + case V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL: + RunContextEvalCspBypassUnsafeEval(); + break; + case V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX: + RunContextEvalCspBypassSandbox(); + break; case V8TEST_CONTEXT_ENTERED: RunContextEnteredTest(); break; @@ -890,7 +902,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, CefRefPtr retval; CefRefPtr exception; - EXPECT_TRUE(context->Eval(test.str(), retval, exception)); + EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception)); if (exception.get()) ADD_FAILURE() << exception->GetMessage().c_str(); @@ -929,7 +941,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, CefRefPtr retval; CefRefPtr exception; - EXPECT_TRUE(context->Eval(test.str(), retval, exception)); + EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception)); if (exception.get()) ADD_FAILURE() << exception->GetMessage().c_str(); @@ -971,7 +983,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, CefRefPtr retval; CefRefPtr exception; - EXPECT_TRUE(context->Eval(test.str(), retval, exception)); + EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception)); if (exception.get()) ADD_FAILURE() << exception->GetMessage().c_str(); @@ -1013,7 +1025,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, CefRefPtr retval; CefRefPtr exception; - EXPECT_TRUE(context->Eval(test.str(), retval, exception)); + EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception)); if (exception.get()) ADD_FAILURE() << exception->GetMessage().c_str(); @@ -1053,7 +1065,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, CefRefPtr retval; CefRefPtr exception; - EXPECT_TRUE(context->Eval(test.str(), retval, exception)); + EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception)); if (exception.get()) ADD_FAILURE() << exception->GetMessage().c_str(); @@ -1093,7 +1105,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, CefRefPtr retval; CefRefPtr exception; - EXPECT_TRUE(context->Eval(test.str(), retval, exception)); + EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception)); if (exception.get()) ADD_FAILURE() << exception->GetMessage().c_str(); @@ -1514,7 +1526,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, CefRefPtr retval; CefRefPtr exception; - EXPECT_TRUE(context->Eval("1+2", retval, exception)); + EXPECT_TRUE(context->Eval("1+2", CefString(), 0, retval, exception)); EXPECT_TRUE(retval.get()); EXPECT_TRUE(retval->IsInt()); EXPECT_EQ(3, retval->GetIntValue()); @@ -1529,9 +1541,58 @@ class V8RendererTest : public ClientAppRenderer::Delegate, CefRefPtr retval; CefRefPtr exception; - EXPECT_FALSE(context->Eval("1+foo", retval, exception)); + EXPECT_FALSE(context->Eval("\n\n\n1+foo", CefString(), 0, retval, exception)); EXPECT_FALSE(retval.get()); EXPECT_TRUE(exception.get()); + EXPECT_EQ(4, exception->GetLineNumber()); + + DestroyTest(); + } + + void RunContextEvalCspBypassUnsafeEval() { + CefRefPtr context = GetContext(); + + CefRefPtr retval; + CefRefPtr exception; + + bool success = context->Eval( + "(document.getElementById('result').innerHTML)", + CefString(), 0, retval, exception); + if (exception.get()) { + ADD_FAILURE() << exception->GetMessage().c_str(); + EXPECT_FALSE(success); + } + + EXPECT_TRUE(success); + EXPECT_TRUE(retval.get()); + if (retval.get()) { + EXPECT_TRUE(retval->IsString()); + EXPECT_EQ(CefString("CSP_BYPASSED"), retval->GetStringValue()); + } + + DestroyTest(); + } + + void RunContextEvalCspBypassSandbox() { + CefRefPtr context = GetContext(); + + CefRefPtr retval; + CefRefPtr exception; + + bool success = context->Eval( + "(document.getElementById('result').innerHTML)", + CefString(), 0, retval, exception); + if (exception.get()) { + ADD_FAILURE() << exception->GetMessage().c_str(); + EXPECT_FALSE(success); + } + + EXPECT_TRUE(success); + EXPECT_TRUE(retval.get()); + if (retval.get()) { + EXPECT_TRUE(retval->IsString()); + EXPECT_EQ(CefString("CSP_BYPASSED"), retval->GetStringValue()); + } DestroyTest(); } @@ -1545,7 +1606,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, // Test value defined in OnContextCreated EXPECT_TRUE(context->Eval( "document.getElementById('f').contentWindow.v8_context_entered_test()", - retval, exception)); + CefString(), 0, retval, exception)); if (exception.get()) ADD_FAILURE() << exception->GetMessage().c_str(); @@ -1624,7 +1685,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_TRUE(context->Eval( "function jsfunc() { return window.myfunc(); }\n" "jsfunc();", - retval, exception)); + CefString(), 0, retval, exception)); EXPECT_TRUE(retval.get()); EXPECT_TRUE(retval->IsInt()); EXPECT_EQ(3, retval->GetIntValue()); @@ -1642,7 +1703,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_EQ(1, frame->GetLineNumber()); EXPECT_EQ(35, frame->GetColumn()); EXPECT_TRUE(frame.get()); - EXPECT_TRUE(frame->IsEval()); + EXPECT_FALSE(frame->IsEval()); EXPECT_FALSE(frame->IsConstructor()); frame = handler->stack_trace_->GetFrame(1); @@ -1652,7 +1713,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_EQ(2, frame->GetLineNumber()); EXPECT_EQ(1, frame->GetColumn()); EXPECT_TRUE(frame.get()); - EXPECT_TRUE(frame->IsEval()); + EXPECT_FALSE(frame->IsEval()); EXPECT_FALSE(frame->IsConstructor()); // Exit the V8 context. @@ -1711,6 +1772,10 @@ class V8RendererTest : public ClientAppRenderer::Delegate, } if (test_mode_ > V8TEST_NONE) RunStartupTest(); + if (test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL || + test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX) { + browser_ = browser; + } } CefRefPtr GetLoadHandler( @@ -1930,7 +1995,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, CefRefPtr retval; CefRefPtr exception; EXPECT_TRUE(browser->GetMainFrame()->GetV8Context()->Eval( - "window.close()", retval, exception)); + "window.close()", CefString(), 0, retval, exception)); } protected: @@ -1990,7 +2055,27 @@ class V8TestHandler : public TestHandler { void RunTest() override { // Nested script tag forces creation of the V8 context. - if (test_mode_ == V8TEST_CONTEXT_ENTERED) { + if (test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL || + test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX) { + std::string url; + ResourceContent::HeaderMap headers; + if (test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL) { + url = kV8ContextEvalCspBypassUnsafeEval; + headers.insert(std::pair( + "Content-Security-Policy", "script-src 'self'")); + } else if (test_mode_ == V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX) { + url = kV8ContextEvalCspBypassSandbox; + headers.insert(std::pair( + "Content-Security-Policy", "sandbox")); + } else { + NOTREACHED(); + } + AddResource(url, "" + + url + + "" + "", "text/html", headers); + CreateBrowser(test_url_); + } else if (test_mode_ == V8TEST_CONTEXT_ENTERED) { AddResource(kV8ContextParentTestUrl, "" "" @@ -2161,6 +2246,12 @@ V8_TEST(FunctionHandlerWithContext, V8TEST_FUNCTION_HANDLER_WITH_CONTEXT); V8_TEST(FunctionHandlerEmptyString, V8TEST_FUNCTION_HANDLER_EMPTY_STRING); V8_TEST(ContextEval, V8TEST_CONTEXT_EVAL); V8_TEST(ContextEvalException, V8TEST_CONTEXT_EVAL_EXCEPTION); +V8_TEST_EX(ContextEvalCspBypassUnsafeEval, + V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL, + kV8ContextEvalCspBypassUnsafeEval); +V8_TEST_EX(ContextEvalCspBypassSandbox, + V8TEST_CONTEXT_EVAL_CSP_BYPASS_SANDBOX, + kV8ContextEvalCspBypassSandbox); V8_TEST_EX(ContextEntered, V8TEST_CONTEXT_ENTERED, NULL); V8_TEST(ContextInvalid, V8TEST_CONTEXT_INVALID); V8_TEST_EX(Binding, V8TEST_BINDING, kV8BindingTestUrl);