[email protected] | ffbec69 | 2012-02-26 20:26:42 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 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 "chrome/browser/extensions/convert_user_script.h" |
| 6 | |
avi | a2f4804a | 2015-12-24 23:11:13 | [diff] [blame] | 7 | #include <stddef.h> |
| 8 | |
dcheng | 98e96a7 | 2016-06-11 03:41:48 | [diff] [blame] | 9 | #include <memory> |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 10 | #include <string> |
dcheng | 98e96a7 | 2016-06-11 03:41:48 | [diff] [blame] | 11 | #include <utility> |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 12 | #include <vector> |
| 13 | |
[email protected] | 978df34 | 2009-11-24 06:21:53 | [diff] [blame] | 14 | #include "base/base64.h" |
[email protected] | 5799981 | 2013-02-24 05:40:52 | [diff] [blame] | 15 | #include "base/files/file_path.h" |
thestig | 18dfb7a5 | 2014-08-26 10:44:04 | [diff] [blame] | 16 | #include "base/files/file_util.h" |
[email protected] | ea1a3f6 | 2012-11-16 20:34:23 | [diff] [blame] | 17 | #include "base/files/scoped_temp_dir.h" |
[email protected] | ffbec69 | 2012-02-26 20:26:42 | [diff] [blame] | 18 | #include "base/json/json_file_value_serializer.h" |
[email protected] | 00e7bef | 2013-06-10 20:35:17 | [diff] [blame] | 19 | #include "base/strings/string_util.h" |
[email protected] | 112158af | 2013-06-07 23:46:18 | [diff] [blame] | 20 | #include "base/strings/utf_string_conversions.h" |
vabr | 9984ea6 | 2017-04-10 11:33:49 | [diff] [blame] | 21 | #include "base/values.h" |
[email protected] | 69f27a2 | 2010-07-16 13:49:31 | [diff] [blame] | 22 | #include "chrome/common/chrome_paths.h" |
[email protected] | e078590 | 2011-05-19 23:34:17 | [diff] [blame] | 23 | #include "crypto/sha2.h" |
hanxi | 3b2b3df | 2015-02-24 15:28:07 | [diff] [blame] | 24 | #include "extensions/browser/extension_user_script_loader.h" |
Karandeep Bhatia | aac584a2 | 2020-11-05 12:32:27 | [diff] [blame] | 25 | #include "extensions/common/api/content_scripts.h" |
[email protected] | 993da5e | 2013-03-23 21:25:16 | [diff] [blame] | 26 | #include "extensions/common/constants.h" |
[email protected] | e4452d3 | 2013-11-15 23:07:41 | [diff] [blame] | 27 | #include "extensions/common/extension.h" |
[email protected] | 85df9d1 | 2014-04-15 17:02:14 | [diff] [blame] | 28 | #include "extensions/common/file_util.h" |
[email protected] | 0c3c973 | 2013-09-16 08:53:41 | [diff] [blame] | 29 | #include "extensions/common/manifest_constants.h" |
Julie Jeongeun Kim | 378db14d | 2021-03-05 01:53:00 | [diff] [blame] | 30 | #include "extensions/common/mojom/run_location.mojom-shared.h" |
[email protected] | 49d9b14 | 2013-07-19 08:50:27 | [diff] [blame] | 31 | #include "extensions/common/user_script.h" |
[email protected] | a6483d2 | 2013-07-03 22:11:00 | [diff] [blame] | 32 | #include "url/gurl.h" |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 33 | |
[email protected] | 20f97c9 | 2012-07-13 23:12:37 | [diff] [blame] | 34 | namespace extensions { |
| 35 | |
[email protected] | 66e4eb3 | 2010-10-27 20:37:41 | [diff] [blame] | 36 | scoped_refptr<Extension> ConvertUserScriptToExtension( |
Jan Wilken Dörrie | f27844b | 2021-03-11 23:18:48 | [diff] [blame] | 37 | const base::FilePath& user_script_path, |
| 38 | const GURL& original_url, |
| 39 | const base::FilePath& extensions_dir, |
| 40 | std::u16string* error) { |
Karandeep Bhatia | aac584a2 | 2020-11-05 12:32:27 | [diff] [blame] | 41 | using ContentScript = api::content_scripts::ContentScript; |
| 42 | |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 43 | std::string content; |
[email protected] | 82f84b9 | 2013-08-30 18:23:50 | [diff] [blame] | 44 | if (!base::ReadFileToString(user_script_path, &content)) { |
Jan Wilken Dörrie | 78e88d82e | 2021-03-23 15:24:22 | [diff] [blame] | 45 | *error = u"Could not read source file."; |
kylechar | b59c3d7 | 2019-10-29 05:26:26 | [diff] [blame] | 46 | return nullptr; |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 47 | } |
| 48 | |
[email protected] | 52796541 | 2014-05-07 14:38:26 | [diff] [blame] | 49 | if (!base::IsStringUTF8(content)) { |
Jan Wilken Dörrie | 78e88d82e | 2021-03-23 15:24:22 | [diff] [blame] | 50 | *error = u"User script must be UTF8 encoded."; |
kylechar | b59c3d7 | 2019-10-29 05:26:26 | [diff] [blame] | 51 | return nullptr; |
[email protected] | a4d5e007 | 2010-12-13 18:41:25 | [diff] [blame] | 52 | } |
| 53 | |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 54 | UserScript script; |
[email protected] | 15ad2ee | 2014-08-15 19:15:26 | [diff] [blame] | 55 | if (!UserScriptLoader::ParseMetadataHeader(content, &script)) { |
Jan Wilken Dörrie | 78e88d82e | 2021-03-23 15:24:22 | [diff] [blame] | 56 | *error = u"Invalid script header."; |
kylechar | b59c3d7 | 2019-10-29 05:26:26 | [diff] [blame] | 57 | return nullptr; |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 58 | } |
| 59 | |
[email protected] | 650b2d5 | 2013-02-10 03:41:45 | [diff] [blame] | 60 | base::FilePath install_temp_dir = |
[email protected] | 85df9d1 | 2014-04-15 17:02:14 | [diff] [blame] | 61 | file_util::GetInstallTempDir(extensions_dir); |
[email protected] | 171ab92d | 2012-10-19 01:16:34 | [diff] [blame] | 62 | if (install_temp_dir.empty()) { |
Jan Wilken Dörrie | 522370fb | 2021-04-16 17:22:39 | [diff] [blame] | 63 | *error = u"Could not get path to profile temporary directory."; |
kylechar | b59c3d7 | 2019-10-29 05:26:26 | [diff] [blame] | 64 | return nullptr; |
[email protected] | 650852e | 2011-01-19 13:26:02 | [diff] [blame] | 65 | } |
[email protected] | 69f27a2 | 2010-07-16 13:49:31 | [diff] [blame] | 66 | |
[email protected] | ea1a3f6 | 2012-11-16 20:34:23 | [diff] [blame] | 67 | base::ScopedTempDir temp_dir; |
[email protected] | 171ab92d | 2012-10-19 01:16:34 | [diff] [blame] | 68 | if (!temp_dir.CreateUniqueTempDirUnderPath(install_temp_dir)) { |
Jan Wilken Dörrie | 78e88d82e | 2021-03-23 15:24:22 | [diff] [blame] | 69 | *error = u"Could not create temporary directory."; |
kylechar | b59c3d7 | 2019-10-29 05:26:26 | [diff] [blame] | 70 | return nullptr; |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 71 | } |
| 72 | |
| 73 | // Create the manifest |
dcheng | c963c714 | 2016-04-08 03:55:22 | [diff] [blame] | 74 | std::unique_ptr<base::DictionaryValue> root(new base::DictionaryValue); |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 75 | std::string script_name; |
| 76 | if (!script.name().empty() && !script.name_space().empty()) |
| 77 | script_name = script.name_space() + "/" + script.name(); |
| 78 | else |
| 79 | script_name = original_url.spec(); |
| 80 | |
| 81 | // Create the public key. |
| 82 | // User scripts are not signed, but the public key for an extension doubles as |
| 83 | // its unique identity, and we need one of those. A user script's unique |
| 84 | // identity is its namespace+name, so we hash that to create a public key. |
| 85 | // There will be no corresponding private key, which means user scripts cannot |
| 86 | // be auto-updated, or claimed in the gallery. |
[email protected] | ea73fc7 | 2011-09-22 21:24:50 | [diff] [blame] | 87 | char raw[crypto::kSHA256Length] = {0}; |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 88 | std::string key; |
[email protected] | ea73fc7 | 2011-09-22 21:24:50 | [diff] [blame] | 89 | crypto::SHA256HashString(script_name, raw, crypto::kSHA256Length); |
iceman | 35ea753 | 2016-04-07 19:36:33 | [diff] [blame] | 90 | base::Base64Encode(base::StringPiece(raw, crypto::kSHA256Length), &key); |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 91 | |
| 92 | // The script may not have a name field, but we need one for an extension. If |
| 93 | // it is missing, use the filename of the original URL. |
| 94 | if (!script.name().empty()) |
oscarjohansson | 7716401 | 2018-06-11 07:00:11 | [diff] [blame] | 95 | root->SetString(manifest_keys::kName, script.name()); |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 96 | else |
oscarjohansson | 7716401 | 2018-06-11 07:00:11 | [diff] [blame] | 97 | root->SetString(manifest_keys::kName, original_url.ExtractFileName()); |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 98 | |
[email protected] | abae38bb | 2010-06-22 05:20:25 | [diff] [blame] | 99 | // Not all scripts have a version, but we need one. Default to 1.0 if it is |
| 100 | // missing. |
| 101 | if (!script.version().empty()) |
oscarjohansson | 7716401 | 2018-06-11 07:00:11 | [diff] [blame] | 102 | root->SetString(manifest_keys::kVersion, script.version()); |
[email protected] | abae38bb | 2010-06-22 05:20:25 | [diff] [blame] | 103 | else |
oscarjohansson | 7716401 | 2018-06-11 07:00:11 | [diff] [blame] | 104 | root->SetString(manifest_keys::kVersion, "1.0"); |
[email protected] | abae38bb | 2010-06-22 05:20:25 | [diff] [blame] | 105 | |
oscarjohansson | 7716401 | 2018-06-11 07:00:11 | [diff] [blame] | 106 | root->SetString(manifest_keys::kDescription, script.description()); |
| 107 | root->SetString(manifest_keys::kPublicKey, key); |
| 108 | root->SetBoolean(manifest_keys::kConvertedFromUserScript, true); |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 109 | |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 110 | // If the script provides its own match patterns, we use those. Otherwise, we |
| 111 | // generate some using the include globs. |
Karandeep Bhatia | aac584a2 | 2020-11-05 12:32:27 | [diff] [blame] | 112 | std::vector<std::string> matches; |
[email protected] | 06e8b8ff | 2011-07-13 15:03:47 | [diff] [blame] | 113 | if (!script.url_patterns().is_empty()) { |
Karandeep Bhatia | aac584a2 | 2020-11-05 12:32:27 | [diff] [blame] | 114 | matches.reserve(script.url_patterns().size()); |
| 115 | for (const URLPattern& pattern : script.url_patterns()) |
| 116 | matches.push_back(pattern.GetAsString()); |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 117 | } else { |
| 118 | // TODO(aa): Derive tighter matches where possible. |
Karandeep Bhatia | aac584a2 | 2020-11-05 12:32:27 | [diff] [blame] | 119 | matches.push_back("http://*/*"); |
| 120 | matches.push_back("https://*/*"); |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 121 | } |
| 122 | |
[email protected] | 2c17977c | 2011-12-19 07:05:43 | [diff] [blame] | 123 | // Read the exclude matches, if any are present. |
Karandeep Bhatia | aac584a2 | 2020-11-05 12:32:27 | [diff] [blame] | 124 | std::vector<std::string> exclude_matches; |
| 125 | exclude_matches.reserve(script.exclude_url_patterns().size()); |
| 126 | for (const URLPattern& pattern : script.exclude_url_patterns()) |
| 127 | exclude_matches.push_back(pattern.GetAsString()); |
| 128 | |
| 129 | ContentScript content_script; |
| 130 | content_script.matches = std::move(matches); |
| 131 | content_script.exclude_matches = |
| 132 | std::make_unique<std::vector<std::string>>(std::move(exclude_matches)); |
| 133 | content_script.include_globs = |
| 134 | std::make_unique<std::vector<std::string>>(script.globs()); |
| 135 | content_script.exclude_globs = |
| 136 | std::make_unique<std::vector<std::string>>(script.exclude_globs()); |
| 137 | |
| 138 | content_script.js = std::make_unique<std::vector<std::string>>(); |
| 139 | content_script.js->push_back("script.js"); |
| 140 | |
Julie Jeongeun Kim | 378db14d | 2021-03-05 01:53:00 | [diff] [blame] | 141 | if (script.run_location() == mojom::RunLocation::kDocumentStart) { |
Karandeep Bhatia | aac584a2 | 2020-11-05 12:32:27 | [diff] [blame] | 142 | content_script.run_at = api::content_scripts::RUN_AT_DOCUMENT_START; |
Julie Jeongeun Kim | 378db14d | 2021-03-05 01:53:00 | [diff] [blame] | 143 | } else if (script.run_location() == mojom::RunLocation::kDocumentEnd) { |
Karandeep Bhatia | aac584a2 | 2020-11-05 12:32:27 | [diff] [blame] | 144 | content_script.run_at = api::content_scripts::RUN_AT_DOCUMENT_END; |
Julie Jeongeun Kim | 378db14d | 2021-03-05 01:53:00 | [diff] [blame] | 145 | } else if (script.run_location() == mojom::RunLocation::kDocumentIdle) { |
Karandeep Bhatia | aac584a2 | 2020-11-05 12:32:27 | [diff] [blame] | 146 | // This is the default, but store it just in case we change that. |
| 147 | content_script.run_at = api::content_scripts::RUN_AT_DOCUMENT_IDLE; |
[email protected] | 2c17977c | 2011-12-19 07:05:43 | [diff] [blame] | 148 | } |
| 149 | |
Song Fangzhen | 06c83d5 | 2021-06-24 03:51:27 | [diff] [blame] | 150 | base::Value content_scripts(base::Value::Type::LIST); |
| 151 | content_scripts.Append( |
| 152 | base::Value::FromUniquePtrValue(content_script.ToValue())); |
| 153 | root->SetKey(api::content_scripts::ManifestKeys::kContentScripts, |
| 154 | std::move(content_scripts)); |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 155 | |
vabr | 9142fe2 | 2016-09-08 13:19:22 | [diff] [blame] | 156 | base::FilePath manifest_path = temp_dir.GetPath().Append(kManifestFilename); |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 157 | JSONFileValueSerializer serializer(manifest_path); |
| 158 | if (!serializer.Serialize(*root)) { |
Jan Wilken Dörrie | 78e88d82e | 2021-03-23 15:24:22 | [diff] [blame] | 159 | *error = u"Could not write JSON."; |
kylechar | b59c3d7 | 2019-10-29 05:26:26 | [diff] [blame] | 160 | return nullptr; |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | // Write the script file. |
[email protected] | f0ff2ad | 2013-07-09 17:42:26 | [diff] [blame] | 164 | if (!base::CopyFile(user_script_path, |
vabr | 9142fe2 | 2016-09-08 13:19:22 | [diff] [blame] | 165 | temp_dir.GetPath().AppendASCII("script.js"))) { |
Jan Wilken Dörrie | 78e88d82e | 2021-03-23 15:24:22 | [diff] [blame] | 166 | *error = u"Could not copy script file."; |
kylechar | b59c3d7 | 2019-10-29 05:26:26 | [diff] [blame] | 167 | return nullptr; |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 168 | } |
| 169 | |
[email protected] | fc67082 | 2011-12-17 09:33:49 | [diff] [blame] | 170 | // TODO(rdevlin.cronin): Continue removing std::string errors and replacing |
Jan Wilken Dörrie | f27844b | 2021-03-11 23:18:48 | [diff] [blame] | 171 | // with std::u16string |
[email protected] | fc67082 | 2011-12-17 09:33:49 | [diff] [blame] | 172 | std::string utf8_error; |
vabr | 9142fe2 | 2016-09-08 13:19:22 | [diff] [blame] | 173 | scoped_refptr<Extension> extension = |
Gyuyoung Kim | abc2338 | 2021-03-18 03:09:18 | [diff] [blame] | 174 | Extension::Create(temp_dir.GetPath(), mojom::ManifestLocation::kInternal, |
| 175 | *root, Extension::NO_FLAGS, &utf8_error); |
[email protected] | 0433872 | 2013-12-24 23:18:05 | [diff] [blame] | 176 | *error = base::UTF8ToUTF16(utf8_error); |
[email protected] | dc24976f | 2013-06-02 21:15:09 | [diff] [blame] | 177 | if (!extension.get()) { |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 178 | NOTREACHED() << "Could not init extension " << *error; |
kylechar | b59c3d7 | 2019-10-29 05:26:26 | [diff] [blame] | 179 | return nullptr; |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 180 | } |
| 181 | |
| 182 | temp_dir.Take(); // The caller takes ownership of the directory. |
[email protected] | 66e4eb3 | 2010-10-27 20:37:41 | [diff] [blame] | 183 | return extension; |
[email protected] | 6657afa6 | 2009-11-04 02:15:20 | [diff] [blame] | 184 | } |
[email protected] | 20f97c9 | 2012-07-13 23:12:37 | [diff] [blame] | 185 | |
| 186 | } // namespace extensions |