[Development] Controlling QML Imports
Alan Alpert
416365416c at gmail.com
Tue Dec 11 04:23:51 CET 2012
For finer-grained control over QML it would make sense to have an API
allowing import restrictions. Two usecases come to mind, platform
security contexts and scriptable applications. Platform security
contexts is about when a platform using QML for the applications wants
to have restricted capabilities, like android does, so that packaged
applications can be restricted to just the capabilities the package
says it needs. The scriptable applications usecase is for if you want
to make your application scriptable with QML - you probably only want
the scripts to use your application provided functionality and not
create popup windows (this is why Window gets it own QtQuick.Window
import).
So the API that initially comes to mind is to add a couple of
functions to QQmlEngine
QStringList importWhitelist() const;
void setImportWhitelist(const QStringList &whitelist);
By default no whitelist is set, and when importWhitelist.isEmpty() the
engine doesn't apply any restrictions. When you set a whitelist any
module import attempt which doesn't match the whitelist would lead to
a compiler error like:
Example.qml:23:1:import QtQuick.Window 2.0
^ runtime does not allow this import
So what would the whitelist contain? I'm thinking string matching with
a * wildcard. Example usage:
QStringlist whites;
whites << "QtQuickControls 2.1";//Just QtQuickControls 2.1, perhaps
because that's the same minor version used elsewhere in the
application for consistency (makes sense if using QML as an
application scripting language)
whites << "QtQuick 2.*";//QtQuick major version 2, any minor version.
Does not include QtQuick 1.0, does not include any version of
QtQuick.Window or other modules starting with QtQuick
whites << "QtQml*";//Any module name starting with QtQml, so includes
a hypothetical future QtQml.Examples
whites << "Qt.labs*";//Any module name starting with Qt.labs, so
basically everything under imports/Qt/labs.
whites << "MyApp *";//Any version of the module named MyApp
engine.setImportWhitelist(whites);//Combine with setting the import
path for even more control (but QtQml* is always present)
A further issue to consider is what happens with relative directory
imports. If you've whitelisted modules I think relative directories
need to be completely disabled, otherwise apps can add relative
directories (like ".") which would load a qmldir that could look like
this:
plugin libNasty
Button 1.1 /usr/lib/qt5/imports/filesystems/DeleteEverything.qml
Item 2.0 ../../lib/qt5/imports/innocent/LoadsACPPPluginSomewhere.qml
This also means the implicit import is disabled. Note that qmldir file
could be maliciously inserted in the file structure, so if we're
trying to increase security only strictly trusted imports from trusted
locations can be allowed. That includes restricting the convenience of
the relative and implicit imports.
I do have one idea on how to effective retain the implicit import. A
package system containing all the files could allow the runtime to
expose just the files in the package, through an import packagedTypes
(the new qmlRegisterType allows this easily). There's then the
drawback of having them exposed as a flat structure instead of
importing subdirs. I'm begining to think that's a very subtle
difference in practice. I'd love to hear any other ideas people have
on overcoming the problem of losing those very convenient folder
imports.
With secure import paths and disabled relative imports, it even makes
sense to use "*" as the whitelist. That means all module imports are
allowed, but no relative imports, which is a powerful restriction. It
would mean only QtQml*, runtime exposed modules, and anything in the
module import paths (which could even be empty).
Any comments on adding an API like this?
Are there any other usecases which this might need to support?
Thinking about the broader context, is this enough for a QML security
policy or is more required? For example, could we move sensitive
functions (like XmlHttpRequest) to their own module, or would that be
ineffective or undesirable?
--
Alan Alpert
More information about the Development
mailing list