feat: expose missing process APIs in sandboxed renderers (#13505)

This commit is contained in:
Milan Burda 2018-08-21 20:05:45 +02:00 committed by Samuel Attard
parent f82f89b2a3
commit fc85d02786
11 changed files with 95 additions and 35 deletions

View file

@ -59,7 +59,7 @@ void AtomBindings::BindTo(v8::Isolate* isolate, v8::Local<v8::Object> process) {
dict.SetMethod("getCreationTime", &GetCreationTime); dict.SetMethod("getCreationTime", &GetCreationTime);
dict.SetMethod("getSystemMemoryInfo", &GetSystemMemoryInfo); dict.SetMethod("getSystemMemoryInfo", &GetSystemMemoryInfo);
dict.SetMethod("getCPUUsage", base::Bind(&AtomBindings::GetCPUUsage, dict.SetMethod("getCPUUsage", base::Bind(&AtomBindings::GetCPUUsage,
base::Unretained(this))); base::Unretained(metrics_.get())));
dict.SetMethod("getIOCounters", &GetIOCounters); dict.SetMethod("getIOCounters", &GetIOCounters);
#if defined(OS_POSIX) #if defined(OS_POSIX)
dict.SetMethod("setFdLimit", &base::SetFdLimit); dict.SetMethod("setFdLimit", &base::SetFdLimit);
@ -220,17 +220,19 @@ v8::Local<v8::Value> AtomBindings::GetSystemMemoryInfo(v8::Isolate* isolate,
return dict.GetHandle(); return dict.GetHandle();
} }
v8::Local<v8::Value> AtomBindings::GetCPUUsage(v8::Isolate* isolate) { // static
v8::Local<v8::Value> AtomBindings::GetCPUUsage(base::ProcessMetrics* metrics,
v8::Isolate* isolate) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate); mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.SetHidden("simple", true); dict.SetHidden("simple", true);
int processor_count = base::SysInfo::NumberOfProcessors(); int processor_count = base::SysInfo::NumberOfProcessors();
dict.Set("percentCPUUsage", dict.Set("percentCPUUsage",
metrics_->GetPlatformIndependentCPUUsage() / processor_count); metrics->GetPlatformIndependentCPUUsage() / processor_count);
// NB: This will throw NOTIMPLEMENTED() on Windows // NB: This will throw NOTIMPLEMENTED() on Windows
// For backwards compatibility, we'll return 0 // For backwards compatibility, we'll return 0
#if !defined(OS_WIN) #if !defined(OS_WIN)
dict.Set("idleWakeupsPerSecond", metrics_->GetIdleWakeupsPerSecond()); dict.Set("idleWakeupsPerSecond", metrics->GetIdleWakeupsPerSecond());
#else #else
dict.Set("idleWakeupsPerSecond", 0); dict.Set("idleWakeupsPerSecond", 0);
#endif #endif

View file

@ -40,7 +40,8 @@ class AtomBindings {
static v8::Local<v8::Value> GetCreationTime(v8::Isolate* isolate); static v8::Local<v8::Value> GetCreationTime(v8::Isolate* isolate);
static v8::Local<v8::Value> GetSystemMemoryInfo(v8::Isolate* isolate, static v8::Local<v8::Value> GetSystemMemoryInfo(v8::Isolate* isolate,
mate::Arguments* args); mate::Arguments* args);
v8::Local<v8::Value> GetCPUUsage(v8::Isolate* isolate); static v8::Local<v8::Value> GetCPUUsage(base::ProcessMetrics* metrics,
v8::Isolate* isolate);
static v8::Local<v8::Value> GetIOCounters(v8::Isolate* isolate); static v8::Local<v8::Value> GetIOCounters(v8::Isolate* isolate);
private: private:

View file

@ -15,7 +15,6 @@
#include "base/base_paths.h" #include "base/base_paths.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/environment.h" #include "base/environment.h"
#include "base/files/file_path.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
@ -195,6 +194,10 @@ bool NodeBindings::IsInitialized() {
return g_is_initialized; return g_is_initialized;
} }
base::FilePath::StringType NodeBindings::GetHelperResourcesPath() {
return GetResourcesPath(false).value();
}
void NodeBindings::Initialize() { void NodeBindings::Initialize() {
// Open node's error reporting system for browser process. // Open node's error reporting system for browser process.
node::g_standalone_mode = browser_env_ == BROWSER; node::g_standalone_mode = browser_env_ == BROWSER;

View file

@ -5,6 +5,7 @@
#ifndef ATOM_COMMON_NODE_BINDINGS_H_ #ifndef ATOM_COMMON_NODE_BINDINGS_H_
#define ATOM_COMMON_NODE_BINDINGS_H_ #define ATOM_COMMON_NODE_BINDINGS_H_
#include "base/files/file_path.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
@ -33,6 +34,7 @@ class NodeBindings {
static NodeBindings* Create(BrowserEnvironment browser_env); static NodeBindings* Create(BrowserEnvironment browser_env);
static void RegisterBuiltinModules(); static void RegisterBuiltinModules();
static bool IsInitialized(); static bool IsInitialized();
static base::FilePath::StringType GetHelperResourcesPath();
virtual ~NodeBindings(); virtual ~NodeBindings();

View file

@ -16,6 +16,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/process/process_handle.h"
#include "chrome/renderer/printing/print_web_view_helper.h" #include "chrome/renderer/printing/print_web_view_helper.h"
#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame.h"
#include "native_mate/dictionary.h" #include "native_mate/dictionary.h"
@ -96,27 +97,6 @@ v8::Local<v8::Value> CreatePreloadScript(v8::Isolate* isolate,
return func; return func;
} }
void InitializeBindings(v8::Local<v8::Object> binding,
v8::Local<v8::Context> context) {
auto* isolate = context->GetIsolate();
mate::Dictionary b(isolate, binding);
b.SetMethod("get", GetBinding);
b.SetMethod("createPreloadScript", CreatePreloadScript);
b.SetMethod("crash", AtomBindings::Crash);
b.SetMethod("hang", AtomBindings::Hang);
b.SetMethod("getArgv", GetArgv);
b.SetMethod("getExecPath", GetExecPath);
b.SetMethod("getHeapStatistics", &AtomBindings::GetHeapStatistics);
b.SetMethod("getProcessMemoryInfo", &AtomBindings::GetProcessMemoryInfo);
b.SetMethod("getSystemMemoryInfo", &AtomBindings::GetSystemMemoryInfo);
// Pass in CLI flags needed to setup the renderer
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kGuestInstanceID))
b.Set(options::kGuestInstanceID,
command_line->GetSwitchValueASCII(switches::kGuestInstanceID));
}
class AtomSandboxedRenderFrameObserver : public AtomRenderFrameObserver { class AtomSandboxedRenderFrameObserver : public AtomRenderFrameObserver {
public: public:
AtomSandboxedRenderFrameObserver(content::RenderFrame* render_frame, AtomSandboxedRenderFrameObserver(content::RenderFrame* render_frame,
@ -152,10 +132,38 @@ class AtomSandboxedRenderFrameObserver : public AtomRenderFrameObserver {
AtomSandboxedRendererClient::AtomSandboxedRendererClient() { AtomSandboxedRendererClient::AtomSandboxedRendererClient() {
// Explicitly register electron's builtin modules. // Explicitly register electron's builtin modules.
NodeBindings::RegisterBuiltinModules(); NodeBindings::RegisterBuiltinModules();
metrics_ = base::ProcessMetrics::CreateCurrentProcessMetrics();
} }
AtomSandboxedRendererClient::~AtomSandboxedRendererClient() {} AtomSandboxedRendererClient::~AtomSandboxedRendererClient() {}
void AtomSandboxedRendererClient::InitializeBindings(
v8::Local<v8::Object> binding,
v8::Local<v8::Context> context) {
auto* isolate = context->GetIsolate();
mate::Dictionary b(isolate, binding);
b.SetMethod("get", GetBinding);
b.SetMethod("createPreloadScript", CreatePreloadScript);
b.SetMethod("crash", AtomBindings::Crash);
b.SetMethod("hang", AtomBindings::Hang);
b.SetMethod("getArgv", GetArgv);
b.SetMethod("getExecPath", GetExecPath);
b.SetMethod("getPid", &base::GetCurrentProcId);
b.SetMethod("getResourcesPath", &NodeBindings::GetHelperResourcesPath);
b.SetMethod("getHeapStatistics", &AtomBindings::GetHeapStatistics);
b.SetMethod("getProcessMemoryInfo", &AtomBindings::GetProcessMemoryInfo);
b.SetMethod("getSystemMemoryInfo", &AtomBindings::GetSystemMemoryInfo);
b.SetMethod("getCPUUsage", base::Bind(&AtomBindings::GetCPUUsage,
base::Unretained(metrics_.get())));
b.SetMethod("getIOCounters", &AtomBindings::GetIOCounters);
// Pass in CLI flags needed to setup the renderer
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kGuestInstanceID))
b.Set(options::kGuestInstanceID,
command_line->GetSwitchValueASCII(switches::kGuestInstanceID));
}
void AtomSandboxedRendererClient::RenderFrameCreated( void AtomSandboxedRendererClient::RenderFrameCreated(
content::RenderFrame* render_frame) { content::RenderFrame* render_frame) {
new AtomSandboxedRenderFrameObserver(render_frame, this); new AtomSandboxedRenderFrameObserver(render_frame, this);

View file

@ -8,6 +8,7 @@
#include <vector> #include <vector>
#include "atom/renderer/renderer_client_base.h" #include "atom/renderer/renderer_client_base.h"
#include "base/process/process_metrics.h"
namespace atom { namespace atom {
@ -16,6 +17,8 @@ class AtomSandboxedRendererClient : public RendererClientBase {
AtomSandboxedRendererClient(); AtomSandboxedRendererClient();
~AtomSandboxedRendererClient() override; ~AtomSandboxedRendererClient() override;
void InitializeBindings(v8::Local<v8::Object> binding,
v8::Local<v8::Context> context);
void InvokeIpcCallback(v8::Handle<v8::Context> context, void InvokeIpcCallback(v8::Handle<v8::Context> context,
const std::string& callback_name, const std::string& callback_name,
std::vector<v8::Handle<v8::Value>> args); std::vector<v8::Handle<v8::Value>> args);
@ -30,6 +33,8 @@ class AtomSandboxedRendererClient : public RendererClientBase {
void RenderViewCreated(content::RenderView*) override; void RenderViewCreated(content::RenderView*) override;
private: private:
std::unique_ptr<base::ProcessMetrics> metrics_;
DISALLOW_COPY_AND_ASSIGN(AtomSandboxedRendererClient); DISALLOW_COPY_AND_ASSIGN(AtomSandboxedRendererClient);
}; };

View file

@ -16,10 +16,19 @@ In sandboxed renderers the `process` object contains only a subset of the APIs:
- `getHeapStatistics()` - `getHeapStatistics()`
- `getProcessMemoryInfo()` - `getProcessMemoryInfo()`
- `getSystemMemoryInfo()` - `getSystemMemoryInfo()`
- `getCPUUsage()`
- `getIOCounters()`
- `argv` - `argv`
- `execPath` - `execPath`
- `env` - `env`
- `pid`
- `arch`
- `platform` - `platform`
- `resourcesPath`
- `sandboxed`
- `type`
- `version`
- `versions`
## Events ## Events
@ -68,6 +77,11 @@ instead of the `--no-deprecation` command line flag.
A `String` representing the path to the resources directory. A `String` representing the path to the resources directory.
### `process.sandboxed`
A `Boolean`. When the renderer process is sandboxed, this property is `true`,
otherwise it is `undefined`.
### `process.throwDeprecation` ### `process.throwDeprecation`
A `Boolean` that controls whether or not deprecation warnings will be thrown as A `Boolean` that controls whether or not deprecation warnings will be thrown as

View file

@ -452,9 +452,14 @@ ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
} }
} }
event.returnValue = { event.returnValue = {
preloadSrc, preloadSrc: preloadSrc,
preloadError, preloadError: preloadError,
platform: process.platform, process: {
env: process.env arch: process.arch,
platform: process.platform,
env: process.env,
version: process.version,
versions: process.versions
}
} }
}) })

