History

Prohibit entity script file includes from outside the original entity script's directory (unless its in our resources).

howard-stearns committed 10 years ago

Commit 7b7ec5a1 parent 278fe932 files · +56 −21Browse files

Showing the changes to libraries/script-engine/src/ScriptEngine.cpp (1 of 2 files). Show all

Unified · Split

libraries/script-engine/src/ScriptEngine.cpp+51−19
@@ -578,7 +578,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 577 unmodified lines
578578 _registeredHandlers[entityID] = RegisteredEventHandlers();
579579 }
580580 CallbackList& handlersForEvent = _registeredHandlers[entityID][eventName];
581− CallbackData handlerData = {handler, currentEntityIdentifier};
581+ CallbackData handlerData = {handler, currentEntityIdentifier, currentSandboxURL};
582582 handlersForEvent << handlerData; // Note that the same handler can be added many times. See removeEntityEventHandler().
583583 }
584584
@@ -795,7 +795,7 @@ void ScriptEngine::timerFired() { · 210 unmodified lines
795795
796796 // call the associated JS function, if it exists
797797 if (timerData.function.isValid()) {
798− callWithEnvironment(timerData.definingEntityIdentifier, timerData.function, timerData.function, QScriptValueList());
798+ callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, QScriptValueList());
799799 }
800800 }
801801
@@ -810,7 +810,7 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int · 8 unmodified lines
810810 // make sure the timer stops when the script does
811811 connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop);
812812
813− CallbackData timerData = {function, currentEntityIdentifier};
813+ CallbackData timerData = {function, currentEntityIdentifier, currentSandboxURL};
814814 _timerFunctionMap.insert(newTimer, timerData);
815815
816816 newTimer->start(intervalMS);
@@ -885,19 +885,46 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 68 unmodified lines
885885 return; // bail early
886886 }
887887 QList<QUrl> urls;
888+ bool knowsSensitivity = false;
889+ Qt::CaseSensitivity sensitivity;
890+ auto getSensitivity = [&]() {
891+ if (!knowsSensitivity) {
892+ QString path = currentSandboxURL.path();
893+ QFileInfo upperFI(path.toUpper());
894+ QFileInfo lowerFI(path.toLower());
895+ sensitivity = (upperFI == lowerFI) ? Qt::CaseInsensitive : Qt::CaseSensitive;
896+ knowsSensitivity = true;
897+ }
898+ return sensitivity;
899+ };
900+
888901 for (QString file : includeFiles) {
889902 QUrl thisURL { resolvePath(file) };
890903 if (!_includedURLs.contains(thisURL)) {
891− urls.append(thisURL);
892− _includedURLs << thisURL;
893− }
894− else {
904+ if (!currentSandboxURL.isEmpty() && (thisURL.scheme() == "file") &&
905+ (
906+ (currentSandboxURL.scheme() != "file") ||
907+ (
908+ !thisURL.toString(QUrl::RemoveFilename).startsWith(defaultScriptsLocation().toString(), getSensitivity()) &&
909+ !thisURL.toString(QUrl::RemoveFilename).startsWith(currentSandboxURL.toString(QUrl::RemoveFilename), getSensitivity())
910+ )
911+ )
912+ ) {
913+ qCWarning(scriptengine) << "Script.include() ignoring file path" << thisURL << "outside of original entity script" << currentSandboxURL;
914+ } else {
915+ // We could also check here for CORS, but we don't yet.
916+ // It turns out that QUrl.resolve will not change hosts and copy authority, so we don't need to check that here.
917+ urls.append(thisURL);
918+ _includedURLs << thisURL;
919+ }
920+ } else {
895921 qCDebug(scriptengine) << "Script.include() ignoring previously included url:" << thisURL;
896922 }
897923 }
898924
899925 BatchLoader* loader = new BatchLoader(urls);
900926 EntityItemID capturedEntityIdentifier = currentEntityIdentifier;
927+ QUrl capturedSandboxURL = currentSandboxURL;
901928
902929 auto evaluateScripts = [=](const QMap<QUrl, QString>& data) {
903930 auto parentURL = _parentURL;
@@ -912,13 +939,13 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 8 unmodified lines
912939 auto operation = [&]() {
913940 evaluate(contents, url.toString());
914941 };
915− doWithEnvironment(capturedEntityIdentifier, operation);
942+ doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation);
916943 }
917944 }
918945 _parentURL = parentURL;
919946
920947 if (callback.isFunction()) {
921− callWithEnvironment(capturedEntityIdentifier, QScriptValue(callback), QScriptValue(), QScriptValueList());
948+ callWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, QScriptValue(callback), QScriptValue(), QScriptValueList());
922949 }
923950
924951 loader->deleteLater();
@@ -996,10 +1023,11 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin · 71 unmodified lines
9961023 CallbackList handlersForEvent = handlersOnEntity[eventName];
9971024 if (!handlersForEvent.isEmpty()) {
9981025 for (int i = 0; i < handlersForEvent.count(); ++i) {
999− // handlersForEvent[i] can tonain many handlers that may have each been added by different interface or entity scripts,
1026+ // handlersForEvent[i] can contain many handlers that may have each been added by different interface or entity scripts,
10001027 // and the entity scripts may be for entities other than the one this is a handler for.
10011028 // Fortunately, the definingEntityIdentifier captured the entity script id (if any) when the handler was added.
1002− callWithEnvironment(handlersForEvent[i].definingEntityIdentifier, handlersForEvent[i].function, QScriptValue(), eventHandlerArgs);
1029+ CallbackData& handler = handlersForEvent[i];
1030+ callWithEnvironment(handler.definingEntityIdentifier, handler.definingSandboxURL, handler.function, QScriptValue(), eventHandlerArgs);
10031031 }
10041032 }
10051033 }
@@ -1106,13 +1134,14 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 100 unmodified lines
11061134 lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch();
11071135 }
11081136 QScriptValue entityScriptConstructor, entityScriptObject;
1137+ QUrl sandboxURL = currentSandboxURL.isEmpty() ? scriptOrURL : currentSandboxURL;
11091138 auto initialization = [&]{
11101139 entityScriptConstructor = evaluate(contents, fileName);
11111140 entityScriptObject = entityScriptConstructor.construct();
11121141 };
1113− doWithEnvironment(entityID, initialization);
1142+ doWithEnvironment(entityID, sandboxURL, initialization);
11141143
1115− EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified };
1144+ EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified, sandboxURL };
11161145 _entityScripts[entityID] = newDetails;
11171146 if (isURL) {
11181147 setParentURL("");
@@ -1201,9 +1230,11 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { · 82 unmodified lines
12011230 // Even if entityID is supplied as currentEntityIdentifier, this still documents the source
12021231 // of the code being executed (e.g., if we ever sandbox different entity scripts, or provide different
12031232 // global values for different entity scripts).
1204−void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, std::function<void()> operation) {
1233+void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function<void()> operation) {
12051234 EntityItemID oldIdentifier = currentEntityIdentifier;
1235+ QUrl oldSandboxURL = currentSandboxURL;
12061236 currentEntityIdentifier = entityID;
1237+ currentSandboxURL = sandboxURL;
12071238
12081239 #if DEBUG_CURRENT_ENTITY
12091240 QScriptValue oldData = this->globalObject().property("debugEntityID");
@@ -1215,12 +1246,13 @@ void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, std::function · 5 unmodified lines
12151246 #endif
12161247
12171248 currentEntityIdentifier = oldIdentifier;
1249+ currentSandboxURL = oldSandboxURL;
12181250 }
1219−void ScriptEngine::callWithEnvironment(const EntityItemID& entityID, QScriptValue function, QScriptValue thisObject, QScriptValueList args) {
1251+void ScriptEngine::callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args) {
12201252 auto operation = [&]() {
12211253 function.call(thisObject, args);
12221254 };
1223− doWithEnvironment(entityID, operation);
1255+ doWithEnvironment(entityID, sandboxURL, operation);
12241256 }
12251257
12261258 void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) {
@@ -1249,7 +1281,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 22 unmodified lines
12491281 QScriptValueList args;
12501282 args << entityID.toScriptValue(this);
12511283 args << qScriptValueFromSequence(this, params);
1252− callWithEnvironment(entityID, entityScript.property(methodName), entityScript, args);
1284+ callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
12531285 }
12541286
12551287 }
@@ -1281,7 +1313,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 25 unmodified lines
12811313 QScriptValueList args;
12821314 args << entityID.toScriptValue(this);
12831315 args << event.toScriptValue(this);
1284− callWithEnvironment(entityID, entityScript.property(methodName), entityScript, args);
1316+ callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
12851317 }
12861318 }
12871319 }
@@ -1315,7 +1347,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 27 unmodified lines
13151347 args << entityID.toScriptValue(this);
13161348 args << otherID.toScriptValue(this);
13171349 args << collisionToScriptValue(this, collision);
1318− callWithEnvironment(entityID, entityScript.property(methodName), entityScript, args);
1350+ callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
13191351 }
13201352 }
13211353 }