Commit ef7f9cc7 authored by Mahmoud Gawad's avatar Mahmoud Gawad Committed by Commit Bot

[Telemetry SWX] add Telemetry URLDataSource

Add a new content::URLDataSource (TelemetryExtensionUntrustedSource) to
load the TelemetryExtension's untrusted resources from disk.

Bug: b:156583406, 1085330
Test: added browsertests

Change-Id: I7453c4740ee1a7c9c9240f106461735f018bfc32
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2277735Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarGiovanni Ortuño Urquidi <ortuno@chromium.org>
Reviewed-by: default avatarOleh Lamzin <lamzin@google.com>
Commit-Queue: Mahmoud Gawad <mgawad@google.com>
Cr-Commit-Position: refs/heads/master@{#792582}
parent 72b86c2f
...@@ -2,14 +2,30 @@ ...@@ -2,14 +2,30 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <string>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "chrome/browser/chromeos/web_applications/system_web_app_integration_test.h" #include "chrome/browser/chromeos/web_applications/system_web_app_integration_test.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/web_applications/system_web_app_manager.h" #include "chrome/browser/web_applications/system_web_app_manager.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/components/telemetry_extension_ui/url_constants.h" #include "chromeos/components/telemetry_extension_ui/url_constants.h"
#include "chromeos/constants/chromeos_features.h" #include "chromeos/constants/chromeos_features.h"
#include "chromeos/constants/chromeos_switches.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h" #include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr char kNonExistentUrlPath[] = "non-existent-url.html";
constexpr char kLoadFromDiskUrlPath[] = "telemetry_extension_test.html";
constexpr char kRegisteredUrlPath[] = "untrusted.html";
} // namespace
class TelemetryExtensionIntegrationTest : public SystemWebAppIntegrationTest { class TelemetryExtensionIntegrationTest : public SystemWebAppIntegrationTest {
public: public:
TelemetryExtensionIntegrationTest() { TelemetryExtensionIntegrationTest() {
...@@ -17,16 +33,50 @@ class TelemetryExtensionIntegrationTest : public SystemWebAppIntegrationTest { ...@@ -17,16 +33,50 @@ class TelemetryExtensionIntegrationTest : public SystemWebAppIntegrationTest {
{chromeos::features::kTelemetryExtension}, {}); {chromeos::features::kTelemetryExtension}, {});
} }
web_app::SystemAppType GetTelemetryAppType() const {
return web_app::SystemAppType::TELEMETRY;
}
content::WebContents* LaunchTelemetryApp() {
// Important: wait until the telemetry app type be installed.
WaitForTestSystemAppInstall();
// Launch the Telemetry app.
Browser* app_browser;
return LaunchApp(GetTelemetryAppType(), &app_browser);
}
private: private:
base::test::ScopedFeatureList scoped_feature_list_; base::test::ScopedFeatureList scoped_feature_list_;
}; };
// Test that the Telemetry Extension installs and launches correctly. Runs // Tests that the TelemetryExtension app is successfully installed.
// some spot checks on the manifest. IN_PROC_BROWSER_TEST_P(TelemetryExtensionIntegrationTest,
IN_PROC_BROWSER_TEST_P(TelemetryExtensionIntegrationTest, TelemetryExtension) { TelemetryExtensionInstalled) {
const GURL url(chromeos::kChromeUITelemetryExtensionURL); const GURL url(chromeos::kChromeUITelemetryExtensionURL);
EXPECT_NO_FATAL_FAILURE(ExpectSystemWebAppValid( EXPECT_NO_FATAL_FAILURE(ExpectSystemWebAppValid(GetTelemetryAppType(), url,
web_app::SystemAppType::TELEMETRY, url, "Telemetry Extension")); "Telemetry Extension"));
}
// Tests that TelemetryExtensionUntrustedSource successfully loads the
// registered resource.
IN_PROC_BROWSER_TEST_P(
TelemetryExtensionIntegrationTest,
TelemetryExtensionUntrustedSourceCanLoadRegisteredResource) {
content::WebContents* web_contents = LaunchTelemetryApp();
const GURL registered_resource_gurl =
GURL(std::string(chromeos::kChromeUIUntrustedTelemetryExtensionURL) +
kRegisteredUrlPath);
// Load the |registered_resource_gurl| into the same tab.
// The |registered_resource_gurl| is a file that is included in the
// TelemteryExtensionUntrustedSource's list of registered resources.
EXPECT_TRUE(content::NavigateToURL(web_contents, registered_resource_gurl));
// Verify that the file loaded from disk has the expected title.
EXPECT_EQ(base::UTF8ToUTF16("Untrusted Telemetry Extension"),
web_contents->GetTitle());
} }
INSTANTIATE_TEST_SUITE_P(All, INSTANTIATE_TEST_SUITE_P(All,
...@@ -34,3 +84,61 @@ INSTANTIATE_TEST_SUITE_P(All, ...@@ -34,3 +84,61 @@ INSTANTIATE_TEST_SUITE_P(All,
::testing::Values(web_app::ProviderType::kBookmarkApps, ::testing::Values(web_app::ProviderType::kBookmarkApps,
web_app::ProviderType::kWebApps), web_app::ProviderType::kWebApps),
web_app::ProviderTypeParamToString); web_app::ProviderTypeParamToString);
// A Test suite that use the switch "--telemetry-extension-dir".
class TelemetryExtensionWithDirIntegrationTest
: public TelemetryExtensionIntegrationTest {
public:
TelemetryExtensionWithDirIntegrationTest() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
base::FilePath src_dir;
base::PathService::Get(chrome::DIR_TEST_DATA, &src_dir);
SystemWebAppManagerBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(
chromeos::switches::kTelemetryExtensionDirectory, src_dir.value());
}
};
// Tests that TelemetryExtensionUntrustedSource can successfully load a resource
// from disk.
IN_PROC_BROWSER_TEST_P(TelemetryExtensionWithDirIntegrationTest,
TelemetryExtensionUntrustedSourceCanLoadFileFromDisk) {
content::WebContents* web_contents = LaunchTelemetryApp();
const GURL load_from_disk_resource_gurl =
GURL(std::string(chromeos::kChromeUIUntrustedTelemetryExtensionURL) +
kLoadFromDiskUrlPath);
// Load the |load_from_disk_resource_gurl| into the same tab.
// The |load_from_disk_resource_gurl| is a file that is not included in the
// TelemteryExtensionUntrustedSource's list of registered resources.
EXPECT_TRUE(
content::NavigateToURL(web_contents, load_from_disk_resource_gurl));
// Verify that the file loaded from disk has the expected title.
EXPECT_EQ(base::UTF8ToUTF16("TelemetryExtension - Loaded From Disk"),
web_contents->GetTitle());
}
// Tests that TelemetryExtensionUntrustedSource fails to load non-existing
// chrome-untrusted://telemetry-extension/ resource from disk.
IN_PROC_BROWSER_TEST_P(
TelemetryExtensionWithDirIntegrationTest,
TelemetryExtensionUntrustedSourceFailToLoadNonExistentURL) {
content::WebContents* web_contents = LaunchTelemetryApp();
const GURL non_existent_resource_gurl =
GURL(std::string(chromeos::kChromeUIUntrustedTelemetryExtensionURL) +
kNonExistentUrlPath);
EXPECT_FALSE(
content::NavigateToURL(web_contents, non_existent_resource_gurl));
}
INSTANTIATE_TEST_SUITE_P(All,
TelemetryExtensionWithDirIntegrationTest,
::testing::Values(web_app::ProviderType::kBookmarkApps,
web_app::ProviderType::kWebApps),
web_app::ProviderTypeParamToString);
<!-- Copyright 2020 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<html>
<meta charset="utf-8">
<title>TelemetryExtension - Loaded From Disk</title>
</html>
...@@ -14,6 +14,8 @@ source_set("telemetry_extension_ui") { ...@@ -14,6 +14,8 @@ source_set("telemetry_extension_ui") {
"probe_service_converters.h", "probe_service_converters.h",
"telemetry_extension_ui.cc", "telemetry_extension_ui.cc",
"telemetry_extension_ui.h", "telemetry_extension_ui.h",
"telemetry_extension_untrusted_source.cc",
"telemetry_extension_untrusted_source.h",
"url_constants.cc", "url_constants.cc",
"url_constants.h", "url_constants.h",
] ]
......
...@@ -3,5 +3,6 @@ include_rules = [ ...@@ -3,5 +3,6 @@ include_rules = [
"+chromeos/grit/chromeos_telemetry_extension_resources.h", "+chromeos/grit/chromeos_telemetry_extension_resources.h",
"+content/public/browser", "+content/public/browser",
"+content/public/common", "+content/public/common",
"+ui/base/resource/resource_bundle.h",
"+ui/webui", "+ui/webui",
] ]
...@@ -9,41 +9,20 @@ ...@@ -9,41 +9,20 @@
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "chromeos/components/telemetry_extension_ui/mojom/probe_service.mojom.h" #include "chromeos/components/telemetry_extension_ui/mojom/probe_service.mojom.h"
#include "chromeos/components/telemetry_extension_ui/probe_service.h" #include "chromeos/components/telemetry_extension_ui/probe_service.h"
#include "chromeos/components/telemetry_extension_ui/telemetry_extension_untrusted_source.h"
#include "chromeos/components/telemetry_extension_ui/url_constants.h" #include "chromeos/components/telemetry_extension_ui/url_constants.h"
#include "chromeos/grit/chromeos_telemetry_extension_resources.h" #include "chromeos/grit/chromeos_telemetry_extension_resources.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h" #include "content/public/browser/web_ui_data_source.h"
#include "content/public/common/url_constants.h" #include "content/public/common/url_constants.h"
#include "services/network/public/mojom/content_security_policy.mojom.h" #include "services/network/public/mojom/content_security_policy.mojom.h"
namespace chromeos { namespace chromeos {
namespace {
content::WebUIDataSource* CreateUntrustedTelemetryExtensionDataSource() {
content::WebUIDataSource* untrusted_source =
content::WebUIDataSource::Create(kChromeUIUntrustedTelemetryExtensionURL);
untrusted_source->AddResourcePath("untrusted.html",
IDR_TELEMETRY_EXTENSION_UNTRUSTED_HTML);
untrusted_source->AddResourcePath(
"untrusted_scripts.js", IDR_TELEMETRY_EXTENSION_UNTRUSTED_SCRIPTS_JS);
untrusted_source->AddResourcePath(
"untrusted_worker.js", IDR_TELEMETRY_EXTENSION_UNTRUSTED_WORKER_JS);
untrusted_source->AddFrameAncestor(GURL(kChromeUITelemetryExtensionURL));
// TODO(https://crbug.com/1085330): tighten CSP.
untrusted_source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::DefaultSrc, std::string());
// Allow chrome-untrusted:// to load Web Worker scripts. namespace {
untrusted_source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::WorkerSrc, "worker-src 'self';");
return untrusted_source;
}
} // namespace
TelemetryExtensionUI::TelemetryExtensionUI(content::WebUI* web_ui) std::unique_ptr<content::WebUIDataSource>
: ui::MojoWebUIController(web_ui) { CreateTrustedTelemetryExtensionDataSource() {
auto trusted_source = base::WrapUnique( auto trusted_source = base::WrapUnique(
content::WebUIDataSource::Create(kChromeUITelemetryExtensionHost)); content::WebUIDataSource::Create(kChromeUITelemetryExtensionHost));
...@@ -72,10 +51,42 @@ TelemetryExtensionUI::TelemetryExtensionUI(content::WebUI* web_ui) ...@@ -72,10 +51,42 @@ TelemetryExtensionUI::TelemetryExtensionUI(content::WebUI* web_ui)
std::string("frame-src ") + kChromeUIUntrustedTelemetryExtensionURL + ";"; std::string("frame-src ") + kChromeUIUntrustedTelemetryExtensionURL + ";";
trusted_source->OverrideContentSecurityPolicy( trusted_source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::FrameSrc, csp); network::mojom::CSPDirectiveName::FrameSrc, csp);
return trusted_source;
}
std::unique_ptr<TelemetryExtensionUntrustedSource>
CreateUntrustedTelemetryExtensionDataSource() {
auto untrusted_source = TelemetryExtensionUntrustedSource::Create(
chromeos::kChromeUIUntrustedTelemetryExtensionURL);
untrusted_source->AddResourcePath("untrusted.html",
IDR_TELEMETRY_EXTENSION_UNTRUSTED_HTML);
untrusted_source->AddResourcePath(
"untrusted_scripts.js", IDR_TELEMETRY_EXTENSION_UNTRUSTED_SCRIPTS_JS);
untrusted_source->AddResourcePath(
"untrusted_worker.js", IDR_TELEMETRY_EXTENSION_UNTRUSTED_WORKER_JS);
untrusted_source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::FrameAncestors,
std::string("frame-ancestors ") +
chromeos::kChromeUITelemetryExtensionURL + ";");
untrusted_source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::WorkerSrc, "worker-src 'self';");
return untrusted_source;
}
} // namespace
TelemetryExtensionUI::TelemetryExtensionUI(content::WebUI* web_ui)
: ui::MojoWebUIController(web_ui) {
auto* browser_context = web_ui->GetWebContents()->GetBrowserContext(); auto* browser_context = web_ui->GetWebContents()->GetBrowserContext();
content::WebUIDataSource::Add(browser_context, trusted_source.release());
content::WebUIDataSource::Add(browser_context, content::WebUIDataSource::Add(
CreateUntrustedTelemetryExtensionDataSource()); browser_context, CreateTrustedTelemetryExtensionDataSource().release());
content::URLDataSource::Add(browser_context,
CreateUntrustedTelemetryExtensionDataSource());
// Add ability to request chrome-untrusted: URLs // Add ability to request chrome-untrusted: URLs
web_ui->AddRequestableScheme(content::kChromeUIUntrustedScheme); web_ui->AddRequestableScheme(content::kChromeUIUntrustedScheme);
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/components/telemetry_extension_ui/telemetry_extension_untrusted_source.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chromeos/components/telemetry_extension_ui/url_constants.h"
#include "chromeos/constants/chromeos_switches.h"
#include "net/base/mime_util.h"
#include "ui/base/resource/resource_bundle.h"
namespace chromeos {
namespace {
constexpr char kDefaultMime[] = "text/html";
base::FilePath GetTelemetryDirectory() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
bool has_switch =
command_line->HasSwitch(chromeos::switches::kTelemetryExtensionDirectory);
if (!has_switch) {
LOG(WARNING) << "Switch value '--telemetry-extension-dir' is not "
"specified, some resources might not be loaded";
return base::FilePath();
}
return base::FilePath(command_line->GetSwitchValueASCII(
chromeos::switches::kTelemetryExtensionDirectory));
}
void ReadFile(const base::FilePath& path,
content::URLDataSource::GotDataCallback callback) {
std::string content;
if (!base::ReadFileToString(path, &content)) {
PLOG(ERROR) << "Failed to read content from file: " << path;
std::move(callback).Run(nullptr);
return;
}
scoped_refptr<base::RefCountedString> response =
base::RefCountedString::TakeString(&content);
std::move(callback).Run(response.get());
}
} // namespace
// static
std::unique_ptr<TelemetryExtensionUntrustedSource>
TelemetryExtensionUntrustedSource::Create(std::string source) {
return base::WrapUnique(new TelemetryExtensionUntrustedSource(source));
}
TelemetryExtensionUntrustedSource::TelemetryExtensionUntrustedSource(
const std::string& source)
: root_directory_(GetTelemetryDirectory()), source_(source) {}
TelemetryExtensionUntrustedSource::~TelemetryExtensionUntrustedSource() =
default;
void TelemetryExtensionUntrustedSource::AddResourcePath(base::StringPiece path,
int resource_id) {
path_to_idr_map_[path.as_string()] = resource_id;
}
void TelemetryExtensionUntrustedSource::OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName directive,
const std::string& value) {
csp_overrides_map_.insert_or_assign(directive, value);
}
std::string TelemetryExtensionUntrustedSource::GetSource() {
return source_;
}
void TelemetryExtensionUntrustedSource::StartDataRequest(
const GURL& url,
const content::WebContents::Getter& wc_getter,
content::URLDataSource::GotDataCallback callback) {
std::string path = content::URLDataSource::URLToRequestPath(url);
// Remove the query string for named resource lookups.
path = path.substr(0, path.find_first_of('?'));
base::Optional<int> resource_id = PathToIdr(path);
if (resource_id.has_value()) {
scoped_refptr<base::RefCountedMemory> response(
ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
resource_id.value()));
std::move(callback).Run(response.get());
return;
}
if (root_directory_.empty()) {
LOG(WARNING) << "Cannot load resources from disk, switch value "
"'--telemetry-extension-dir' is not specified";
std::move(callback).Run(nullptr);
return;
}
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
base::BindOnce(&ReadFile, root_directory_.Append(path),
std::move(callback)));
}
std::string TelemetryExtensionUntrustedSource::GetMimeType(
const std::string& path) {
const std::string ext = base::FilePath(path).Extension();
if (ext.empty()) {
return kDefaultMime;
}
std::string mime_type;
net::GetWellKnownMimeTypeFromExtension(ext.substr(1), &mime_type);
return mime_type;
}
std::string TelemetryExtensionUntrustedSource::GetContentSecurityPolicy(
network::mojom::CSPDirectiveName directive) {
const auto& it = csp_overrides_map_.find(directive);
if (it == csp_overrides_map_.end()) {
return URLDataSource::GetContentSecurityPolicy(directive);
}
return it->second;
}
base::Optional<int> TelemetryExtensionUntrustedSource::PathToIdr(
const std::string& path) {
const auto& it = path_to_idr_map_.find(path);
if (it == path_to_idr_map_.end()) {
return base::nullopt;
}
return base::Optional<int>(it->second);
}
} // namespace chromeos
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_COMPONENTS_TELEMETRY_EXTENSION_UI_TELEMETRY_EXTENSION_UNTRUSTED_SOURCE_H_
#define CHROMEOS_COMPONENTS_TELEMETRY_EXTENSION_UI_TELEMETRY_EXTENSION_UNTRUSTED_SOURCE_H_
#include <map>
#include <memory>
#include <string>
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
#include "url/gurl.h"
namespace chromeos {
// A data source that helps with loading resources needed by
// chrome-untrusted://telemetry-extension/ WebUI pages.
// There are two types of resources:
// 1. GRIT resourse if the resource path exist in |path_to_idr_map_|; otherwise,
// 2. Resource from the directory specified by
// |chromeos::switches::kTelemetryExtensionDirectory| command line switch.
class TelemetryExtensionUntrustedSource : public content::URLDataSource {
public:
static std::unique_ptr<TelemetryExtensionUntrustedSource> Create(
std::string source);
TelemetryExtensionUntrustedSource(const TelemetryExtensionUntrustedSource&) =
delete;
TelemetryExtensionUntrustedSource& operator=(
const TelemetryExtensionUntrustedSource&) = delete;
~TelemetryExtensionUntrustedSource() override;
void AddResourcePath(base::StringPiece path, int resource_id);
void OverrideContentSecurityPolicy(network::mojom::CSPDirectiveName directive,
const std::string& value);
// content::URLDataSource overrides:
std::string GetSource() override;
void StartDataRequest(
const GURL& url,
const content::WebContents::Getter& wc_getter,
content::URLDataSource::GotDataCallback callback) override;
std::string GetMimeType(const std::string& path) override;
std::string GetContentSecurityPolicy(
network::mojom::CSPDirectiveName directive) override;
private:
explicit TelemetryExtensionUntrustedSource(const std::string& source);
base::Optional<int> PathToIdr(const std::string& path);
const base::FilePath root_directory_;
const std::string source_;
std::map<std::string, int> path_to_idr_map_;
base::flat_map<network::mojom::CSPDirectiveName, std::string>
csp_overrides_map_;
};
} // namespace chromeos
#endif // CHROMEOS_COMPONENTS_TELEMETRY_EXTENSION_UI_TELEMETRY_EXTENSION_UNTRUSTED_SOURCE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment