diff --git a/interface/resources/qml/hifi/dialogs/security/EntityScriptQMLWhitelist.qml b/interface/resources/qml/hifi/dialogs/security/EntityScriptQMLWhitelist.qml index aa30b5d014..9e0b6ba4cf 100644 --- a/interface/resources/qml/hifi/dialogs/security/EntityScriptQMLWhitelist.qml +++ b/interface/resources/qml/hifi/dialogs/security/EntityScriptQMLWhitelist.qml @@ -8,7 +8,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// Security Settings for the Entity Script Whitelist +// Security Settings for the Entity Script QML Whitelist import Hifi 1.0 as Hifi import QtQuick 2.8 @@ -21,145 +21,186 @@ import "../../../windows" Rectangle { - - function getWhitelistAsText() { - var whitelist = Settings.getValue("private/settingsSafeURLS"); - var arrayWhitelist = whitelist.split(","); - var whitelistText = arrayWhitelist.join("\n"); - return whitelistText; - } - - function setWhitelistAsText(whitelistText) { - Settings.setValue("private/settingsSafeURLS", whitelistText.text); - - var originalSetString = whitelistText.text; - var originalSet = originalSetString.split(' ').join(''); - - var check = Settings.getValue("private/settingsSafeURLS"); - var arrayCheck = check.split(","); - var textCheck = arrayCheck.join("\n"); - - if(textCheck == originalSet) { - setWhitelistSuccess(true); - } else { - setWhitelistSuccess(false); - } - } - - function setWhitelistSuccess(success) { - if(success) { - notificationText.text = "Successfully saved settings."; - } else { - notificationText.text = "Error! Settings not saved."; - } - } - - - anchors.fill: parent - width: parent.width; - height: 120; - color: "#80010203"; + id: parentBody; - HifiStylesUit.RalewayRegular { - id: titleText; - text: "Entity Script Whitelist" - // Text size - size: 24; - // Style - color: "white"; - elide: Text.ElideRight; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - anchors.leftMargin: 20; - anchors.right: parent.right; - anchors.rightMargin: 20; - height: 60; - } + function getWhitelistAsText() { + var whitelist = Settings.getValue("private/settingsSafeURLS"); + var arrayWhitelist = whitelist.split(",").join("\n"); + return arrayWhitelist; + } + + function setWhitelistAsText(whitelistText) { + Settings.setValue("private/settingsSafeURLS", whitelistText.text); + + var originalSetString = whitelistText.text; + var originalSet = originalSetString.split(' ').join(''); + + var check = Settings.getValue("private/settingsSafeURLS"); + var arrayCheck = check.split(",").join("\n"); + + setWhitelistSuccess(arrayCheck === originalSet); + } + + function setWhitelistSuccess(success) { + if (success) { + notificationText.text = "Successfully saved settings."; + } else { + notificationText.text = "Error! Settings not saved."; + } + } + + function toggleWhitelist(enabled) { + Settings.setValue("private/whitelistEnabled", enabled); + console.info("Toggling Whitelist to:", enabled); + } + + function initCheckbox() { + var check = Settings.getValue("private/whitelistEnabled", false); + + if (check) { + whitelistEnabled.toggle(); + } + } - Rectangle { - id: textAreaRectangle; - color: "black"; + + anchors.fill: parent width: parent.width; - height: 250; - anchors.top: titleText.bottom; - - ScrollView { - id: textAreaScrollView - anchors.fill: parent; - width: parent.width - height: parent.height - contentWidth: parent.width - contentHeight: parent.height - clip: false; - - TextArea { - id: whitelistTextArea - text: getWhitelistAsText(); - onTextChanged: notificationText.text = ""; - width: parent.width; - height: parent.height; - font.family: "Ubuntu"; - font.pointSize: 12; - color: "white"; - } - } - - Button { - id: saveChanges - anchors.topMargin: 5; - anchors.leftMargin: 20; - anchors.rightMargin: 20; - x: textAreaRectangle.x + textAreaRectangle.width - width - 15; - y: textAreaRectangle.y + textAreaRectangle.height - height; - contentItem: Text { - text: saveChanges.text - font.family: "Ubuntu"; - font.pointSize: 12; - opacity: enabled ? 1.0 : 0.3 - color: "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - text: "Save Changes" - onClicked: setWhitelistAsText(whitelistTextArea) - - HifiStylesUit.RalewayRegular { - id: notificationText; - text: "" - // Text size - size: 14; - // Style - color: "white"; - elide: Text.ElideLeft; - // Anchors - anchors.right: parent.right; - anchors.rightMargin: 130; - } - } - + height: 120; + color: "#80010203"; + HifiStylesUit.RalewayRegular { - id: descriptionText; - text: "Separate your URLs by line, not commas. Example: - https://google.com/ - https://bing.com/ - https://mydomain.here/ - \nEnsure there are no spaces or whitespace. - \nFor QML files, you can only whitelist each file individually - ending with '.qml'." + id: titleText; + text: "Entity Script / QML Whitelist" // Text size - size: 16; + size: 24; // Style color: "white"; elide: Text.ElideRight; // Anchors - anchors.top: parent.bottom; - anchors.topMargin: 90; + anchors.top: parent.top; anchors.left: parent.left; anchors.leftMargin: 20; anchors.right: parent.right; anchors.rightMargin: 20; + height: 60; + + CheckBox { + Component.onCompleted: { + initCheckbox(); + } + + id: whitelistEnabled; + + anchors.right: parent.right; + anchors.top: parent.top; + anchors.topMargin: 10; + onToggled: { + toggleWhitelist(whitelistEnabled.checked) + } + + Label { + text: "Enabled" + color: "white" + font.pixelSize: 18; + anchors.right: parent.left; + anchors.top: parent.top; + anchors.topMargin: 10; + } + } } - } -} \ No newline at end of file + + Rectangle { + id: textAreaRectangle; + color: "black"; + width: parent.width; + height: 250; + anchors.top: titleText.bottom; + + ScrollView { + id: textAreaScrollView + anchors.fill: parent; + width: parent.width + height: parent.height + contentWidth: parent.width + contentHeight: parent.height + clip: false; + + TextArea { + id: whitelistTextArea + text: getWhitelistAsText(); + onTextChanged: notificationText.text = ""; + width: parent.width; + height: parent.height; + font.family: "Ubuntu"; + font.pointSize: 12; + color: "white"; + } + } + + Button { + id: saveChanges + anchors.topMargin: 5; + anchors.leftMargin: 20; + anchors.rightMargin: 20; + x: textAreaRectangle.x + textAreaRectangle.width - width - 15; + y: textAreaRectangle.y + textAreaRectangle.height - height; + contentItem: Text { + text: saveChanges.text + font.family: "Ubuntu"; + font.pointSize: 12; + opacity: enabled ? 1.0 : 0.3 + color: "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + text: "Save Changes" + onClicked: setWhitelistAsText(whitelistTextArea) + + HifiStylesUit.RalewayRegular { + id: notificationText; + text: "" + // Text size + size: 16; + // Style + color: "white"; + elide: Text.ElideLeft; + // Anchors + anchors.right: parent.left; + anchors.rightMargin: 10; + } + } + + HifiStylesUit.RalewayRegular { + id: descriptionText; + text: + "The whitelist checks scripts and QML as they are loaded.
+ Therefore, if a script is cached or has no reason to load again,
+ removing it from the whitelist will have no effect until
+ it is reloaded.
+ Separate your whitelisted domains by line, not commas. e.g. +
+ https://google.com/
+ hifi://the-spot/
+ 127.0.0.1
+ https://mydomain.here/ +
+ Ensure there are no spaces or whitespace.

+ For QML files, you can only whitelist each file individually
+ ending with '.qml'." + // Text size + size: 16; + // Style + color: "white"; + elide: Text.ElideRight; + textFormat: Text.RichText; + // Anchors + anchors.top: parent.bottom; + anchors.topMargin: 90; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + } + } +} diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index b0e5bbe8de..fbb69842bd 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -287,7 +287,7 @@ Menu::Menu() { } }); - // Settings > Entity Script Whitelist + // Settings > Entity Script / QML Whitelist action = addActionToQMenuAndActionHash(settingsMenu, "Entity Script / QML Whitelist"); connect(action, &QAction::triggered, [] { auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index de7fc488aa..98f1f3082f 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -2369,34 +2369,35 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co QList safeURLPrefixes = { "file:///", "atp:", "cache:" }; safeURLPrefixes += qEnvironmentVariable("EXTRA_WHITELIST").trimmed().split(QRegExp("\\s*,\\s*"), QString::SkipEmptyParts); - // IF WHITELIST IS DISABLED IN SETTINGS - bool whitelistEnabled = Setting::Handle("private/whitelistEnabled", true).get(); - if (!whitelistEnabled) { + // Entity Script Whitelist toggle check. + Setting::Handle whitelistEnabled {"private/whitelistEnabled", false }; + + if (!whitelistEnabled.get()) { passList = true; } - // PULL SAFEURLS FROM INTERFACE.JSON Settings + // Pull SAFEURLS from the Interface.JSON settings. QVariant raw = Setting::Handle("private/settingsSafeURLS").get(); QStringList settingsSafeURLS = raw.toString().trimmed().split(QRegExp("\\s*[,\r\n]+\\s*"), QString::SkipEmptyParts); safeURLPrefixes += settingsSafeURLS; - // END PULL SAFEURLS FROM INTERFACE.JSON Settings + // END Pull SAFEURLS from the Interface.JSON settings. - // GET CURRENT DOMAIN WHITELIST BYPASS, IN CASE AN ENTIRE DOMAIN IS WHITELISTED + // Get current domain whitelist bypass, in case an entire domain is whitelisted. QString currentDomain = DependencyManager::get()->getDomainURL().host(); QString domainSafeIP = nodeList->getDomainHandler().getHostname(); QString domainSafeURL = URL_SCHEME_HIFI + "://" + currentDomain; for (const auto& str : safeURLPrefixes) { if (domainSafeURL.startsWith(str) || domainSafeIP.startsWith(str)) { - qCDebug(scriptengine) << whitelistPrefix << "Whitelist Bypassed. Current Domain Host: " + qCDebug(scriptengine) << whitelistPrefix << "Whitelist Bypassed, entire domain is whitelisted. Current Domain Host: " << nodeList->getDomainHandler().getHostname() << "Current Domain: " << currentDomain; passList = true; } } - // END CURRENT DOMAIN WHITELIST BYPASS + // END bypass whitelist based on current domain. - // START CHECKING AGAINST THE WHITELIST + // Start processing scripts through the whitelist. if (ScriptEngine::getContext() == "entity_server") { // If running on the server, do not engage whitelist. passList = true; } else if (!passList) { // If waved through, do not engage whitelist. @@ -2406,11 +2407,11 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co if (!str.isEmpty() && scriptOrURL.startsWith(str)) { passList = true; qCDebug(scriptengine) << whitelistPrefix << "Script approved."; - break; // bail early since we found a match + break; // Bail early since we found a match. } } } - // END CHECKING AGAINST THE WHITELIST + // END processing of scripts through the whitelist. if (!passList) { // If the entity failed to pass for any reason, it's blocked and an error is thrown. qCDebug(scriptengine) << whitelistPrefix << "(disabled entity script)" << entityID.toString() << scriptOrURL;