Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add creationTime / sandboxed / integrityLevel to app.getAppMetrics() #18718

Merged
merged 2 commits into from
Jun 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 {
miniak marked this conversation as resolved.
Show resolved Hide resolved
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_
11 changes: 11 additions & 0 deletions docs/api/structures/process-metric.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,14 @@
* `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.
The time is represented as number of milliseconds since epoch.
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