mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-05-10 11:09:01 +08:00
Merge pull request #6994 from Ultimaker/ppscript_pre_secure
Prepare PostProcessingPlugin for security.
This commit is contained in:
commit
cc365c23e1
@ -1,23 +1,24 @@
|
||||
# Copyright (c) 2018 Jaime van Kessel, Ultimaker B.V.
|
||||
# The PostProcessingPlugin is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
||||
from typing import Dict, Type, TYPE_CHECKING, List, Optional, cast
|
||||
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Resources import Resources
|
||||
from UM.Application import Application
|
||||
from UM.Extension import Extension
|
||||
from UM.Logger import Logger
|
||||
|
||||
import configparser # The script lists are stored in metadata as serialised config files.
|
||||
import importlib.util
|
||||
import io # To allow configparser to write to a string.
|
||||
import os.path
|
||||
import pkgutil
|
||||
import sys
|
||||
import importlib.util
|
||||
from typing import Dict, Type, TYPE_CHECKING, List, Optional, cast
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Extension import Extension
|
||||
from UM.Logger import Logger
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Resources import Resources
|
||||
from UM.Trust import Trust
|
||||
from UM.i18n import i18nCatalog
|
||||
from cura import ApplicationMetadata
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
@ -161,7 +162,13 @@ class PostProcessingPlugin(QObject, Extension):
|
||||
# Iterate over all scripts.
|
||||
if script_name not in sys.modules:
|
||||
try:
|
||||
spec = importlib.util.spec_from_file_location(__name__ + "." + script_name, os.path.join(path, script_name + ".py"))
|
||||
file_path = os.path.join(path, script_name + ".py")
|
||||
if not self._isScriptAllowed(file_path):
|
||||
Logger.warning("Skipped loading post-processing script {}: not trusted".format(file_path))
|
||||
continue
|
||||
|
||||
spec = importlib.util.spec_from_file_location(__name__ + "." + script_name,
|
||||
file_path)
|
||||
loaded_script = importlib.util.module_from_spec(spec)
|
||||
if spec.loader is None:
|
||||
continue
|
||||
@ -334,4 +341,26 @@ class PostProcessingPlugin(QObject, Extension):
|
||||
if global_container_stack is not None:
|
||||
global_container_stack.propertyChanged.emit("post_processing_plugin", "value")
|
||||
|
||||
@staticmethod
|
||||
def _isScriptAllowed(file_path: str) -> bool:
|
||||
"""Checks whether the given file is allowed to be loaded"""
|
||||
if not ApplicationMetadata.IsEnterpriseVersion:
|
||||
# No signature needed
|
||||
return True
|
||||
|
||||
dir_path = os.path.split(file_path)[0] # type: str
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin")
|
||||
assert plugin_path is not None # appease mypy
|
||||
bundled_path = os.path.join(plugin_path, "scripts")
|
||||
if dir_path == bundled_path:
|
||||
# Bundled scripts are trusted.
|
||||
return True
|
||||
|
||||
trust_instance = Trust.getInstanceOrNone()
|
||||
if trust_instance is not None and Trust.signatureFileExistsFor(file_path):
|
||||
if trust_instance.signedFileCheck(file_path):
|
||||
return True
|
||||
|
||||
return False # Default verdict should be False, being the most secure fallback
|
||||
|
||||
|
||||
|
@ -0,0 +1,61 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Resources import Resources
|
||||
from UM.Trust import Trust
|
||||
from ..PostProcessingPlugin import PostProcessingPlugin
|
||||
|
||||
# not sure if needed
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
|
||||
|
||||
""" In this file, commnunity refers to regular Cura for makers."""
|
||||
|
||||
mock_plugin_registry = MagicMock()
|
||||
mock_plugin_registry.getPluginPath = MagicMock(return_value = "mocked_plugin_path")
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
@patch("cura.ApplicationMetadata.IsEnterpriseVersion", False)
|
||||
def test_community_user_script_allowed():
|
||||
assert PostProcessingPlugin._isScriptAllowed("blaat.py")
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
@patch("cura.ApplicationMetadata.IsEnterpriseVersion", False)
|
||||
def test_community_bundled_script_allowed():
|
||||
assert PostProcessingPlugin._isScriptAllowed(_bundled_file_path())
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
@patch("cura.ApplicationMetadata.IsEnterpriseVersion", True)
|
||||
@patch.object(PluginRegistry, "getInstance", return_value=mock_plugin_registry)
|
||||
def test_enterprise_unsigned_user_script_not_allowed(plugin_registry):
|
||||
assert not PostProcessingPlugin._isScriptAllowed("blaat.py")
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
@patch("cura.ApplicationMetadata.IsEnterpriseVersion", True)
|
||||
@patch.object(PluginRegistry, "getInstance", return_value=mock_plugin_registry)
|
||||
def test_enterprise_signed_user_script_allowed(plugin_registry):
|
||||
mocked_trust = MagicMock()
|
||||
mocked_trust.signedFileCheck = MagicMock(return_value=True)
|
||||
|
||||
plugin_registry.getPluginPath = MagicMock(return_value="mocked_plugin_path")
|
||||
|
||||
with patch.object(Trust, "signatureFileExistsFor", return_value = True):
|
||||
with patch("UM.Trust.Trust.getInstanceOrNone", return_value=mocked_trust):
|
||||
assert PostProcessingPlugin._isScriptAllowed("mocked_plugin_path/scripts/blaat.py")
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
@patch("cura.ApplicationMetadata.IsEnterpriseVersion", False)
|
||||
def test_enterprise_bundled_script_allowed():
|
||||
assert PostProcessingPlugin._isScriptAllowed(_bundled_file_path())
|
||||
|
||||
|
||||
def _bundled_file_path():
|
||||
return os.path.join(
|
||||
Resources.getStoragePath(Resources.Resources) + "scripts/blaat.py"
|
||||
)
|
0
plugins/PostProcessingPlugin/tests/__init__.py
Normal file
0
plugins/PostProcessingPlugin/tests/__init__.py
Normal file
Loading…
x
Reference in New Issue
Block a user