mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 00:56:45 +02:00
Merge pull request #16120 from jherico/fix/bugz-1365
BUGZ-1365: prevent server scripts from accessing local files in HTML controls
This commit is contained in:
commit
8e3b337a6e
12 changed files with 141 additions and 17 deletions
11
interface/resources/qml/hifi/tablet/DynamicWebview.qml
Normal file
11
interface/resources/qml/hifi/tablet/DynamicWebview.qml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import "../../controls" as Controls
|
||||||
|
|
||||||
|
Controls.WebView {
|
||||||
|
id: root
|
||||||
|
function fromScript(message) {
|
||||||
|
root.url = message.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,7 @@
|
||||||
#include <SoundCacheScriptingInterface.h>
|
#include <SoundCacheScriptingInterface.h>
|
||||||
#include <ui/TabletScriptingInterface.h>
|
#include <ui/TabletScriptingInterface.h>
|
||||||
#include <ui/ToolbarScriptingInterface.h>
|
#include <ui/ToolbarScriptingInterface.h>
|
||||||
|
#include <ui/types/ContextAwareProfile.h>
|
||||||
#include <Tooltip.h>
|
#include <Tooltip.h>
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
#include <UserActivityLogger.h>
|
#include <UserActivityLogger.h>
|
||||||
|
@ -3285,6 +3286,9 @@ void Application::initializeUi() {
|
||||||
}
|
}
|
||||||
return result.toPoint();
|
return result.toPoint();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// BUGZ-1365 - the root context should explicitly default to being unable to load local HTML content
|
||||||
|
ContextAwareProfile::restrictContext(offscreenUi->getSurfaceContext(), true);
|
||||||
offscreenUi->resume();
|
offscreenUi->resume();
|
||||||
#endif
|
#endif
|
||||||
connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){
|
connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){
|
||||||
|
@ -7457,7 +7461,7 @@ void Application::addingEntityWithCertificate(const QString& certificateID, cons
|
||||||
ledger->updateLocation(certificateID, placeName);
|
ledger->updateLocation(certificateID, placeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointer scriptEngine) {
|
void Application::registerScriptEngineWithApplicationServices(const ScriptEnginePointer& scriptEngine) {
|
||||||
|
|
||||||
scriptEngine->setEmitScriptUpdatesFunction([this]() {
|
scriptEngine->setEmitScriptUpdatesFunction([this]() {
|
||||||
SharedNodePointer entityServerNode = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::EntityServer);
|
SharedNodePointer entityServerNode = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::EntityServer);
|
||||||
|
@ -7496,9 +7500,19 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
||||||
qScriptRegisterMetaType(scriptEngine.data(), RayToOverlayIntersectionResultToScriptValue,
|
qScriptRegisterMetaType(scriptEngine.data(), RayToOverlayIntersectionResultToScriptValue,
|
||||||
RayToOverlayIntersectionResultFromScriptValue);
|
RayToOverlayIntersectionResultFromScriptValue);
|
||||||
|
|
||||||
|
bool clientScript = scriptEngine->isClientScript();
|
||||||
|
|
||||||
#if !defined(DISABLE_QML)
|
#if !defined(DISABLE_QML)
|
||||||
scriptEngine->registerGlobalObject("OffscreenFlags", getOffscreenUI()->getFlags());
|
scriptEngine->registerGlobalObject("OffscreenFlags", getOffscreenUI()->getFlags());
|
||||||
scriptEngine->registerGlobalObject("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());
|
if (clientScript) {
|
||||||
|
scriptEngine->registerGlobalObject("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());
|
||||||
|
} else {
|
||||||
|
auto desktopScriptingInterface = new DesktopScriptingInterface(nullptr, true);
|
||||||
|
scriptEngine->registerGlobalObject("Desktop", desktopScriptingInterface);
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
desktopScriptingInterface->moveToThread(thread());
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
qScriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue<ToolbarProxy>, wrapperFromScriptValue<ToolbarProxy>);
|
qScriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue<ToolbarProxy>, wrapperFromScriptValue<ToolbarProxy>);
|
||||||
|
@ -7523,7 +7537,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
||||||
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
|
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
|
||||||
LocationScriptingInterface::locationSetter);
|
LocationScriptingInterface::locationSetter);
|
||||||
|
|
||||||
bool clientScript = scriptEngine->isClientScript();
|
|
||||||
scriptEngine->registerFunction("OverlayWindow", clientScript ? QmlWindowClass::constructor : QmlWindowClass::restricted_constructor);
|
scriptEngine->registerFunction("OverlayWindow", clientScript ? QmlWindowClass::constructor : QmlWindowClass::restricted_constructor);
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(DISABLE_QML)
|
#if !defined(Q_OS_ANDROID) && !defined(DISABLE_QML)
|
||||||
scriptEngine->registerFunction("OverlayWebWindow", clientScript ? QmlWebWindowClass::constructor : QmlWebWindowClass::restricted_constructor);
|
scriptEngine->registerFunction("OverlayWebWindow", clientScript ? QmlWebWindowClass::constructor : QmlWebWindowClass::restricted_constructor);
|
||||||
|
|
|
@ -252,7 +252,7 @@ public:
|
||||||
NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; }
|
NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; }
|
||||||
|
|
||||||
virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; }
|
virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; }
|
||||||
virtual void registerScriptEngineWithApplicationServices(ScriptEnginePointer scriptEngine) override;
|
virtual void registerScriptEngineWithApplicationServices(const ScriptEnginePointer& scriptEngine) override;
|
||||||
|
|
||||||
virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const override { copyDisplayViewFrustum(viewOut); }
|
virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const override { copyDisplayViewFrustum(viewOut); }
|
||||||
virtual QThread* getMainThread() override { return thread(); }
|
virtual QThread* getMainThread() override { return thread(); }
|
||||||
|
|
|
@ -52,6 +52,9 @@ static const QVariantMap DOCK_AREA {
|
||||||
{ "RIGHT", DockArea::RIGHT }
|
{ "RIGHT", DockArea::RIGHT }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DesktopScriptingInterface::DesktopScriptingInterface(QObject* parent, bool restricted)
|
||||||
|
: QObject(parent), _restricted(restricted) { }
|
||||||
|
|
||||||
int DesktopScriptingInterface::getWidth() {
|
int DesktopScriptingInterface::getWidth() {
|
||||||
QSize size = qApp->getWindow()->windowHandle()->screen()->virtualSize();
|
QSize size = qApp->getWindow()->windowHandle()->screen()->virtualSize();
|
||||||
return size.width();
|
return size.width();
|
||||||
|
@ -128,7 +131,7 @@ InteractiveWindowPointer DesktopScriptingInterface::createWindow(const QString&
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new InteractiveWindow(sourceUrl, properties);
|
return new InteractiveWindow(sourceUrl, properties, _restricted);
|
||||||
}
|
}
|
||||||
|
|
||||||
InteractiveWindowPointer DesktopScriptingInterface::createWindowOnThread(const QString& sourceUrl, const QVariantMap& properties, QThread* targetThread) {
|
InteractiveWindowPointer DesktopScriptingInterface::createWindowOnThread(const QString& sourceUrl, const QVariantMap& properties, QThread* targetThread) {
|
||||||
|
@ -139,7 +142,7 @@ InteractiveWindowPointer DesktopScriptingInterface::createWindowOnThread(const Q
|
||||||
if (!urlValidator(sourceUrl)) {
|
if (!urlValidator(sourceUrl)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
InteractiveWindowPointer window = new InteractiveWindow(sourceUrl, properties);
|
InteractiveWindowPointer window = new InteractiveWindow(sourceUrl, properties, _restricted);
|
||||||
window->moveToThread(targetThread);
|
window->moveToThread(targetThread);
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,8 @@ class DesktopScriptingInterface : public QObject, public Dependency {
|
||||||
Q_PROPERTY(int CLOSE_BUTTON_HIDES READ flagCloseButtonHides CONSTANT FINAL)
|
Q_PROPERTY(int CLOSE_BUTTON_HIDES READ flagCloseButtonHides CONSTANT FINAL)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
DesktopScriptingInterface(QObject* parent= nullptr, bool restricted = false);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Sets the opacity of the HUD surface.
|
* Sets the opacity of the HUD surface.
|
||||||
* @function Desktop.setHUDAlpha
|
* @function Desktop.setHUDAlpha
|
||||||
|
@ -106,6 +108,7 @@ private:
|
||||||
static QVariantMap getDockArea();
|
static QVariantMap getDockArea();
|
||||||
|
|
||||||
Q_INVOKABLE static QVariantMap getPresentationMode();
|
Q_INVOKABLE static QVariantMap getPresentationMode();
|
||||||
|
const bool _restricted;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#include <QQuickView>
|
#include <QQuickView>
|
||||||
|
|
||||||
#include <ui/types/ContextAwareProfile.h>
|
#include <ui/types/ContextAwareProfile.h>
|
||||||
|
#include <ui/types/HFWebEngineProfile.h>
|
||||||
|
#include <ui/types/FileTypeProfile.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <DockWidget.h>
|
#include <DockWidget.h>
|
||||||
#include <RegisteredMetaTypes.h>
|
#include <RegisteredMetaTypes.h>
|
||||||
|
@ -135,7 +137,7 @@ void InteractiveWindow::emitMainWindowResizeEvent() {
|
||||||
* Set at window creation. Possible flag values are provided as {@link Desktop|Desktop.ALWAYS_ON_TOP} and {@link Desktop|Desktop.CLOSE_BUTTON_HIDES}.
|
* Set at window creation. Possible flag values are provided as {@link Desktop|Desktop.ALWAYS_ON_TOP} and {@link Desktop|Desktop.CLOSE_BUTTON_HIDES}.
|
||||||
* Additional flag values can be found on Qt's website at https://doc.qt.io/qt-5/qt.html#WindowType-enum.
|
* Additional flag values can be found on Qt's website at https://doc.qt.io/qt-5/qt.html#WindowType-enum.
|
||||||
*/
|
*/
|
||||||
InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties) {
|
InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties, bool restricted) {
|
||||||
InteractiveWindowPresentationMode presentationMode = InteractiveWindowPresentationMode::Native;
|
InteractiveWindowPresentationMode presentationMode = InteractiveWindowPresentationMode::Native;
|
||||||
|
|
||||||
if (properties.contains(PRESENTATION_MODE_PROPERTY)) {
|
if (properties.contains(PRESENTATION_MODE_PROPERTY)) {
|
||||||
|
@ -230,10 +232,11 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
||||||
mainWindow->addDockWidget(dockArea, _dockWidget.get());
|
mainWindow->addDockWidget(dockArea, _dockWidget.get());
|
||||||
} else {
|
} else {
|
||||||
auto contextInitLambda = [&](QQmlContext* context) {
|
auto contextInitLambda = [&](QQmlContext* context) {
|
||||||
|
// If the restricted flag is on, the web content will not be able to access local files
|
||||||
|
ContextAwareProfile::restrictContext(context, restricted);
|
||||||
#if !defined(Q_OS_ANDROID)
|
#if !defined(Q_OS_ANDROID)
|
||||||
// If the restricted flag is on, override the FileTypeProfile and HFWebEngineProfile objects in the
|
FileTypeProfile::registerWithContext(context);
|
||||||
// QML surface root context with local ones
|
HFWebEngineProfile::registerWithContext(context);
|
||||||
ContextAwareProfile::restrictContext(context, false);
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ class InteractiveWindow : public QObject {
|
||||||
Q_PROPERTY(int presentationMode READ getPresentationMode WRITE setPresentationMode)
|
Q_PROPERTY(int presentationMode READ getPresentationMode WRITE setPresentationMode)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties);
|
InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties, bool restricted);
|
||||||
~InteractiveWindow();
|
~InteractiveWindow();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
class AbstractScriptingServicesInterface {
|
class AbstractScriptingServicesInterface {
|
||||||
public:
|
public:
|
||||||
/// Registers application specific services with a script engine.
|
/// Registers application specific services with a script engine.
|
||||||
virtual void registerScriptEngineWithApplicationServices(ScriptEnginePointer scriptEngine) = 0;
|
virtual void registerScriptEngineWithApplicationServices(const ScriptEnginePointer& scriptEngine) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -137,10 +137,8 @@ void QmlWindowClass::initQml(QVariantMap properties) {
|
||||||
// If the restricted flag is on, override the FileTypeProfile and HFWebEngineProfile objects in the
|
// If the restricted flag is on, override the FileTypeProfile and HFWebEngineProfile objects in the
|
||||||
// QML surface root context with local ones
|
// QML surface root context with local ones
|
||||||
ContextAwareProfile::restrictContext(context, _restricted);
|
ContextAwareProfile::restrictContext(context, _restricted);
|
||||||
if (_restricted) {
|
FileTypeProfile::registerWithContext(context);
|
||||||
FileTypeProfile::registerWithContext(context);
|
HFWebEngineProfile::registerWithContext(context);
|
||||||
HFWebEngineProfile::registerWithContext(context);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,15 @@ bool ContextAwareProfile::isRestrictedInternal() {
|
||||||
BLOCKING_INVOKE_METHOD(this, "isRestrictedInternal", Q_RETURN_ARG(bool, restrictedResult));
|
BLOCKING_INVOKE_METHOD(this, "isRestrictedInternal", Q_RETURN_ARG(bool, restrictedResult));
|
||||||
return restrictedResult;
|
return restrictedResult;
|
||||||
}
|
}
|
||||||
return _context->contextProperty(RESTRICTED_FLAG_PROPERTY).toBool();
|
|
||||||
|
QVariant variant = _context->contextProperty(RESTRICTED_FLAG_PROPERTY);
|
||||||
|
if (variant.isValid()) {
|
||||||
|
return variant.toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
// BUGZ-1365 - we MUST defalut to restricted mode in the absence of a flag, or it's too easy for someone to make
|
||||||
|
// a new mechanism for loading web content that fails to restrict access to local files
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContextAwareProfile::isRestricted() {
|
bool ContextAwareProfile::isRestricted() {
|
||||||
|
|
55
scripts/developer/tests/ui/testLocalFileAccess.js
Normal file
55
scripts/developer/tests/ui/testLocalFileAccess.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Created by Bradley Austin Davis on 2019/08/29
|
||||||
|
// Copyright 2013-2019 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
|
||||||
|
|
||||||
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
|
|
||||||
|
var QML_URL = "qrc:/qml/hifi/tablet/DynamicWebview.qml"
|
||||||
|
var LOCAL_FILE_URL = "file:///C:/test-file.html"
|
||||||
|
|
||||||
|
var TABLET_BUTTON_NAME = "SCRIPT";
|
||||||
|
var ICONS = {
|
||||||
|
icon: "icons/tablet-icons/menu-i.svg",
|
||||||
|
activeIcon: "icons/tablet-icons/meni-a.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
|
var button = tablet.addButton({
|
||||||
|
icon: ICONS.icon,
|
||||||
|
activeIcon: ICONS.activeIcon,
|
||||||
|
text: TABLET_BUTTON_NAME,
|
||||||
|
sortOrder: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
function onClicked() {
|
||||||
|
var window = Desktop.createWindow(QML_URL, {
|
||||||
|
title: "Local file access test",
|
||||||
|
additionalFlags: Desktop.ALWAYS_ON_TOP,
|
||||||
|
presentationMode: Desktop.PresentationMode.NATIVE,
|
||||||
|
size: { x: 640, y: 480 },
|
||||||
|
visible: true,
|
||||||
|
position: { x: 100, y: 100 },
|
||||||
|
});
|
||||||
|
window.sendToQml({ url: LOCAL_FILE_URL });
|
||||||
|
}
|
||||||
|
|
||||||
|
button.clicked.connect(onClicked);
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(function () {
|
||||||
|
button.clicked.disconnect(onClicked);
|
||||||
|
tablet.removeButton(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
}()); // END LOCAL_SCOPE
|
||||||
|
|
||||||
|
|
||||||
|
|
30
scripts/developer/tests/ui/testLocalFileAccessServer.js
Normal file
30
scripts/developer/tests/ui/testLocalFileAccessServer.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Created by Bradley Austin Davis on 2019/08/29
|
||||||
|
// Copyright 2013-2019 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var QML_URL = "qrc:/qml/hifi/tablet/DynamicWebview.qml"
|
||||||
|
var LOCAL_FILE_URL = "file:///C:/test-file.html"
|
||||||
|
|
||||||
|
var _this = this;
|
||||||
|
_this.preload = function (pEntityID) {
|
||||||
|
var window = Desktop.createWindow(QML_URL, {
|
||||||
|
title: "Local file access test",
|
||||||
|
additionalFlags: Desktop.ALWAYS_ON_TOP,
|
||||||
|
presentationMode: Desktop.PresentationMode.NATIVE,
|
||||||
|
size: { x: 640, y: 480 },
|
||||||
|
visible: true,
|
||||||
|
position: { x: 100, y: 100 },
|
||||||
|
});
|
||||||
|
window.sendToQml({ url: LOCAL_FILE_URL });
|
||||||
|
};
|
||||||
|
|
||||||
|
_this.unload = function () {
|
||||||
|
console.log("QQQ on preload");
|
||||||
|
};
|
||||||
|
});
|
Loading…
Reference in a new issue