File: convert_user_script.cc

package info (click to toggle)
chromium 145.0.7632.159-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,976,224 kB
  • sloc: cpp: 36,198,469; ansic: 7,634,080; javascript: 3,564,060; python: 1,649,622; xml: 838,470; asm: 717,087; pascal: 185,708; sh: 88,786; perl: 88,718; objc: 79,984; sql: 59,811; cs: 42,452; fortran: 24,101; makefile: 21,144; tcl: 15,277; php: 14,022; yacc: 9,066; ruby: 7,553; awk: 3,720; lisp: 3,233; lex: 1,328; ada: 727; jsp: 228; sed: 36
file content (189 lines) | stat: -rw-r--r-- 6,285 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/browser/convert_user_script.h"

#include <stddef.h>

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/base64.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/json/json_file_value_serializer.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "crypto/hash.h"
#include "extensions/browser/extension_user_script_loader.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/api/content_scripts.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/file_util.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/mojom/run_location.mojom-shared.h"
#include "extensions/common/user_script.h"
#include "extensions/common/utils/extension_types_utils.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_ANDROID)
#include "base/android/content_uri_utils.h"
#endif

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

namespace extensions {

scoped_refptr<Extension> ConvertUserScriptToExtension(
    const base::FilePath& user_script_path,
    const GURL& original_url,
    const base::FilePath& extensions_dir,
    std::u16string* error) {
  using ContentScript = api::content_scripts::ContentScript;

  std::string content;
  if (!base::ReadFileToString(user_script_path, &content)) {
    *error = u"Could not read source file.";
    return nullptr;
  }

  if (!base::IsStringUTF8(content)) {
    *error = u"User script must be UTF8 encoded.";
    return nullptr;
  }

  UserScript script;
  if (!UserScriptLoader::ParseMetadataHeader(content, &script)) {
    *error = u"Invalid script header.";
    return nullptr;
  }

  base::FilePath install_temp_dir =
      file_util::GetInstallTempDir(extensions_dir);
  if (install_temp_dir.empty()) {
    *error = u"Could not get path to profile temporary directory.";
    return nullptr;
  }

  base::ScopedTempDir temp_dir;
  if (!temp_dir.CreateUniqueTempDirUnderPath(install_temp_dir)) {
    *error = u"Could not create temporary directory.";
    return nullptr;
  }

  // Create the manifest
  base::Value::Dict root;
  std::string script_name;
  if (!script.name().empty() && !script.name_space().empty()) {
    script_name = script.name_space() + "/" + script.name();
  } else {
    script_name = original_url.spec();
  }

  // Create the public key.
  // User scripts are not signed, but the public key for an extension doubles as
  // its unique identity, and we need one of those. A user script's unique
  // identity is its namespace+name, so we hash that to create a public key.
  // There will be no corresponding private key, which means user scripts cannot
  // be auto-updated, or claimed in the gallery.
  std::string key =
      base::Base64Encode(crypto::hash::Sha256(base::as_byte_span(script_name)));

  // The script may not have a name field, but we need one for an extension. If
  // it is missing, use the filename of the original URL.
  std::string name = script.name();
#if BUILDFLAG(IS_ANDROID)
  if (name.empty()) {
    std::u16string display_name;
    if (base::MaybeGetFileDisplayName(user_script_path, &display_name) &&
        !display_name.empty()) {
      name = base::UTF16ToUTF8(display_name);
    }
  }
#endif
  if (name.empty()) {
    name = original_url.ExtractFileName();
  }
  root.Set(manifest_keys::kName, name);

  // Not all scripts have a version, but we need one. Default to 1.0 if it is
  // missing.
  if (!script.version().empty()) {
    root.Set(manifest_keys::kVersion, script.version());
  } else {
    root.Set(manifest_keys::kVersion, "1.0");
  }

  root.Set(manifest_keys::kDescription, script.description());
  root.Set(manifest_keys::kPublicKey, key);
  root.Set(manifest_keys::kConvertedFromUserScript, true);

  // If the script provides its own match patterns, we use those. Otherwise, we
  // generate some using the include globs.
  std::vector<std::string> matches;
  if (!script.url_patterns().is_empty()) {
    matches.reserve(script.url_patterns().size());
    for (const URLPattern& pattern : script.url_patterns()) {
      matches.push_back(pattern.GetAsString());
    }
  } else {
    // TODO(aa): Derive tighter matches where possible.
    matches.push_back("http://*/*");
    matches.push_back("https://*/*");
  }

  // Read the exclude matches, if any are present.
  std::vector<std::string> exclude_matches;
  exclude_matches.reserve(script.exclude_url_patterns().size());
  for (const URLPattern& pattern : script.exclude_url_patterns()) {
    exclude_matches.push_back(pattern.GetAsString());
  }

  ContentScript content_script;
  content_script.matches = std::move(matches);
  content_script.exclude_matches = std::move(exclude_matches);
  content_script.include_globs = script.globs();
  content_script.exclude_globs = script.exclude_globs();

  content_script.js.emplace();
  content_script.js->push_back("script.js");

  content_script.run_at = ConvertRunLocationForAPI(script.run_location());

  base::Value::List content_scripts;
  content_scripts.Append(content_script.ToValue());
  root.Set(api::content_scripts::ManifestKeys::kContentScripts,
           std::move(content_scripts));

  base::FilePath manifest_path = temp_dir.GetPath().Append(kManifestFilename);
  JSONFileValueSerializer serializer(manifest_path);
  if (!serializer.Serialize(root)) {
    *error = u"Could not write JSON.";
    return nullptr;
  }

  // Write the script file.
  if (!base::CopyFile(user_script_path,
                      temp_dir.GetPath().AppendASCII("script.js"))) {
    *error = u"Could not copy script file.";
    return nullptr;
  }

  scoped_refptr<Extension> extension =
      Extension::Create(temp_dir.GetPath(), mojom::ManifestLocation::kInternal,
                        root, Extension::NO_FLAGS, error);
  if (!extension.get()) {
    NOTREACHED() << "Could not init extension " << *error;
  }

  temp_dir.Take();  // The caller takes ownership of the directory.
  return extension;
}

}  // namespace extensions