Don't allow Web surfaces created by non-client scripts to access files

This commit is contained in:
Brad Davis 2018-07-27 15:25:57 -07:00
parent e321ee0cb6
commit 4b317a84d8
26 changed files with 275 additions and 234 deletions

View file

@ -62,6 +62,7 @@ Windows.ScrollingWindow {
url: "about:blank"
anchors.fill: parent
focus: true
profile: HFWebEngineProfile;
property string userScriptUrl: ""

View file

@ -6603,11 +6603,12 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
LocationScriptingInterface::locationSetter);
bool clientScript = scriptEngine->isClientScript();
scriptEngine->registerFunction("OverlayWindow", clientScript ? QmlWindowClass::constructor : QmlWindowClass::restricted_constructor);
#if !defined(Q_OS_ANDROID)
scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor);
scriptEngine->registerFunction("OverlayWebWindow", clientScript ? QmlWebWindowClass::constructor : QmlWebWindowClass::restricted_constructor);
#endif
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
scriptEngine->registerFunction("QmlFragment", QmlFragmentClass::constructor);
scriptEngine->registerFunction("QmlFragment", clientScript ? QmlFragmentClass::constructor : QmlFragmentClass::restricted_constructor);
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get<DesktopPreviewProvider>().data());

View file

@ -40,7 +40,8 @@ static QSize clampSize(const QSize& qsize, uint32_t maxDimension) {
return fromGlm(clampSize(toGlm(qsize), maxDimension));
}
const QmlContextObjectCallback OffscreenSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*, QQuickItem*) {};
const QmlContextObjectCallback OffscreenSurface::DEFAULT_CONTEXT_OBJECT_CALLBACK = [](QQmlContext*, QQuickItem*) {};
const QmlContextCallback OffscreenSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*) {};
void OffscreenSurface::initializeEngine(QQmlEngine* engine) {
}
@ -266,8 +267,8 @@ void OffscreenSurface::load(const QUrl& qmlSource, bool createNewContext, const
loadInternal(qmlSource, createNewContext, nullptr, callback);
}
void OffscreenSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback) {
load(qmlSource, true, callback);
void OffscreenSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback, const QmlContextCallback& contextCallback) {
loadInternal(qmlSource, true, nullptr, callback, contextCallback);
}
void OffscreenSurface::load(const QUrl& qmlSource, const QmlContextObjectCallback& callback) {
@ -281,7 +282,8 @@ void OffscreenSurface::load(const QString& qmlSourceFile, const QmlContextObject
void OffscreenSurface::loadInternal(const QUrl& qmlSource,
bool createNewContext,
QQuickItem* parent,
const QmlContextObjectCallback& callback) {
const QmlContextObjectCallback& callback,
const QmlContextCallback& contextCallback) {
PROFILE_RANGE_EX(app, "OffscreenSurface::loadInternal", 0xffff00ff, 0, { std::make_pair("url", qmlSource.toDisplayString()) });
if (QThread::currentThread() != thread()) {
qFatal("Called load on a non-surface thread");
@ -310,6 +312,7 @@ void OffscreenSurface::loadInternal(const QUrl& qmlSource,
}
auto targetContext = contextForUrl(finalQmlSource, parent, createNewContext);
contextCallback(targetContext);
QQmlComponent* qmlComponent;
{
PROFILE_RANGE(app, "new QQmlComponent");

View file

@ -37,13 +37,15 @@ namespace impl {
class SharedObject;
}
using QmlContextCallback = ::std::function<void(QQmlContext*)>;
using QmlContextObjectCallback = ::std::function<void(QQmlContext*, QQuickItem*)>;
class OffscreenSurface : public QObject {
Q_OBJECT
public:
static const QmlContextObjectCallback DEFAULT_CONTEXT_CALLBACK;
static const QmlContextObjectCallback DEFAULT_CONTEXT_OBJECT_CALLBACK;
static const QmlContextCallback DEFAULT_CONTEXT_CALLBACK;
using TextureAndFence = std::pair<uint32_t, void*>;
using MouseTranslator = std::function<QPoint(const QPointF&)>;
@ -85,10 +87,15 @@ public:
Q_INVOKABLE void load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback);
// For use from C++
Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
Q_INVOKABLE void load(const QString& qmlSourceFile, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK);
Q_INVOKABLE void load(const QUrl& qmlSource,
bool createNewContext,
const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK);
Q_INVOKABLE void load(const QString& qmlSourceFile,
const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK);
Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource,
const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_OBJECT_CALLBACK,
const QmlContextCallback& contextCallback = DEFAULT_CONTEXT_CALLBACK);
public slots:
virtual void onFocusObjectChanged(QObject* newFocus) {}
@ -103,19 +110,21 @@ protected:
virtual void initializeEngine(QQmlEngine* engine);
virtual void loadInternal(const QUrl& qmlSource,
bool createNewContext,
QQuickItem* parent,
const QmlContextObjectCallback& callback) final;
bool createNewContext,
QQuickItem* parent,
const QmlContextObjectCallback& callback,
const QmlContextCallback& contextCallback = DEFAULT_CONTEXT_CALLBACK) final;
virtual void finishQmlLoad(QQmlComponent* qmlComponent,
QQmlContext* qmlContext,
QQuickItem* parent,
const QmlContextObjectCallback& onQmlLoadedCallback) final;
QQmlContext* qmlContext,
QQuickItem* parent,
const QmlContextObjectCallback& onQmlLoadedCallback) final;
virtual void onRootCreated() {}
virtual void onItemCreated(QQmlContext* context, QQuickItem* newItem) {}
virtual void onRootContextCreated(QQmlContext* qmlContext) {}
virtual QQmlContext* contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext);
private:
MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } };
friend class hifi::qml::impl::SharedObject;

