1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chromeos/components/telemetry_extension_ui/telemetry_extension_untrusted_source.h"
6 
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/logging.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/memory/ref_counted_memory.h"
13 #include "base/task/post_task.h"
14 #include "base/task/task_traits.h"
15 #include "base/task/thread_pool.h"
16 #include "chromeos/components/telemetry_extension_ui/url_constants.h"
17 #include "chromeos/constants/chromeos_switches.h"
18 #include "net/base/mime_util.h"
19 #include "ui/base/resource/resource_bundle.h"
20 
21 namespace chromeos {
22 
23 namespace {
24 
25 constexpr char kDefaultMime[] = "text/html";
26 
GetTelemetryDirectory()27 base::FilePath GetTelemetryDirectory() {
28   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
29   bool has_switch =
30       command_line->HasSwitch(chromeos::switches::kTelemetryExtensionDirectory);
31 
32   if (!has_switch) {
33     LOG(WARNING) << "Switch value '--telemetry-extension-dir' is not "
34                     "specified, some resources might not be loaded";
35     return base::FilePath();
36   }
37 
38   return base::FilePath(command_line->GetSwitchValueASCII(
39       chromeos::switches::kTelemetryExtensionDirectory));
40 }
41 
ReadFile(const base::FilePath & path,content::URLDataSource::GotDataCallback callback)42 void ReadFile(const base::FilePath& path,
43               content::URLDataSource::GotDataCallback callback) {
44   std::string content;
45   if (!base::ReadFileToString(path, &content)) {
46     PLOG(ERROR) << "Failed to read content from file: " << path;
47     std::move(callback).Run(nullptr);
48     return;
49   }
50 
51   scoped_refptr<base::RefCountedString> response =
52       base::RefCountedString::TakeString(&content);
53   std::move(callback).Run(response.get());
54 }
55 
56 }  // namespace
57 
58 // static
59 std::unique_ptr<TelemetryExtensionUntrustedSource>
Create(std::string source)60 TelemetryExtensionUntrustedSource::Create(std::string source) {
61   return base::WrapUnique(new TelemetryExtensionUntrustedSource(source));
62 }
63 
TelemetryExtensionUntrustedSource(const std::string & source)64 TelemetryExtensionUntrustedSource::TelemetryExtensionUntrustedSource(
65     const std::string& source)
66     : root_directory_(GetTelemetryDirectory()), source_(source) {}
67 
68 TelemetryExtensionUntrustedSource::~TelemetryExtensionUntrustedSource() =
69     default;
70 
AddResourcePath(base::StringPiece path,int resource_id)71 void TelemetryExtensionUntrustedSource::AddResourcePath(base::StringPiece path,
72                                                         int resource_id) {
73   path_to_idr_map_[path.as_string()] = resource_id;
74 }
75 
OverrideContentSecurityPolicy(network::mojom::CSPDirectiveName directive,const std::string & value)76 void TelemetryExtensionUntrustedSource::OverrideContentSecurityPolicy(
77     network::mojom::CSPDirectiveName directive,
78     const std::string& value) {
79   csp_overrides_map_.insert_or_assign(directive, value);
80 }
81 
GetSource()82 std::string TelemetryExtensionUntrustedSource::GetSource() {
83   return source_;
84 }
85 
StartDataRequest(const GURL & url,const content::WebContents::Getter & wc_getter,content::URLDataSource::GotDataCallback callback)86 void TelemetryExtensionUntrustedSource::StartDataRequest(
87     const GURL& url,
88     const content::WebContents::Getter& wc_getter,
89     content::URLDataSource::GotDataCallback callback) {
90   std::string path = content::URLDataSource::URLToRequestPath(url);
91   // Remove the query string for named resource lookups.
92   path = path.substr(0, path.find_first_of('?'));
93 
94   base::Optional<int> resource_id = PathToIdr(path);
95   if (resource_id.has_value()) {
96     scoped_refptr<base::RefCountedMemory> response(
97         ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
98             resource_id.value()));
99     std::move(callback).Run(response.get());
100     return;
101   }
102 
103   if (root_directory_.empty()) {
104     LOG(WARNING) << "Cannot load resources from disk, switch value "
105                     "'--telemetry-extension-dir' is not specified";
106     std::move(callback).Run(nullptr);
107     return;
108   }
109 
110   base::ThreadPool::PostTask(
111       FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
112       base::BindOnce(&ReadFile, root_directory_.Append(path),
113                      std::move(callback)));
114 }
115 
GetMimeType(const std::string & path)116 std::string TelemetryExtensionUntrustedSource::GetMimeType(
117     const std::string& path) {
118   const std::string ext = base::FilePath(path).Extension();
119   if (ext.empty()) {
120     return kDefaultMime;
121   }
122 
123   std::string mime_type;
124   net::GetWellKnownMimeTypeFromExtension(ext.substr(1), &mime_type);
125   return mime_type;
126 }
127 
GetContentSecurityPolicy(network::mojom::CSPDirectiveName directive)128 std::string TelemetryExtensionUntrustedSource::GetContentSecurityPolicy(
129     network::mojom::CSPDirectiveName directive) {
130   const auto& it = csp_overrides_map_.find(directive);
131   if (it == csp_overrides_map_.end()) {
132     return URLDataSource::GetContentSecurityPolicy(directive);
133   }
134   return it->second;
135 }
136 
PathToIdr(const std::string & path)137 base::Optional<int> TelemetryExtensionUntrustedSource::PathToIdr(
138     const std::string& path) {
139   const auto& it = path_to_idr_map_.find(path);
140   if (it == path_to_idr_map_.end()) {
141     return base::nullopt;
142   }
143   return base::Optional<int>(it->second);
144 }
145 
146 }  // namespace chromeos
147