Showing the changes to libraries/script-engine/src/ScriptManager.cpp (1 of 2 files). Show all
Unified · Split
| @@ -2059,7 +2059,6 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 2058 unmodified lines | ||
| 2059 | 2059 | exception = testConstructor; |
| 2060 | 2060 | } |
| 2061 | 2061 | } else { |
| 2062 | − // V8TODO: re-enable entity script whitelist | |
| 2063 | 2062 | // ENTITY SCRIPT WHITELIST STARTS HERE |
| 2064 | 2063 | auto nodeList = DependencyManager::get<NodeList>(); |
| 2065 | 2064 | bool passList = false; // assume unsafe |
| @@ -2171,6 +2170,77 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 105 unmodified lines | ||
| 2171 | 2170 | return; // done processing script |
| 2172 | 2171 | }*/ |
| 2173 | 2172 | |
| 2173 | + // ENTITY SCRIPT WHITELIST STARTS HERE | |
| 2174 | + auto nodeList = DependencyManager::get<NodeList>(); | |
| 2175 | + bool passList = false; // assume unsafe | |
| 2176 | + QString whitelistPrefix = "[WHITELIST ENTITY SCRIPTS]"; | |
| 2177 | + QList<QString> safeURLPrefixes = { "file:///", "atp:", "cache:" }; | |
| 2178 | + safeURLPrefixes += qEnvironmentVariable("EXTRA_WHITELIST").trimmed().split(QRegExp("\\s*,\\s*"), Qt::SkipEmptyParts); | |
| 2179 | + | |
| 2180 | + // Entity Script Whitelist toggle check. | |
| 2181 | + Setting::Handle<bool> whitelistEnabled {"private/whitelistEnabled", false }; | |
| 2182 | + | |
| 2183 | + if (!whitelistEnabled.get()) { | |
| 2184 | + passList = true; | |
| 2185 | + } | |
| 2186 | + | |
| 2187 | + // Pull SAFEURLS from the Interface.JSON settings. | |
| 2188 | + QVariant raw = Setting::Handle<QVariant>("private/settingsSafeURLS").get(); | |
| 2189 | + QStringList settingsSafeURLS = raw.toString().trimmed().split(QRegExp("\\s*[,\r\n]+\\s*"), Qt::SkipEmptyParts); | |
| 2190 | + safeURLPrefixes += settingsSafeURLS; | |
| 2191 | + // END Pull SAFEURLS from the Interface.JSON settings. | |
| 2192 | + | |
| 2193 | + // Get current domain whitelist bypass, in case an entire domain is whitelisted. | |
| 2194 | + QString currentDomain = DependencyManager::get<AddressManager>()->getDomainURL().host(); | |
| 2195 | + | |
| 2196 | + QString domainSafeIP = nodeList->getDomainHandler().getHostname(); | |
| 2197 | + QString domainSafeURL = URL_SCHEME_OVERTE + "://" + currentDomain; | |
| 2198 | + for (const auto& str : safeURLPrefixes) { | |
| 2199 | + if (domainSafeURL.startsWith(str) || domainSafeIP.startsWith(str)) { | |
| 2200 | + qCDebug(scriptengine) << whitelistPrefix << "Whitelist Bypassed, entire domain is whitelisted. Current Domain Host: " | |
| 2201 | + << nodeList->getDomainHandler().getHostname() | |
| 2202 | + << "Current Domain: " << currentDomain; | |
| 2203 | + passList = true; | |
| 2204 | + } | |
| 2205 | + } | |
| 2206 | + // END bypass whitelist based on current domain. | |
| 2207 | + | |
| 2208 | + // Start processing scripts through the whitelist. | |
| 2209 | + if (ScriptManager::getContext() == "entity_server") { // If running on the server, do not engage whitelist. | |
| 2210 | + passList = true; | |
| 2211 | + } else if (!passList) { // If waved through, do not engage whitelist. | |
| 2212 | + for (const auto& str : safeURLPrefixes) { | |
| 2213 | + qCDebug(scriptengine) << whitelistPrefix << "Script URL: " << scriptOrURL << "TESTING AGAINST" << str << "RESULTS IN" | |
| 2214 | + << scriptOrURL.startsWith(str); | |
| 2215 | + if (!str.isEmpty() && scriptOrURL.startsWith(str)) { | |
| 2216 | + passList = true; | |
| 2217 | + qCDebug(scriptengine) << whitelistPrefix << "Script approved."; | |
| 2218 | + break; // Bail early since we found a match. | |
| 2219 | + } | |
| 2220 | + } | |
| 2221 | + } | |
| 2222 | + // END processing of scripts through the whitelist. | |
| 2223 | + | |
| 2224 | + ScriptValue exception; | |
| 2225 | + if (!passList) { // If the entity failed to pass for any reason, it's blocked and an error is thrown. | |
| 2226 | + qCDebug(scriptengine) << whitelistPrefix << "(disabled entity script)" << entityID.toString() << scriptOrURL; | |
| 2227 | + exception = _engine->makeError(_engine->newValue("UNSAFE_ENTITY_SCRIPTS == 0")); | |
| 2228 | + } | |
| 2229 | + | |
| 2230 | + if (exception.isError()) { | |
| 2231 | + setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT); | |
| 2232 | + auto scriptRuntimeException = std::make_shared<ScriptRuntimeException>(); | |
| 2233 | + ScriptValue message = exception.property("stack"); //This contains more details along with the error message | |
| 2234 | + scriptRuntimeException->errorMessage = message.toString(); | |
| 2235 | + scriptRuntimeException->thrownValue = exception; | |
| 2236 | + emit unhandledException(scriptRuntimeException); | |
| 2237 | + return; | |
| 2238 | + } | |
| 2239 | + // ENTITY SCRIPT WHITELIST ENDS HERE, uncomment below for original full disabling. | |
| 2240 | + | |
| 2241 | + // qCDebug(scriptengine) << "(disabled entity script)" << entityID.toString() << scriptOrURL; | |
| 2242 | + // exception = makeError("UNSAFE_ENTITY_SCRIPTS == 0"); | |
| 2243 | + | |
| 2174 | 2244 | // (this feeds into refreshFileScript) |
| 2175 | 2245 | int64_t lastModified = 0; |
| 2176 | 2246 | if (isFileUrl) { |