Skip to content

Commit

Permalink
feat: add creationTime / sandboxed / integrityLevel to app.getAppMetr…
Browse files Browse the repository at this point in the history
…ics()
  • Loading branch information
Milan Burda committed Jun 11, 2019
1 parent 3a1d6d2 commit a23362e
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 49 deletions.
68 changes: 38 additions & 30 deletions atom/browser/api/atom_api_app.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,25 @@ using atom::Browser;
namespace mate {

#if defined(OS_WIN)
template <>
struct Converter<atom::ProcessIntegrityLevel> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
atom::ProcessIntegrityLevel value) {
switch (value) {
case atom::ProcessIntegrityLevel::Untrusted:
return mate::StringToV8(isolate, "untrusted");
case atom::ProcessIntegrityLevel::Low:
return mate::StringToV8(isolate, "low");
case atom::ProcessIntegrityLevel::Medium:
return mate::StringToV8(isolate, "medium");
case atom::ProcessIntegrityLevel::High:
return mate::StringToV8(isolate, "high");
default:
return mate::StringToV8(isolate, "unknown");
}
}
};

template <>
struct Converter<Browser::UserTask> {
static bool FromV8(v8::Isolate* isolate,
Expand Down Expand Up @@ -357,31 +376,10 @@ struct Converter<content::CertificateRequestResultType> {

namespace atom {

ProcessMetric::ProcessMetric(int type,
base::ProcessId pid,
std::unique_ptr<base::ProcessMetrics> metrics) {
this->type = type;
this->pid = pid;
this->metrics = std::move(metrics);
}

ProcessMetric::~ProcessMetric() = default;

namespace api {

namespace {

class AppIdProcessIterator : public base::ProcessIterator {
public:
AppIdProcessIterator() : base::ProcessIterator(nullptr) {}

protected:
bool IncludeEntry() override {
return (entry().parent_pid() == base::GetCurrentProcId() ||
entry().pid() == base::GetCurrentProcId());
}
};

IconLoader::IconSize GetIconSizeByString(const std::string& size) {
if (size == "small") {
return IconLoader::IconSize::SMALL;
Expand Down Expand Up @@ -550,7 +548,7 @@ App::App(v8::Isolate* isolate) {

base::ProcessId pid = base::GetCurrentProcId();
auto process_metric = std::make_unique<atom::ProcessMetric>(
content::PROCESS_TYPE_BROWSER, pid,
content::PROCESS_TYPE_BROWSER, base::GetCurrentProcessHandle(),
base::ProcessMetrics::CreateCurrentProcessMetrics());
app_metrics_[pid] = std::move(process_metric);
Init(isolate);
Expand Down Expand Up @@ -825,15 +823,13 @@ void App::ChildProcessLaunched(int process_type, base::ProcessHandle handle) {
auto pid = base::GetProcId(handle);

#if defined(OS_MACOSX)
std::unique_ptr<base::ProcessMetrics> metrics(
base::ProcessMetrics::CreateProcessMetrics(
handle, content::BrowserChildProcessHost::GetPortProvider()));
auto metrics = base::ProcessMetrics::CreateProcessMetrics(
handle, content::BrowserChildProcessHost::GetPortProvider());
#else
std::unique_ptr<base::ProcessMetrics> metrics(
base::ProcessMetrics::CreateProcessMetrics(handle));
auto metrics = base::ProcessMetrics::CreateProcessMetrics(handle);
#endif
app_metrics_[pid] = std::make_unique<atom::ProcessMetric>(process_type, pid,
std::move(metrics));
app_metrics_[pid] = std::make_unique<atom::ProcessMetric>(
process_type, handle, std::move(metrics));
}

void App::ChildProcessDisconnected(base::ProcessId pid) {
Expand Down Expand Up @@ -1215,9 +1211,21 @@ std::vector<mate::Dictionary> App::GetAppMetrics(v8::Isolate* isolate) {
#endif

pid_dict.Set("cpu", cpu_dict);
pid_dict.Set("pid", process_metric.second->pid);
pid_dict.Set("pid", process_metric.second->process.Pid());
pid_dict.Set("type", content::GetProcessTypeNameInEnglish(
process_metric.second->type));
pid_dict.Set("creationTime",
process_metric.second->process.CreationTime().ToJsTime());

#if defined(OS_MACOSX)
pid_dict.Set("sandboxed", process_metric.second->IsSandboxed());
#elif defined(OS_WIN)
auto integrity_level = process_metric.second->GetIntegrityLevel();
auto sandboxed = ProcessMetric::IsSandboxed(integrity_level);
pid_dict.Set("integrityLevel", integrity_level);
pid_dict.Set("sandboxed", sandboxed);
#endif

result.push_back(pid_dict);
}

Expand Down
14 changes: 1 addition & 13 deletions atom/browser/api/atom_api_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@
#include <vector>

#include "atom/browser/api/event_emitter.h"
#include "atom/browser/api/process_metric.h"
#include "atom/browser/atom_browser_client.h"
#include "atom/browser/browser.h"
#include "atom/browser/browser_observer.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/promise_util.h"
#include "base/process/process_iterator.h"
#include "base/process/process_metrics.h"
#include "base/task/cancelable_task_tracker.h"
#include "chrome/browser/icon_manager.h"
#include "chrome/browser/process_singleton.h"
Expand Down Expand Up @@ -49,17 +48,6 @@ namespace atom {
enum class JumpListResult : int;
#endif

struct ProcessMetric {
int type;
base::ProcessId pid;
std::unique_ptr<base::ProcessMetrics> metrics;

ProcessMetric(int type,
base::ProcessId pid,
std::unique_ptr<base::ProcessMetrics> metrics);
~ProcessMetric();
};

namespace api {

class App : public AtomBrowserClient::Delegate,
Expand Down
109 changes: 109 additions & 0 deletions atom/browser/api/process_metric.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "atom/browser/api/process_metric.h"

#include <memory>
#include <utility>

#if defined(OS_WIN)
#include <windows.h>
#endif

#if defined(OS_MACOSX)
extern "C" int sandbox_check(pid_t pid, const char* operation, int type, ...);
#endif

namespace atom {

ProcessMetric::ProcessMetric(int type,
base::ProcessHandle handle,
std::unique_ptr<base::ProcessMetrics> metrics) {
this->type = type;
this->metrics = std::move(metrics);

#if defined(OS_WIN)
HANDLE duplicate_handle = INVALID_HANDLE_VALUE;
::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(),
&duplicate_handle, 0, false, DUPLICATE_SAME_ACCESS);
this->process = base::Process(duplicate_handle);
#else
this->process = base::Process(handle);
#endif
}

ProcessMetric::~ProcessMetric() = default;

#if defined(OS_WIN)

ProcessIntegrityLevel ProcessMetric::GetIntegrityLevel() const {
HANDLE token = nullptr;
if (!::OpenProcessToken(process.Handle(), TOKEN_QUERY, &token)) {
return ProcessIntegrityLevel::Unknown;
}

base::win::ScopedHandle token_scoped(token);

DWORD token_info_length = 0;
if (::GetTokenInformation(token, TokenIntegrityLevel, nullptr, 0,
&token_info_length) ||
::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
return ProcessIntegrityLevel::Unknown;
}

auto token_label_bytes = std::make_unique<char[]>(token_info_length);
TOKEN_MANDATORY_LABEL* token_label =
reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_label_bytes.get());
if (!::GetTokenInformation(token, TokenIntegrityLevel, token_label,
token_info_length, &token_info_length)) {
return ProcessIntegrityLevel::Unknown;
}

DWORD integrity_level = *::GetSidSubAuthority(
token_label->Label.Sid,
static_cast<DWORD>(*::GetSidSubAuthorityCount(token_label->Label.Sid) -
1));

if (integrity_level >= SECURITY_MANDATORY_UNTRUSTED_RID &&
integrity_level < SECURITY_MANDATORY_LOW_RID) {
return ProcessIntegrityLevel::Untrusted;
}

if (integrity_level >= SECURITY_MANDATORY_LOW_RID &&
integrity_level < SECURITY_MANDATORY_MEDIUM_RID) {
return ProcessIntegrityLevel::Low;
}

if (integrity_level >= SECURITY_MANDATORY_MEDIUM_RID &&
integrity_level < SECURITY_MANDATORY_HIGH_RID) {
return ProcessIntegrityLevel::Medium;
}

if (integrity_level >= SECURITY_MANDATORY_HIGH_RID &&
integrity_level < SECURITY_MANDATORY_SYSTEM_RID) {
return ProcessIntegrityLevel::High;
}

return ProcessIntegrityLevel::Unknown;
}

// static
bool ProcessMetric::IsSandboxed(ProcessIntegrityLevel integrity_level) {
return integrity_level > ProcessIntegrityLevel::Unknown &&
integrity_level < ProcessIntegrityLevel::Medium;
}

#elif defined(OS_MACOSX)

bool ProcessMetric::IsSandboxed() const {
#if defined(MAS_BUILD)
return true;
#else
return sandbox_check(process.Pid(), nullptr, 0) != 0;
#endif
}

#endif // defined(OS_MACOSX)

} // namespace atom
46 changes: 46 additions & 0 deletions atom/browser/api/process_metric.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#ifndef ATOM_BROWSER_API_PROCESS_METRIC_H_
#define ATOM_BROWSER_API_PROCESS_METRIC_H_

#include <memory>

#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/process/process_metrics.h"

namespace atom {

#if defined(OS_WIN)
enum class ProcessIntegrityLevel {
Unknown,
Untrusted,
Low,
Medium,
High,
};
#endif

struct ProcessMetric {
int type;
base::Process process;
std::unique_ptr<base::ProcessMetrics> metrics;

ProcessMetric(int type,
base::ProcessHandle handle,
std::unique_ptr<base::ProcessMetrics> metrics);
~ProcessMetric();

#if defined(OS_WIN)
ProcessIntegrityLevel GetIntegrityLevel() const;
static bool IsSandboxed(ProcessIntegrityLevel integrity_level);
#elif defined(OS_MACOSX)
bool IsSandboxed() const;
#endif
};

} // namespace atom

#endif // ATOM_BROWSER_API_PROCESS_METRIC_H_
9 changes: 9 additions & 0 deletions docs/api/structures/process-metric.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,12 @@
* `pid` Integer - Process id of the process.
* `type` String - Process type (Browser or Tab or GPU etc).
* `cpu` [CPUUsage](cpu-usage.md) - CPU usage of the process.
* `creationTime` Number - Creation time for this process. Since the `pid` can be reused after a process dies,
it is useful to use both the `pid` and the `creationTime` to uniquely identify a process.
* `sandboxed` Boolean (optional) _macOS_ _Windows_ - Whether the process is sandboxed on OS level.
* `integrityLevel` String (optional) _Windows_ - One of the following values:
* `untrusted`
* `low`
* `medium`
* `high`
* `unknown`
2 changes: 2 additions & 0 deletions filenames.gni
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ filenames = {
"atom/browser/api/gpu_info_enumerator.h",
"atom/browser/api/gpuinfo_manager.cc",
"atom/browser/api/gpuinfo_manager.h",
"atom/browser/api/process_metric.cc",
"atom/browser/api/process_metric.h",
"atom/browser/api/save_page_handler.cc",
"atom/browser/api/save_page_handler.h",
"atom/browser/auto_updater.cc",
Expand Down
21 changes: 15 additions & 6 deletions spec-main/api-app-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -931,13 +931,22 @@ describe('app module', () => {
expect(appMetrics).to.be.an('array').and.have.lengthOf.at.least(1, 'App memory info object is not > 0')

const types = []
for (const { pid, type, cpu } of appMetrics) {
expect(pid).to.be.above(0, 'pid is not > 0')
expect(type).to.be.a('string').that.does.not.equal('')
for (const entry of appMetrics) {
expect(entry.pid).to.be.above(0, 'pid is not > 0')
expect(entry.type).to.be.a('string').that.does.not.equal('')
expect(entry.creationTime).to.be.a('number').that.is.greaterThan(0)

types.push(type)
expect(cpu).to.have.ownProperty('percentCPUUsage').that.is.a('number')
expect(cpu).to.have.ownProperty('idleWakeupsPerSecond').that.is.a('number')
types.push(entry.type)
expect(entry.cpu).to.have.ownProperty('percentCPUUsage').that.is.a('number')
expect(entry.cpu).to.have.ownProperty('idleWakeupsPerSecond').that.is.a('number')

if (process.platform !== 'linux') {
expect(entry.sandboxed).to.be.a('boolean')
}

if (process.platform === 'win32') {
expect(entry.integrityLevel).to.be.a('string')
}
}

if (process.platform === 'darwin') {
Expand Down

0 comments on commit a23362e

Please sign in to comment.