View file

@ -20,10 +20,10 @@
std::mutex QmlFragmentClass::_mutex;
std::map<QString, QScriptValue> QmlFragmentClass::_fragments;
QmlFragmentClass::QmlFragmentClass(QString id) : qml(id) { }
QmlFragmentClass::QmlFragmentClass(bool restricted, QString id) : QmlWindowClass(restricted), qml(id) { }
// Method called by Qt scripts to create a new bottom menu bar in Android
QScriptValue QmlFragmentClass::constructor(QScriptContext* context, QScriptEngine* engine) {
QScriptValue QmlFragmentClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) {
std::lock_guard<std::mutex> guard(_mutex);
auto qml = context->argument(0).toVariant().toMap().value("qml");
@ -41,7 +41,7 @@ QScriptValue QmlFragmentClass::constructor(QScriptContext* context, QScriptEngin
auto properties = parseArguments(context);
auto offscreenUi = DependencyManager::get<OffscreenUi>();
QmlFragmentClass* retVal = new QmlFragmentClass(qml.toString());
QmlFragmentClass* retVal = new QmlFragmentClass(restricted, qml.toString());
Q_ASSERT(retVal);
if (QThread::currentThread() != qApp->thread()) {
retVal->moveToThread(qApp->thread());

View file

@ -13,9 +13,19 @@
class QmlFragmentClass : public QmlWindowClass {
Q_OBJECT
private:
static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted);
public:
QmlFragmentClass(QString id);
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) {
return internal_constructor(context, engine, false);
}
static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){
return internal_constructor(context, engine, true);
}
QmlFragmentClass(bool restricted, QString id);
/**jsdoc
* Creates a new button, adds it to this and returns it.

View file

@ -20,10 +20,10 @@ static const char* const URL_PROPERTY = "source";
static const char* const SCRIPT_PROPERTY = "scriptUrl";
// Method called by Qt scripts to create a new web window in the overlay
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
QScriptValue QmlWebWindowClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) {
auto properties = parseArguments(context);
auto offscreenUi = DependencyManager::get<OffscreenUi>();
QmlWebWindowClass* retVal = new QmlWebWindowClass();
QmlWebWindowClass* retVal = new QmlWebWindowClass(restricted);
Q_ASSERT(retVal);
if (QThread::currentThread() != qApp->thread()) {
retVal->moveToThread(qApp->thread());

View file

@ -57,8 +57,18 @@ class QmlWebWindowClass : public QmlWindowClass {
Q_OBJECT
Q_PROPERTY(QString url READ getURL CONSTANT)
private:
static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted);
public:
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
QmlWebWindowClass(bool restricted) : QmlWindowClass(restricted) {}
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) {
return internal_constructor(context, engine, false);
}
static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){
return internal_constructor(context, engine, true);
}
public slots:

View file

@ -25,6 +25,8 @@
#include <shared/QtHelpers.h>
#include "OffscreenUi.h"
#include "ui/types/HFWebEngineProfile.h"
#include "ui/types/FileTypeProfile.h"
static const char* const SOURCE_PROPERTY = "source";
static const char* const TITLE_PROPERTY = "title";
@ -68,10 +70,10 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) {
// Method called by Qt scripts to create a new web window in the overlay
QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
QScriptValue QmlWindowClass::internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted) {
auto properties = parseArguments(context);
auto offscreenUi = DependencyManager::get<OffscreenUi>();
QmlWindowClass* retVal = new QmlWindowClass();
QmlWindowClass* retVal = new QmlWindowClass(restricted);
Q_ASSERT(retVal);
if (QThread::currentThread() != qApp->thread()) {
retVal->moveToThread(qApp->thread());
@ -83,7 +85,7 @@ QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine*
return engine->newQObject(retVal);
}
QmlWindowClass::QmlWindowClass() {
QmlWindowClass::QmlWindowClass(bool restricted) : _restricted(restricted) {
}
@ -99,8 +101,7 @@ void QmlWindowClass::initQml(QVariantMap properties) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
_source = properties[SOURCE_PROPERTY].toString();
// Build the event bridge and wrapper on the main thread
offscreenUi->loadInNewContext(qmlSource(), [&](QQmlContext* context, QObject* object) {
auto objectInitLambda = [&](QQmlContext* context, QObject* object) {
_qmlWindow = object;
context->setContextProperty(EVENT_BRIDGE_PROPERTY, this);
context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
@ -128,7 +129,24 @@ void QmlWindowClass::initQml(QVariantMap properties) {
if (metaObject->indexOfSignal("moved") >= 0)
connect(_qmlWindow, SIGNAL(moved(QVector2D)), this, SLOT(hasMoved(QVector2D)), Qt::QueuedConnection);
connect(_qmlWindow, SIGNAL(windowClosed()), this, SLOT(hasClosed()), Qt::QueuedConnection);
});
};
auto contextInitLambda = [&](QQmlContext* context) {
#if !defined(Q_OS_ANDROID)
// If the restricted flag is on, override the FileTypeProfile and HFWebEngineProfile objects in the
// QML surface root context with local ones
qDebug() << "Context initialization lambda";
if (_restricted) {
qDebug() << "Restricting web content";
ContextAwareProfile::restrictContext(context);
FileTypeProfile::registerWithContext(context);
HFWebEngineProfile::registerWithContext(context);
}
#endif
};
// Build the event bridge and wrapper on the main thread
offscreenUi->loadInNewContext(qmlSource(), objectInitLambda, contextInitLambda);
Q_ASSERT(_qmlWindow);
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));

View file

@ -38,9 +38,18 @@ class QmlWindowClass : public QObject {
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
private:
static QScriptValue internal_constructor(QScriptContext* context, QScriptEngine* engine, bool restricted);
public:
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
QmlWindowClass();
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) {
return internal_constructor(context, engine, false);
}
static QScriptValue restricted_constructor(QScriptContext* context, QScriptEngine* engine ){
return internal_constructor(context, engine, true);
}
QmlWindowClass(bool restricted);
~QmlWindowClass();
/**jsdoc
@ -51,6 +60,8 @@ public:
QQuickItem* asQuickItem() const;
public slots:
/**jsdoc
@ -250,10 +261,12 @@ protected:
QPointer<QObject> _qmlWindow;
QString _source;
const bool _restricted;
private:
// QmlWindow content may include WebView requiring EventBridge.
void setKeyboardRaised(QObject* object, bool raised, bool numeric = false);
};
#endif

View file

@ -265,19 +265,6 @@ void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
if (!javaScriptToInject.isEmpty()) {
rootContext->setContextProperty("eventBridgeJavaScriptToInject", QVariant(javaScriptToInject));
}
#if !defined(Q_OS_ANDROID)
rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext));
rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext));
{
PROFILE_RANGE(startup, "FileTypeProfile");
rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext));
}
{
PROFILE_RANGE(startup, "HFWebEngineProfile");
rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext));
}
#endif
rootContext->setContextProperty("Paths", DependencyManager::get<PathUtils>().data());
rootContext->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
rootContext->setContextProperty("Toolbars", DependencyManager::get<ToolbarScriptingInterface>().data());
@ -300,6 +287,17 @@ void OffscreenQmlSurface::onRootContextCreated(QQmlContext* qmlContext) {
// FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, qmlContext));
#if !defined(Q_OS_ANDROID)
{
PROFILE_RANGE(startup, "FileTypeProfile");
FileTypeProfile::registerWithContext(qmlContext);
}
{
PROFILE_RANGE(startup, "HFWebEngineProfile");
HFWebEngineProfile::registerWithContext(qmlContext);
}
#endif
}
QQmlContext* OffscreenQmlSurface::contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext) {

View file

@ -334,6 +334,8 @@ static const char* VRMENU_SOURCE_URL = "hifi/tablet/TabletMenu.qml";
class TabletRootWindow : public QmlWindowClass {
virtual QString qmlSource() const override { return "hifi/tablet/WindowRoot.qml"; }
public:
TabletRootWindow() : QmlWindowClass(false) {}
};
TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent), _name(name) {

View file

@ -0,0 +1,32 @@
//
// FileTypeProfile.cpp
// interface/src/networking
//
// Created by Kunal Gosar on 2017-03-10.
// Copyright 2017 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
//
#include "ContextAwareProfile.h"
#if !defined(Q_OS_ANDROID)
#include <QtQml/QQmlContext>
static const QString RESTRICTED_FLAG_PROPERTY = "RestrictFileAccess";
ContextAwareProfile::ContextAwareProfile(QQmlContext* parent) :
QQuickWebEngineProfile(parent), _context(parent) { }
void ContextAwareProfile::restrictContext(QQmlContext* context) {
context->setContextProperty(RESTRICTED_FLAG_PROPERTY, true);
}
bool ContextAwareProfile::isRestricted(QQmlContext* context) {
return context->contextProperty(RESTRICTED_FLAG_PROPERTY).toBool();
}
#endif

View file

@ -0,0 +1,42 @@
//
// Created by Bradley Austin Davis on 2018/07/27
// Copyright 2013-2018 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
//
#pragma once
#ifndef hifi_ContextAwareProfile_h
#define hifi_ContextAwareProfile_h
#include <QtCore/QtGlobal>
#if !defined(Q_OS_ANDROID)
#include <QtWebEngine/QQuickWebEngineProfile>
#include <QtWebEngineCore/QWebEngineUrlRequestInterceptor>
class QQmlContext;
class ContextAwareProfile : public QQuickWebEngineProfile {
public:
static void restrictContext(QQmlContext* context);
static bool isRestricted(QQmlContext* context);
QQmlContext* getContext() const { return _context; }
protected:
class RequestInterceptor : public QWebEngineUrlRequestInterceptor {
public:
RequestInterceptor(ContextAwareProfile* parent) : QWebEngineUrlRequestInterceptor(parent), _profile(parent) {}
QQmlContext* getContext() const { return _profile->getContext(); }
protected:
ContextAwareProfile* _profile;
};
ContextAwareProfile(QQmlContext* parent);
QQmlContext* _context;
};
#endif
#endif // hifi_FileTypeProfile_h

View file

@ -11,18 +11,31 @@
#include "FileTypeProfile.h"
#include "FileTypeRequestInterceptor.h"
#include <QtQml/QQmlContext>
#include "RequestFilters.h"
#if !defined(Q_OS_ANDROID)
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
FileTypeProfile::FileTypeProfile(QObject* parent) :
QQuickWebEngineProfile(parent)
FileTypeProfile::FileTypeProfile(QQmlContext* parent) :
ContextAwareProfile(parent)
{
static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)";
setHttpUserAgent(WEB_ENGINE_USER_AGENT);
auto requestInterceptor = new FileTypeRequestInterceptor(this);
auto requestInterceptor = new RequestInterceptor(this);
setRequestInterceptor(requestInterceptor);
}
void FileTypeProfile::RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
RequestFilters::interceptHFWebEngineRequest(info, getContext());
RequestFilters::interceptFileType(info, getContext());
}
void FileTypeProfile::registerWithContext(QQmlContext* context) {
context->setContextProperty("FileTypeProfile", new FileTypeProfile(context));
}
#endif

View file

@ -17,12 +17,23 @@
#include <QtCore/QtGlobal>
#if !defined(Q_OS_ANDROID)
#include <QtWebEngine/QQuickWebEngineProfile>
#include "ContextAwareProfile.h"
class FileTypeProfile : public ContextAwareProfile {
using Parent = ContextAwareProfile;
class FileTypeProfile : public QQuickWebEngineProfile {
public:
FileTypeProfile(QObject* parent = Q_NULLPTR);
static void registerWithContext(QQmlContext* parent);
protected:
FileTypeProfile(QQmlContext* parent);
class RequestInterceptor : public Parent::RequestInterceptor {
public:
RequestInterceptor(ContextAwareProfile* parent) : Parent::RequestInterceptor(parent) {}
void interceptRequest(QWebEngineUrlRequestInfo& info) override;
};
};
#endif
#endif // hifi_FileTypeProfile_h

View file

@ -1,25 +0,0 @@
//
// FileTypeRequestInterceptor.cpp
// interface/src/networking
//
// Created by Kunal Gosar on 2017-03-10.
// Copyright 2017 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
//
#include "FileTypeRequestInterceptor.h"
#include <QtCore/QDebug>
#include "RequestFilters.h"
#if !defined(Q_OS_ANDROID)
void FileTypeRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
RequestFilters::interceptHFWebEngineRequest(info);
RequestFilters::interceptFileType(info);
}
#endif

View file

@ -1,30 +0,0 @@
//
// FileTypeRequestInterceptor.h
// interface/src/networking
//
// Created by Kunal Gosar on 2017-03-10.
// Copyright 2017 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
//
#pragma once
#ifndef hifi_FileTypeRequestInterceptor_h
#define hifi_FileTypeRequestInterceptor_h
#include <QtCore/QtGlobal>
#if !defined(Q_OS_ANDROID)
#include <QWebEngineUrlRequestInterceptor>
class FileTypeRequestInterceptor : public QWebEngineUrlRequestInterceptor {
public:
FileTypeRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {};
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
};
#endif
#endif // hifi_FileTypeRequestInterceptor_h

View file

@ -1,23 +0,0 @@
//
// HFTabletWebEngineProfile.h
// interface/src/networking
//
// Created by Dante Ruiz on 2017-03-31.
// Copyright 2017 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
//
#ifndef hifi_HFTabletWebEngineProfile_h
#define hifi_HFTabletWebEngineProfile_h
#include <QtWebEngine/QQuickWebEngineProfile>
class HFTabletWebEngineProfile : public QQuickWebEngineProfile {
public:
HFTabletWebEngineProfile(QObject* parent = Q_NULLPTR);
};
#endif // hifi_HFTabletWebEngineProfile_h

View file

@ -1,30 +0,0 @@
//
// HFTabletWebEngineRequestInterceptor.h
// interface/src/networking
//
// Created by Dante Ruiz on 2017-3-31.
// Copyright 2017 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
//
#ifndef hifi_HFTabletWebEngineRequestInterceptor_h
#define hifi_HFTabletWebEngineRequestInterceptor_h
#if !defined(Q_OS_ANDROID)
#include <QtCore/QObject>
#include <QWebEngineUrlRequestInterceptor>
class HFTabletWebEngineRequestInterceptor
: public QWebEngineUrlRequestInterceptor
{
public:
HFTabletWebEngineRequestInterceptor(QObject* parent)
: QWebEngineUrlRequestInterceptor(parent)
{};
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
};
#endif
#endif // hifi_HFWebEngineRequestInterceptor_h

View file

@ -11,20 +11,28 @@
#include "HFWebEngineProfile.h"
#include "HFWebEngineRequestInterceptor.h"
#include <QtQml/QQmlContext>
#include "RequestFilters.h"
#if !defined(Q_OS_ANDROID)
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
HFWebEngineProfile::HFWebEngineProfile(QObject* parent) :
QQuickWebEngineProfile(parent)
HFWebEngineProfile::HFWebEngineProfile(QQmlContext* parent) : Parent(parent)
{
setStorageName(QML_WEB_ENGINE_STORAGE_NAME);
// we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user
auto requestInterceptor = new HFWebEngineRequestInterceptor(this);
setRequestInterceptor(requestInterceptor);
setRequestInterceptor(new RequestInterceptor(this));
}
#endif
void HFWebEngineProfile::RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
RequestFilters::interceptHFWebEngineRequest(info, getContext());
}
void HFWebEngineProfile::registerWithContext(QQmlContext* context) {
context->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(context));
}
#endif

View file

@ -14,15 +14,24 @@
#ifndef hifi_HFWebEngineProfile_h
#define hifi_HFWebEngineProfile_h
#include <QtCore/QtGlobal>
#include "ContextAwareProfile.h"
#if !defined(Q_OS_ANDROID)
#include <QtWebEngine/QQuickWebEngineProfile>
class HFWebEngineProfile : public QQuickWebEngineProfile {
class HFWebEngineProfile : public ContextAwareProfile {
using Parent = ContextAwareProfile;
public:
HFWebEngineProfile(QObject* parent = Q_NULLPTR);
static void registerWithContext(QQmlContext* parent);
protected:
HFWebEngineProfile(QQmlContext* parent);
class RequestInterceptor : public Parent::RequestInterceptor {
public:
RequestInterceptor(ContextAwareProfile* parent) : Parent::RequestInterceptor(parent) {}
void interceptRequest(QWebEngineUrlRequestInfo& info) override;
};
};
#endif
#endif // hifi_HFWebEngineProfile_h

View file

@ -1,25 +0,0 @@
//
// HFWebEngineRequestInterceptor.cpp
// interface/src/networking
//
// Created by Stephen Birarda on 2016-10-14.
// Copyright 2016 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
//
#include "HFWebEngineRequestInterceptor.h"
#include <QtCore/QDebug>
#include "AccountManager.h"
#include "RequestFilters.h"
#if !defined(Q_OS_ANDROID)
void HFWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
RequestFilters::interceptHFWebEngineRequest(info);
}
#endif

View file

@ -1,30 +0,0 @@
//
// HFWebEngineRequestInterceptor.h
// interface/src/networking
//
// Created by Stephen Birarda on 2016-10-14.
// Copyright 2016 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
//
#pragma once
#ifndef hifi_HFWebEngineRequestInterceptor_h
#define hifi_HFWebEngineRequestInterceptor_h
#include <QtCore/QtGlobal>
#if !defined(Q_OS_ANDROID)
#include <QWebEngineUrlRequestInterceptor>
class HFWebEngineRequestInterceptor : public QWebEngineUrlRequestInterceptor {
public:
HFWebEngineRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {};
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
};
#endif
#endif // hifi_HFWebEngineRequestInterceptor_h

View file

@ -10,12 +10,15 @@
//
#include "RequestFilters.h"
#include "NetworkingConstants.h"
#include <QtCore/QDebug>
#include <SettingHandle.h>
#include <QtCore/QFileInfo>
#include "AccountManager.h"
#include <SettingHandle.h>
#include <NetworkingConstants.h>
#include <AccountManager.h>
#include "ContextAwareProfile.h"
#if !defined(Q_OS_ANDROID)
@ -42,9 +45,28 @@ namespace {
return filename.endsWith(".json", Qt::CaseInsensitive);
}
bool blockLocalFiles(QWebEngineUrlRequestInfo& info) {
auto requestUrl = info.requestUrl();
if (!requestUrl.isLocalFile()) {
// Not a local file, do not block
return false;
}
QString targetFilePath = QFileInfo(requestUrl.toLocalFile()).canonicalFilePath();
// If we get here, then it's a local file that isn't whitelisted and the
// developer mode environment variable is not enabled. Block access to the file
qWarning() << "Blocking web access to local file path" << targetFilePath;
info.block(true);
return true;
}
}
void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info) {
void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, QQmlContext* context) {
if (ContextAwareProfile::isRestricted(context) && blockLocalFiles(info)) {
return;
}
// check if this is a request to a highfidelity URL
bool isAuthable = isAuthableHighFidelityURL(info.requestUrl());
if (isAuthable) {
@ -71,7 +93,7 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info)
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
}
void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info) {
void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info, QQmlContext* context) {
QString filename = info.requestUrl().fileName();
if (isScript(filename) || isJSON(filename)) {
static const QString CONTENT_HEADER = "Accept";

View file

@ -20,10 +20,12 @@
#include <QObject>
#include <QWebEngineUrlRequestInfo>
class QQmlContext;
class RequestFilters : public QObject {
public:
static void interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info);
static void interceptFileType(QWebEngineUrlRequestInfo& info);
static void interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, QQmlContext* context);
static void interceptFileType(QWebEngineUrlRequestInfo& info, QQmlContext* context);
};
#endif