From 8758e4a9ba0229c80285ab0416c1a84cd165dec7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 9 Nov 2016 19:12:11 -0800 Subject: [PATCH] add support for whitelist entity scripts --- .../src/entities/EntityServer.cpp | 7 ++++ .../resources/describe-settings.json | 8 ++++ libraries/entities/src/EntityTree.cpp | 40 ++++++++++++++++++- libraries/entities/src/EntityTree.h | 3 ++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 7594d5dd2c..23eec6197c 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -278,6 +278,13 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio tree->setWantEditLogging(wantEditLogging); tree->setWantTerseEditLogging(wantTerseEditLogging); + + QString entityScriptSourceWhitelist; + if (readOptionString("entityScriptSourceWhitelist", settingsSectionObject, entityScriptSourceWhitelist)) { + tree->setEntityScriptSourceWhitelist(entityScriptSourceWhitelist); + } else { + tree->setEntityScriptSourceWhitelist(""); + } } void EntityServer::nodeAdded(SharedNodePointer node) { diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 4006673cad..e6663888b4 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1075,6 +1075,14 @@ "default": "3600", "advanced": true }, + { + "name": "entityScriptSourceWhitelist", + "label": "Entity Scripts Allowed from:", + "help": "The domains that entity scripts are allowed from. A comma separated list of domains that entity scripts are allowed from, if someone attempts to create and entity or edit an entity to have a different domain, it will be rejected. If left blank, any domain is allowed.", + "placeholder": "", + "default": "", + "advanced": true + }, { "name": "persistFilePath", "label": "Entities File Path", diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 7de841a321..5399adfa82 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -63,6 +63,11 @@ EntityTree::~EntityTree() { eraseAllOctreeElements(false); } +void EntityTree::setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist) { + _entityScriptSourceWhitelist = entityScriptSourceWhitelist.split(','); +} + + void EntityTree::createRootElement() { _rootElement = createNewElement(); } @@ -925,6 +930,9 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c quint64 startCreate = 0, endCreate = 0; quint64 startLogging = 0, endLogging = 0; + const quint64 LAST_EDITED_SERVERSIDE_BUMP = 1; // usec + bool suppressDisallowedScript = false; + _totalEditMessages++; EntityItemID entityItemID; @@ -935,7 +943,31 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c entityItemID, properties); endDecode = usecTimestampNow(); - const quint64 LAST_EDITED_SERVERSIDE_BUMP = 1; // usec + if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty() && !properties.getScript().isEmpty()) { + bool passedWhiteList = false; + auto entityScript = properties.getScript(); + for (const auto& whiteListedPrefix : _entityScriptSourceWhitelist) { + if (entityScript.startsWith(whiteListedPrefix, Qt::CaseInsensitive)) { + passedWhiteList = true; + break; + } + } + if (!passedWhiteList) { + if (wantEditLogging()) { + qCDebug(entities) << "User [" << senderNode->getUUID() << "] attempting to set entity script not on whitelist, edit rejected"; + } + + // If this was an add, we also want to tell the client that sent this edit that the entity was not added. + if (message.getType() == PacketType::EntityAdd) { + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + validEditPacket = passedWhiteList; + } else { + suppressDisallowedScript = true; + } + } + } + if ((message.getType() == PacketType::EntityAdd || (message.getType() == PacketType::EntityEdit && properties.lifetimeChanged())) && !senderNode->getCanRez() && senderNode->getCanRezTmp()) { @@ -960,6 +992,12 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c EntityItemPointer existingEntity = findEntityByEntityItemID(entityItemID); endLookup = usecTimestampNow(); if (existingEntity && message.getType() == PacketType::EntityEdit) { + + if (suppressDisallowedScript) { + properties.setLastEdited(properties.getLastEdited() + LAST_EDITED_SERVERSIDE_BUMP); + properties.setScript(existingEntity->getScript()); + } + // if the EntityItem exists, then update it startLogging = usecTimestampNow(); if (wantEditLogging()) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 68c8618482..441b686e3b 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -64,6 +64,7 @@ public: void setEntityMaxTmpLifetime(float maxTmpEntityLifetime) { _maxTmpEntityLifetime = maxTmpEntityLifetime; } + void setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist); /// Implements our type specific root element factory virtual OctreeElementPointer createNewElement(unsigned char* octalCode = NULL) override; @@ -342,6 +343,8 @@ protected: QHash> _childrenOfAvatars; // which entities are children of which avatars float _maxTmpEntityLifetime { DEFAULT_MAX_TMP_ENTITY_LIFETIME }; + + QStringList _entityScriptSourceWhitelist; }; #endif // hifi_EntityTree_h