View file

@ -36,7 +36,7 @@ const loadedModules = new Map([
]) ])
const { const {
preloadSrc, preloadError, platform, env preloadSrc, preloadError, process: processProps
} = electron.ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD') } = electron.ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD')
require('../renderer/web-frame-init')() require('../renderer/web-frame-init')()
@ -49,10 +49,16 @@ preloadProcess.hang = () => binding.hang()
preloadProcess.getHeapStatistics = () => binding.getHeapStatistics() preloadProcess.getHeapStatistics = () => binding.getHeapStatistics()
preloadProcess.getProcessMemoryInfo = () => binding.getProcessMemoryInfo() preloadProcess.getProcessMemoryInfo = () => binding.getProcessMemoryInfo()
preloadProcess.getSystemMemoryInfo = () => binding.getSystemMemoryInfo() preloadProcess.getSystemMemoryInfo = () => binding.getSystemMemoryInfo()
preloadProcess.getCPUUsage = () => binding.getCPUUsage()
preloadProcess.getIOCounters = () => binding.getIOCounters()
preloadProcess.argv = process.argv = binding.getArgv() preloadProcess.argv = process.argv = binding.getArgv()
preloadProcess.execPath = process.execPath = binding.getExecPath() preloadProcess.execPath = process.execPath = binding.getExecPath()
preloadProcess.platform = process.platform = platform preloadProcess.pid = process.pid = binding.getPid()
preloadProcess.env = process.env = env preloadProcess.resourcesPath = binding.getResourcesPath()
preloadProcess.sandboxed = true
preloadProcess.type = 'renderer'
Object.assign(preloadProcess, processProps)
Object.assign(process, processProps)
process.on('exit', () => preloadProcess.emit('exit')) process.on('exit', () => preloadProcess.emit('exit'))

