How to isolate a QML item (<ifame> in QML?)
-
Question:
Is it possible to create a QML item which can not modify or access it's parent item?Context:
I am developing a QML based application which has the ability to load user written qml files into the host application over the internet (think web browser). I do not want these user written qml components to reach outside themselves and interact with the host or other user written components for obvious security reasons. That means when these 3rd party qml items are displayed they must not be able to traverse up their own parent hierarchy.I have played around with creating components from C++ with their own separate QQmlEngine instances which works to a point. But as soon as I add these new objects into my application their parent sgets set and they can reach out.
What I am basically looking for is a analogue to the <iframe> in HTML. Any pointers would be appreciated.
Similar Questions:
https://forum.qt.io/topic/73188/qml-sandboxing
https://forum.qt.io/topic/78904/application-in-a-child-process
https://forum.qt.io/topic/45356/solved-qml-sandboxing-of-components
https://forum.qt.io/topic/42348/embedding-a-qwindow-inside-a-qml-scene -
I've just tested out the QQuickWidget class and it's working as I hoped. I can give each QQuickWidget it's own separate QMLEngine instance and the qml items inside each widget are not able to reach out into Qt widget land.
There are still a few issues I'm looking into though regarding displaying one QQuickWidget over another. I've worked out how to make the top widget transparent but I'm still looking into event handling and how to stop the top QQuickWidget from eating events that should really be processed by the bottom QQuickWidget. I'm sure it's possible though.
It's a shame that this isn't possible from QML itself. It'd be lovely to have a option you just pass to a Loader to say to sandbox the loaded item or something along those lines but this will work for now.
-
Here is an example of what I am trying to stop for those who want to see some code:
It shows a component loaded by a loader reaching outside itself and reading the secret property on the root item.
import QtQuick 2.15 Rectangle { id: root anchors.fill: parent // The untrustedComponent should not be able to access this property string secret: "super secret string" // Imagine that this source component was actually loaded from a third party http server Component { id: untrustedComponent Rectangle { anchors { margins: 20 fill: parent } color: '#dddddd' Text { anchors.centerIn: parent text: { // Iterate up the heierarchy to find the parent with the secret var elements = 'Parent Items' var i = 0 var currentItem = parent while(currentItem !== null && currentItem !== undefined) { elements = elements + '\n' + i + ': ' + currentItem if(currentItem.secret) { elements = elements + ' FOUND SECRET: ' + currentItem.secret } i = i + 1 currentItem = currentItem.parent } return elements } } } } Loader { id: untrustedComponentLoader anchors.fill: parent sourceComponent: untrustedComponent } }
-
QML is not sandboxable, nor is its javascript. Add a 3rd party scripting language that you can sandbox yourself. I don't know of any declarative languages like QML. So you may end up with just code or code plus XML or something similar.
You can use LUA or add a version of V8 for this. I am sure there are others.
-
@fcarney from what I see you can assign different QMLEngines (which inherit from QJSEngine) to sandbox the JavaScript execution environments of two qml components. The problem I have is how to display one of these components inside the other. As soon as one is parented to the other then it can access the other through it's parent attribute (which I'm guessing reaches out into c++ land).
One potential solution I have come across is to base the top level interface on QtWidgets and embed my qml views via the QQuickWidget class. I've yet to test this out though as I was hoping for a QML / C++ only solution.
-
I've just tested out the QQuickWidget class and it's working as I hoped. I can give each QQuickWidget it's own separate QMLEngine instance and the qml items inside each widget are not able to reach out into Qt widget land.
There are still a few issues I'm looking into though regarding displaying one QQuickWidget over another. I've worked out how to make the top widget transparent but I'm still looking into event handling and how to stop the top QQuickWidget from eating events that should really be processed by the bottom QQuickWidget. I'm sure it's possible though.
It's a shame that this isn't possible from QML itself. It'd be lovely to have a option you just pass to a Loader to say to sandbox the loaded item or something along those lines but this will work for now.