safe_builtins.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "extensions/renderer/safe_builtins.h"
  5. #include "base/check.h"
  6. #include "base/notreached.h"
  7. #include "base/strings/stringprintf.h"
  8. #include "extensions/renderer/script_context.h"
  9. #include "extensions/renderer/v8_helpers.h"
  10. #include "v8/include/v8-context.h"
  11. #include "v8/include/v8-exception.h"
  12. #include "v8/include/v8-extension.h"
  13. #include "v8/include/v8-function.h"
  14. #include "v8/include/v8-isolate.h"
  15. #include "v8/include/v8-microtask-queue.h"
  16. #include "v8/include/v8-object.h"
  17. #include "v8/include/v8-primitive-object.h"
  18. #include "v8/include/v8-primitive.h"
  19. #include "v8/include/v8-template.h"
  20. namespace extensions {
  21. namespace {
  22. const char kClassName[] = "extensions::SafeBuiltins";
  23. // Documentation for makeCallback in the JavaScript, out here to reduce the
  24. // (very small) amount of effort that the v8 parser needs to do:
  25. //
  26. // Returns a new object with every function on |obj| configured to call()
  27. // itself with the given arguments.
  28. // E.g. given
  29. // var result = makeCallable(Function.prototype)
  30. // |result| will be a object including 'bind' such that
  31. // result.bind(foo, 1, 2, 3);
  32. // is equivalent to Function.prototype.bind.call(foo, 1, 2, 3), and so on.
  33. // This is a convenient way to save functions that user scripts may clobber.
  34. const char kScript[] =
  35. "(function() {\n"
  36. "'use strict';\n"
  37. "native function Apply();\n"
  38. "native function Save();\n"
  39. "\n"
  40. "// Used in the callback implementation, could potentially be clobbered.\n"
  41. "function makeCallable(obj, target, isStatic, propertyNames) {\n"
  42. " propertyNames.forEach(function(propertyName) {\n"
  43. " var property = obj[propertyName];\n"
  44. " target[propertyName] = function() {\n"
  45. " var recv = obj;\n"
  46. " var firstArgIndex = 0;\n"
  47. " if (!isStatic) {\n"
  48. " if (arguments.length == 0)\n"
  49. " throw 'There must be at least one argument, the receiver';\n"
  50. " recv = arguments[0];\n"
  51. " firstArgIndex = 1;\n"
  52. " }\n"
  53. " return Apply(\n"
  54. " property, recv, arguments, firstArgIndex, arguments.length);\n"
  55. " };\n"
  56. " });\n"
  57. "}\n"
  58. "\n"
  59. "function saveBuiltin(builtin, protoPropertyNames, staticPropertyNames) {\n"
  60. " var safe = function() {\n"
  61. " throw 'Safe objects cannot be called nor constructed. ' +\n"
  62. " 'Use $Foo.self() or new $Foo.self() instead.';\n"
  63. " };\n"
  64. " safe.self = builtin;\n"
  65. " makeCallable(builtin.prototype, safe, false, protoPropertyNames);\n"
  66. " if (staticPropertyNames)\n"
  67. " makeCallable(builtin, safe, true, staticPropertyNames);\n"
  68. " Save(builtin.name, safe);\n"
  69. "}\n"
  70. "\n"
  71. "// Save only what is needed by the extension modules.\n"
  72. "saveBuiltin(Object,\n"
  73. " ['hasOwnProperty'],\n"
  74. " ['create', 'defineProperty', 'freeze',\n"
  75. " 'getOwnPropertyDescriptor', 'getPrototypeOf', 'keys',\n"
  76. " 'assign', 'setPrototypeOf']);\n"
  77. "saveBuiltin(Function,\n"
  78. " ['apply', 'bind', 'call']);\n"
  79. "saveBuiltin(Array,\n"
  80. " ['concat', 'forEach', 'indexOf', 'join', 'push', 'slice',\n"
  81. " 'splice', 'map', 'filter', 'shift', 'unshift', 'pop',\n"
  82. " 'reverse'],\n"
  83. " ['from', 'isArray']);\n"
  84. "saveBuiltin(String,\n"
  85. " ['indexOf', 'slice', 'split', 'substr', 'toLowerCase',\n"
  86. " 'toUpperCase', 'replace']);\n"
  87. "// Use exec rather than test to defend against clobbering in the\n"
  88. "// presence of ES2015 semantics, which read RegExp.prototype.exec.\n"
  89. "saveBuiltin(RegExp,\n"
  90. " ['exec']);\n"
  91. "saveBuiltin(Error,\n"
  92. " [],\n"
  93. " ['captureStackTrace']);\n"
  94. "saveBuiltin(Promise,\n"
  95. " ['then', 'catch']);\n"
  96. "\n"
  97. "// JSON is trickier because extensions can override toJSON in\n"
  98. "// incompatible ways, and we need to prevent that.\n"
  99. "var builtinTypes = [\n"
  100. " Object, Function, Array, String, Boolean, Number, Date, RegExp\n"
  101. "];\n"
  102. "var builtinToJSONs = builtinTypes.map(function(t) {\n"
  103. " return t.toJSON;\n"
  104. "});\n"
  105. "var builtinArray = Array;\n"
  106. "var builtinJSONStringify = JSON.stringify;\n"
  107. "Save('JSON', {\n"
  108. " parse: JSON.parse,\n"
  109. " stringify: function(obj) {\n"
  110. " var savedToJSONs = new builtinArray(builtinTypes.length);\n"
  111. " try {\n"
  112. " for (var i = 0; i < builtinTypes.length; ++i) {\n"
  113. " try {\n"
  114. " if (builtinTypes[i].prototype.toJSON !==\n"
  115. " builtinToJSONs[i]) {\n"
  116. " savedToJSONs[i] = builtinTypes[i].prototype.toJSON;\n"
  117. " builtinTypes[i].prototype.toJSON = builtinToJSONs[i];\n"
  118. " }\n"
  119. " } catch (e) {}\n"
  120. " }\n"
  121. " } catch (e) {}\n"
  122. " try {\n"
  123. " return builtinJSONStringify(obj);\n"
  124. " } finally {\n"
  125. " for (var i = 0; i < builtinTypes.length; ++i) {\n"
  126. " try {\n"
  127. " if (i in savedToJSONs)\n"
  128. " builtinTypes[i].prototype.toJSON = savedToJSONs[i];\n"
  129. " } catch (e) {}\n"
  130. " }\n"
  131. " }\n"
  132. " }\n"
  133. "});\n"
  134. "\n"
  135. "}());\n";
  136. v8::Local<v8::Private> MakeKey(const char* name, v8::Isolate* isolate) {
  137. return v8::Private::ForApi(
  138. isolate, v8_helpers::ToV8StringUnsafe(
  139. isolate, base::StringPrintf("%s::%s", kClassName, name)));
  140. }
  141. void SaveImpl(const char* name,
  142. v8::Local<v8::Value> value,
  143. v8::Local<v8::Context> context) {
  144. CHECK(!value.IsEmpty() && value->IsObject()) << name;
  145. context->Global()
  146. ->SetPrivate(context, MakeKey(name, context->GetIsolate()), value)
  147. .FromJust();
  148. }
  149. v8::Local<v8::Object> Load(const char* name, v8::Local<v8::Context> context) {
  150. v8::Local<v8::Value> value =
  151. context->Global()
  152. ->GetPrivate(context, MakeKey(name, context->GetIsolate()))
  153. .ToLocalChecked();
  154. CHECK(value->IsObject()) << name;
  155. return v8::Local<v8::Object>::Cast(value);
  156. }
  157. class ExtensionImpl : public v8::Extension {
  158. public:
  159. ExtensionImpl() : v8::Extension(kClassName, kScript) {}
  160. private:
  161. v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
  162. v8::Isolate* isolate,
  163. v8::Local<v8::String> name) override {
  164. if (name->StringEquals(v8_helpers::ToV8StringUnsafe(isolate, "Apply")))
  165. return v8::FunctionTemplate::New(isolate, Apply);
  166. if (name->StringEquals(v8_helpers::ToV8StringUnsafe(isolate, "Save")))
  167. return v8::FunctionTemplate::New(isolate, Save);
  168. NOTREACHED() << *v8::String::Utf8Value(isolate, name);
  169. return v8::Local<v8::FunctionTemplate>();
  170. }
  171. static void Apply(const v8::FunctionCallbackInfo<v8::Value>& info) {
  172. CHECK(info.Length() == 5 && info[0]->IsFunction() && // function
  173. // info[1] could be an object or a string
  174. info[2]->IsObject() && // args
  175. info[3]->IsInt32() && // first_arg_index
  176. info[4]->IsInt32()); // args_length
  177. v8::MicrotasksScope microtasks(info.GetIsolate(),
  178. v8::MicrotasksScope::kDoNotRunMicrotasks);
  179. v8::Local<v8::Function> function = info[0].As<v8::Function>();
  180. v8::Local<v8::Object> recv;
  181. if (info[1]->IsObject()) {
  182. recv = v8::Local<v8::Object>::Cast(info[1]);
  183. } else if (info[1]->IsString()) {
  184. recv = v8::StringObject::New(info.GetIsolate(),
  185. v8::Local<v8::String>::Cast(info[1]))
  186. .As<v8::Object>();
  187. } else {
  188. info.GetIsolate()->ThrowException(
  189. v8::Exception::TypeError(v8_helpers::ToV8StringUnsafe(
  190. info.GetIsolate(),
  191. "The first argument is the receiver and must be an object")));
  192. return;
  193. }
  194. v8::Local<v8::Object> args = v8::Local<v8::Object>::Cast(info[2]);
  195. int first_arg_index = info[3].As<v8::Int32>()->Value();
  196. int args_length = info[4].As<v8::Int32>()->Value();
  197. v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
  198. int argc = args_length - first_arg_index;
  199. std::unique_ptr<v8::Local<v8::Value>[]> argv(
  200. new v8::Local<v8::Value>[argc]);
  201. for (int i = 0; i < argc; ++i) {
  202. CHECK(v8_helpers::IsTrue(args->Has(context, i + first_arg_index)));
  203. // Getting a property value could throw an exception.
  204. if (!v8_helpers::GetProperty(context, args, i + first_arg_index,
  205. &argv[i]))
  206. return;
  207. }
  208. v8::Local<v8::Value> return_value;
  209. if (function->Call(context, recv, argc, argv.get()).ToLocal(&return_value))
  210. info.GetReturnValue().Set(return_value);
  211. }
  212. static void Save(const v8::FunctionCallbackInfo<v8::Value>& info) {
  213. CHECK(info.Length() == 2 && info[0]->IsString() && info[1]->IsObject());
  214. SaveImpl(*v8::String::Utf8Value(info.GetIsolate(), info[0]), info[1],
  215. info.GetIsolate()->GetCurrentContext());
  216. }
  217. };
  218. } // namespace
  219. // static
  220. std::unique_ptr<v8::Extension> SafeBuiltins::CreateV8Extension() {
  221. return std::make_unique<ExtensionImpl>();
  222. }
  223. SafeBuiltins::SafeBuiltins(ScriptContext* context) : context_(context) {}
  224. SafeBuiltins::~SafeBuiltins() {}
  225. v8::Local<v8::Object> SafeBuiltins::GetArray() const {
  226. return Load("Array", context_->v8_context());
  227. }
  228. v8::Local<v8::Object> SafeBuiltins::GetFunction() const {
  229. return Load("Function", context_->v8_context());
  230. }
  231. v8::Local<v8::Object> SafeBuiltins::GetJSON() const {
  232. return Load("JSON", context_->v8_context());
  233. }
  234. v8::Local<v8::Object> SafeBuiltins::GetObjekt() const {
  235. return Load("Object", context_->v8_context());
  236. }
  237. v8::Local<v8::Object> SafeBuiltins::GetRegExp() const {
  238. return Load("RegExp", context_->v8_context());
  239. }
  240. v8::Local<v8::Object> SafeBuiltins::GetString() const {
  241. return Load("String", context_->v8_context());
  242. }
  243. v8::Local<v8::Object> SafeBuiltins::GetError() const {
  244. return Load("Error", context_->v8_context());
  245. }
  246. v8::Local<v8::Object> SafeBuiltins::GetPromise() const {
  247. return Load("Promise", context_->v8_context());
  248. }
  249. } // namespace extensions