View file

@ -1617,9 +1617,16 @@ describe('BrowserWindow module', () => {
it('validates process APIs access in sandboxed renderer', (done) => { it('validates process APIs access in sandboxed renderer', (done) => {
ipcMain.once('answer', function (event, test) { ipcMain.once('answer', function (event, test) {
assert.equal(test.pid, w.webContents.getOSProcessId())
assert.equal(test.arch, remote.process.arch)
assert.equal(test.platform, remote.process.platform) assert.equal(test.platform, remote.process.platform)
assert.deepEqual(test.env, remote.process.env) assert.deepEqual(test.env, remote.process.env)
assert.equal(test.execPath, remote.process.helperExecPath) assert.equal(test.execPath, remote.process.helperExecPath)
assert.equal(test.resourcesPath, remote.process.resourcesPath)
assert.equal(test.sandboxed, true)
assert.equal(test.type, 'renderer')
assert.equal(test.version, remote.process.version)
assert.deepEqual(test.versions, remote.process.versions)
done() done()
}) })
remote.process.env.sandboxmain = 'foo' remote.process.env.sandboxmain = 'foo'

View file

@ -11,7 +11,14 @@
window.test = { window.test = {
env: process.env, env: process.env,
execPath: process.execPath, execPath: process.execPath,
platform: process.platform pid: process.pid,
arch: process.arch,
platform: process.platform,
resourcesPath: process.resourcesPath,
sandboxed: process.sandboxed,
type: process.type,
version: process.version,
versions: process.versions
} }
} }
} else if (location.href !== 'about:blank') { } else if (location.href !== 'about:blank') {