feat: support app#login event for utility process net requests (#43317)
* feat: support app#login event for utility process net requests
* feat: support app#login event for utility process net requests
* chore: address review feedback
* GlobalRequestID: Avoid unwanted inlining and narrowing int conversions
Refs 5702737
* chore: fix lint
This commit is contained in:
parent
7123b313cf
commit
8c1be5ade2
17 changed files with 536 additions and 37 deletions
|
@ -64,7 +64,8 @@ UtilityProcessWrapper::UtilityProcessWrapper(
|
|||
std::map<IOHandle, IOType> stdio,
|
||||
base::EnvironmentMap env_map,
|
||||
base::FilePath current_working_directory,
|
||||
bool use_plugin_helper) {
|
||||
bool use_plugin_helper,
|
||||
bool create_network_observer) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
base::win::ScopedHandle stdout_write(nullptr);
|
||||
base::win::ScopedHandle stderr_write(nullptr);
|
||||
|
@ -204,6 +205,11 @@ UtilityProcessWrapper::UtilityProcessWrapper(
|
|||
loader_params->process_id = pid_;
|
||||
loader_params->is_orb_enabled = false;
|
||||
loader_params->is_trusted = true;
|
||||
if (create_network_observer) {
|
||||
url_loader_network_observer_.emplace();
|
||||
loader_params->url_loader_network_observer =
|
||||
url_loader_network_observer_->Bind();
|
||||
}
|
||||
network::mojom::NetworkContext* network_context =
|
||||
g_browser_process->system_network_context_manager()->GetContext();
|
||||
network_context->CreateURLLoaderFactory(
|
||||
|
@ -214,6 +220,8 @@ UtilityProcessWrapper::UtilityProcessWrapper(
|
|||
network_context->CreateHostResolver(
|
||||
{}, host_resolver.InitWithNewPipeAndPassReceiver());
|
||||
params->host_resolver = std::move(host_resolver);
|
||||
params->use_network_observer_from_url_loader_factory =
|
||||
create_network_observer;
|
||||
|
||||
node_service_remote_->Initialize(std::move(params));
|
||||
}
|
||||
|
@ -231,6 +239,9 @@ void UtilityProcessWrapper::OnServiceProcessLaunch(
|
|||
EmitWithoutEvent("stdout", stdout_read_fd_);
|
||||
if (stderr_read_fd_ != -1)
|
||||
EmitWithoutEvent("stderr", stderr_read_fd_);
|
||||
if (url_loader_network_observer_.has_value()) {
|
||||
url_loader_network_observer_->set_process_id(pid_);
|
||||
}
|
||||
EmitWithoutEvent("spawn");
|
||||
}
|
||||
|
||||
|
@ -379,6 +390,7 @@ gin::Handle<UtilityProcessWrapper> UtilityProcessWrapper::Create(
|
|||
|
||||
std::u16string display_name;
|
||||
bool use_plugin_helper = false;
|
||||
bool create_network_observer = false;
|
||||
std::map<IOHandle, IOType> stdio;
|
||||
base::FilePath current_working_directory;
|
||||
base::EnvironmentMap env_map;
|
||||
|
@ -404,6 +416,7 @@ gin::Handle<UtilityProcessWrapper> UtilityProcessWrapper::Create(
|
|||
|
||||
opts.Get("serviceName", &display_name);
|
||||
opts.Get("cwd", ¤t_working_directory);
|
||||
opts.Get("respondToAuthRequestsFromMainProcess", &create_network_observer);
|
||||
|
||||
std::vector<std::string> stdio_arr{"ignore", "inherit", "inherit"};
|
||||
opts.Get("stdio", &stdio_arr);
|
||||
|
@ -424,10 +437,10 @@ gin::Handle<UtilityProcessWrapper> UtilityProcessWrapper::Create(
|
|||
#endif
|
||||
}
|
||||
auto handle = gin::CreateHandle(
|
||||
args->isolate(),
|
||||
new UtilityProcessWrapper(std::move(params), display_name,
|
||||
std::move(stdio), env_map,
|
||||
current_working_directory, use_plugin_helper));
|
||||
args->isolate(), new UtilityProcessWrapper(
|
||||
std::move(params), display_name, std::move(stdio),
|
||||
env_map, current_working_directory,
|
||||
use_plugin_helper, create_network_observer));
|
||||
handle->Pin(args->isolate());
|
||||
return handle;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "mojo/public/cpp/bindings/receiver.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "shell/browser/event_emitter_mixin.h"
|
||||
#include "shell/browser/net/url_loader_network_observer.h"
|
||||
#include "shell/common/gin_helper/pinnable.h"
|
||||
#include "shell/services/node/public/mojom/node_service.mojom.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
@ -64,7 +65,8 @@ class UtilityProcessWrapper
|
|||
std::map<IOHandle, IOType> stdio,
|
||||
base::EnvironmentMap env_map,
|
||||
base::FilePath current_working_directory,
|
||||
bool use_plugin_helper);
|
||||
bool use_plugin_helper,
|
||||
bool create_network_observer);
|
||||
void OnServiceProcessLaunch(const base::Process& process);
|
||||
void CloseConnectorPort();
|
||||
|
||||
|
@ -99,6 +101,8 @@ class UtilityProcessWrapper
|
|||
std::unique_ptr<mojo::Connector> connector_;
|
||||
blink::MessagePortDescriptor host_port_;
|
||||
mojo::Remote<node::mojom::NodeService> node_service_remote_;
|
||||
std::optional<electron::URLLoaderNetworkObserver>
|
||||
url_loader_network_observer_;
|
||||
base::WeakPtrFactory<UtilityProcessWrapper> weak_factory_{this};
|
||||
};
|
||||
|
||||
|
|
|
@ -1635,8 +1635,8 @@ ElectronBrowserClient::CreateLoginDelegate(
|
|||
bool first_auth_attempt,
|
||||
LoginAuthRequiredCallback auth_required_callback) {
|
||||
return std::make_unique<LoginHandler>(
|
||||
auth_info, web_contents, is_main_frame, url, response_headers,
|
||||
first_auth_attempt, std::move(auth_required_callback));
|
||||
auth_info, web_contents, is_main_frame, base::kNullProcessId, url,
|
||||
response_headers, first_auth_attempt, std::move(auth_required_callback));
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "base/task/sequenced_task_runner.h"
|
||||
#include "gin/arguments.h"
|
||||
#include "gin/dictionary.h"
|
||||
#include "shell/browser/api/electron_api_app.h"
|
||||
#include "shell/browser/api/electron_api_web_contents.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/common/gin_converters/callback_converter.h"
|
||||
|
@ -25,39 +26,44 @@ LoginHandler::LoginHandler(
|
|||
const net::AuthChallengeInfo& auth_info,
|
||||
content::WebContents* web_contents,
|
||||
bool is_main_frame,
|
||||
base::ProcessId process_id,
|
||||
const GURL& url,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers,
|
||||
bool first_auth_attempt,
|
||||
LoginAuthRequiredCallback auth_required_callback)
|
||||
|
||||
: WebContentsObserver(web_contents),
|
||||
auth_required_callback_(std::move(auth_required_callback)) {
|
||||
: auth_required_callback_(std::move(auth_required_callback)) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&LoginHandler::EmitEvent, weak_factory_.GetWeakPtr(),
|
||||
auth_info, is_main_frame, url, response_headers,
|
||||
first_auth_attempt));
|
||||
auth_info, web_contents, is_main_frame, process_id, url,
|
||||
response_headers, first_auth_attempt));
|
||||
}
|
||||
|
||||
void LoginHandler::EmitEvent(
|
||||
net::AuthChallengeInfo auth_info,
|
||||
content::WebContents* web_contents,
|
||||
bool is_main_frame,
|
||||
base::ProcessId process_id,
|
||||
const GURL& url,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers,
|
||||
bool first_auth_attempt) {
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
|
||||
api::WebContents* api_web_contents = api::WebContents::From(web_contents());
|
||||
if (!api_web_contents) {
|
||||
std::move(auth_required_callback_).Run(std::nullopt);
|
||||
return;
|
||||
raw_ptr<api::WebContents> api_web_contents = nullptr;
|
||||
if (web_contents) {
|
||||
api_web_contents = api::WebContents::From(web_contents);
|
||||
if (!api_web_contents) {
|
||||
std::move(auth_required_callback_).Run(std::nullopt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto details = gin::Dictionary::CreateEmpty(isolate);
|
||||
details.Set("url", url);
|
||||
details.Set("pid", process_id);
|
||||
|
||||
// These parameters aren't documented, and I'm not sure that they're useful,
|
||||
// but we might as well stick 'em on the details object. If it turns out they
|
||||
|
@ -67,10 +73,18 @@ void LoginHandler::EmitEvent(
|
|||
details.Set("responseHeaders", response_headers.get());
|
||||
|
||||
auto weak_this = weak_factory_.GetWeakPtr();
|
||||
bool default_prevented =
|
||||
api_web_contents->Emit("login", std::move(details), auth_info,
|
||||
base::BindOnce(&LoginHandler::CallbackFromJS,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
bool default_prevented = false;
|
||||
if (api_web_contents) {
|
||||
default_prevented =
|
||||
api_web_contents->Emit("login", std::move(details), auth_info,
|
||||
base::BindOnce(&LoginHandler::CallbackFromJS,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
} else {
|
||||
default_prevented =
|
||||
api::App::Get()->Emit("login", nullptr, std::move(details), auth_info,
|
||||
base::BindOnce(&LoginHandler::CallbackFromJS,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
// ⚠️ NB, if CallbackFromJS is called during Emit(), |this| will have been
|
||||
// deleted. Check the weak ptr before accessing any member variables to
|
||||
// prevent UAF.
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
#ifndef ELECTRON_SHELL_BROWSER_LOGIN_HANDLER_H_
|
||||
#define ELECTRON_SHELL_BROWSER_LOGIN_HANDLER_H_
|
||||
|
||||
#include "base/process/process_handle.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/browser/content_browser_client.h"
|
||||
#include "content/public/browser/login_delegate.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
|
||||
namespace content {
|
||||
class WebContents;
|
||||
|
@ -21,12 +21,12 @@ class Arguments;
|
|||
namespace electron {
|
||||
|
||||
// Handles HTTP basic auth.
|
||||
class LoginHandler : public content::LoginDelegate,
|
||||
private content::WebContentsObserver {
|
||||
class LoginHandler : public content::LoginDelegate {
|
||||
public:
|
||||
LoginHandler(const net::AuthChallengeInfo& auth_info,
|
||||
content::WebContents* web_contents,
|
||||
bool is_main_frame,
|
||||
base::ProcessId process_id,
|
||||
const GURL& url,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers,
|
||||
bool first_auth_attempt,
|
||||
|
@ -39,7 +39,9 @@ class LoginHandler : public content::LoginDelegate,
|
|||
|
||||
private:
|
||||
void EmitEvent(net::AuthChallengeInfo auth_info,
|
||||
content::WebContents* web_contents,
|
||||
bool is_main_frame,
|
||||
base::ProcessId process_id,
|
||||
const GURL& url,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers,
|
||||
bool first_auth_attempt);
|
||||
|
|
120
shell/browser/net/url_loader_network_observer.cc
Normal file
120
shell/browser/net/url_loader_network_observer.cc
Normal file
|
@ -0,0 +1,120 @@
|
|||
// Copyright (c) 2024 Microsoft, GmbH
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/net/url_loader_network_observer.h"
|
||||
|
||||
#include "base/functional/bind.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "shell/browser/login_handler.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
class LoginHandlerDelegate {
|
||||
public:
|
||||
LoginHandlerDelegate(
|
||||
mojo::PendingRemote<network::mojom::AuthChallengeResponder>
|
||||
auth_challenge_responder,
|
||||
const net::AuthChallengeInfo& auth_info,
|
||||
const GURL& url,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers,
|
||||
base::ProcessId process_id,
|
||||
bool first_auth_attempt)
|
||||
: auth_challenge_responder_(std::move(auth_challenge_responder)) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
auth_challenge_responder_.set_disconnect_handler(base::BindOnce(
|
||||
&LoginHandlerDelegate::OnRequestCancelled, base::Unretained(this)));
|
||||
|
||||
login_handler_ = std::make_unique<LoginHandler>(
|
||||
auth_info, nullptr, false, process_id, url, response_headers,
|
||||
first_auth_attempt,
|
||||
base::BindOnce(&LoginHandlerDelegate::OnAuthCredentials,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
private:
|
||||
void OnRequestCancelled() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
delete this;
|
||||
}
|
||||
|
||||
void OnAuthCredentials(
|
||||
const std::optional<net::AuthCredentials>& auth_credentials) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
auth_challenge_responder_->OnAuthCredentials(auth_credentials);
|
||||
delete this;
|
||||
}
|
||||
|
||||
mojo::Remote<network::mojom::AuthChallengeResponder>
|
||||
auth_challenge_responder_;
|
||||
std::unique_ptr<LoginHandler> login_handler_;
|
||||
base::WeakPtrFactory<LoginHandlerDelegate> weak_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
URLLoaderNetworkObserver::URLLoaderNetworkObserver() = default;
|
||||
URLLoaderNetworkObserver::~URLLoaderNetworkObserver() = default;
|
||||
|
||||
mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
|
||||
URLLoaderNetworkObserver::Bind() {
|
||||
mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
|
||||
pending_remote;
|
||||
receivers_.Add(this, pending_remote.InitWithNewPipeAndPassReceiver());
|
||||
return pending_remote;
|
||||
}
|
||||
|
||||
void URLLoaderNetworkObserver::OnAuthRequired(
|
||||
const std::optional<base::UnguessableToken>& window_id,
|
||||
int32_t request_id,
|
||||
const GURL& url,
|
||||
bool first_auth_attempt,
|
||||
const net::AuthChallengeInfo& auth_info,
|
||||
const scoped_refptr<net::HttpResponseHeaders>& head_headers,
|
||||
mojo::PendingRemote<network::mojom::AuthChallengeResponder>
|
||||
auth_challenge_responder) {
|
||||
new LoginHandlerDelegate(std::move(auth_challenge_responder), auth_info, url,
|
||||
head_headers, process_id_, first_auth_attempt);
|
||||
}
|
||||
|
||||
void URLLoaderNetworkObserver::OnSSLCertificateError(
|
||||
const GURL& url,
|
||||
int net_error,
|
||||
const net::SSLInfo& ssl_info,
|
||||
bool fatal,
|
||||
OnSSLCertificateErrorCallback response) {
|
||||
std::move(response).Run(net_error);
|
||||
}
|
||||
|
||||
void URLLoaderNetworkObserver::OnClearSiteData(
|
||||
const GURL& url,
|
||||
const std::string& header_value,
|
||||
int32_t load_flags,
|
||||
const std::optional<net::CookiePartitionKey>& cookie_partition_key,
|
||||
bool partitioned_state_allowed_only,
|
||||
OnClearSiteDataCallback callback) {
|
||||
std::move(callback).Run();
|
||||
}
|
||||
|
||||
void URLLoaderNetworkObserver::OnLoadingStateUpdate(
|
||||
network::mojom::LoadInfoPtr info,
|
||||
OnLoadingStateUpdateCallback callback) {
|
||||
std::move(callback).Run();
|
||||
}
|
||||
|
||||
void URLLoaderNetworkObserver::OnSharedStorageHeaderReceived(
|
||||
const url::Origin& request_origin,
|
||||
std::vector<network::mojom::SharedStorageOperationPtr> operations,
|
||||
OnSharedStorageHeaderReceivedCallback callback) {
|
||||
std::move(callback).Run();
|
||||
}
|
||||
|
||||
void URLLoaderNetworkObserver::Clone(
|
||||
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
||||
observer) {
|
||||
receivers_.Add(this, std::move(observer));
|
||||
}
|
||||
|
||||
} // namespace electron
|
82
shell/browser/net/url_loader_network_observer.h
Normal file
82
shell/browser/net/url_loader_network_observer.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) 2024 Microsoft, GmbH
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_BROWSER_NET_URL_LOADER_NETWORK_OBSERVER_H_
|
||||
#define ELECTRON_SHELL_BROWSER_NET_URL_LOADER_NETWORK_OBSERVER_H_
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/process/process_handle.h"
|
||||
#include "mojo/public/cpp/bindings/receiver_set.h"
|
||||
#include "services/network/public/mojom/url_loader_network_service_observer.mojom.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
class URLLoaderNetworkObserver
|
||||
: public network::mojom::URLLoaderNetworkServiceObserver {
|
||||
public:
|
||||
URLLoaderNetworkObserver();
|
||||
~URLLoaderNetworkObserver() override;
|
||||
|
||||
URLLoaderNetworkObserver(const URLLoaderNetworkObserver&) = delete;
|
||||
URLLoaderNetworkObserver& operator=(const URLLoaderNetworkObserver&) = delete;
|
||||
|
||||
mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> Bind();
|
||||
void set_process_id(base::ProcessId pid) { process_id_ = pid; }
|
||||
|
||||
private:
|
||||
void OnAuthRequired(
|
||||
const std::optional<base::UnguessableToken>& window_id,
|
||||
int32_t request_id,
|
||||
const GURL& url,
|
||||
bool first_auth_attempt,
|
||||
const net::AuthChallengeInfo& auth_info,
|
||||
const scoped_refptr<net::HttpResponseHeaders>& head_headers,
|
||||
mojo::PendingRemote<network::mojom::AuthChallengeResponder>
|
||||
auth_challenge_responder) override;
|
||||
void OnSSLCertificateError(const GURL& url,
|
||||
int net_error,
|
||||
const net::SSLInfo& ssl_info,
|
||||
bool fatal,
|
||||
OnSSLCertificateErrorCallback response) override;
|
||||
void OnClearSiteData(
|
||||
const GURL& url,
|
||||
const std::string& header_value,
|
||||
int32_t load_flags,
|
||||
const std::optional<net::CookiePartitionKey>& cookie_partition_key,
|
||||
bool partitioned_state_allowed_only,
|
||||
OnClearSiteDataCallback callback) override;
|
||||
void OnLoadingStateUpdate(network::mojom::LoadInfoPtr info,
|
||||
OnLoadingStateUpdateCallback callback) override;
|
||||
void OnSharedStorageHeaderReceived(
|
||||
const url::Origin& request_origin,
|
||||
std::vector<network::mojom::SharedStorageOperationPtr> operations,
|
||||
OnSharedStorageHeaderReceivedCallback callback) override;
|
||||
void OnDataUseUpdate(int32_t network_traffic_annotation_id_hash,
|
||||
int64_t recv_bytes,
|
||||
int64_t sent_bytes) override {}
|
||||
void OnWebSocketConnectedToPrivateNetwork(
|
||||
network::mojom::IPAddressSpace ip_address_space) override {}
|
||||
void OnCertificateRequested(
|
||||
const std::optional<base::UnguessableToken>& window_id,
|
||||
const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
|
||||
mojo::PendingRemote<network::mojom::ClientCertificateResponder>
|
||||
client_cert_responder) override {}
|
||||
void OnPrivateNetworkAccessPermissionRequired(
|
||||
const GURL& url,
|
||||
const net::IPAddress& ip_address,
|
||||
const std::optional<std::string>& private_network_device_id,
|
||||
const std::optional<std::string>& private_network_device_name,
|
||||
OnPrivateNetworkAccessPermissionRequiredCallback callback) override {}
|
||||
void Clone(
|
||||
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
|
||||
observer) override;
|
||||
|
||||
mojo::ReceiverSet<network::mojom::URLLoaderNetworkServiceObserver> receivers_;
|
||||
base::ProcessId process_id_ = base::kNullProcessId;
|
||||
base::WeakPtrFactory<URLLoaderNetworkObserver> weak_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_NET_URL_LOADER_NETWORK_OBSERVER_H_
|
Loading…
Add table
Add a link
Reference in a new issue