maya-umbrella 0.4.0__py2.py3-none-any.whl → 0.5.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- maya_umbrella/__version__.py +1 -1
- maya_umbrella/constants.py +13 -0
- maya_umbrella/core.py +10 -7
- maya_umbrella/filesystem.py +69 -34
- maya_umbrella/hooks/delete_turtle.py +3 -3
- maya_umbrella/hooks/delete_unknown_plugin_node.py +4 -3
- maya_umbrella/hooks/fix_model_panel.py +2 -2
- maya_umbrella/hooks/fix_on_model_change_3dc.py +3 -3
- maya_umbrella/maya_funs.py +36 -0
- maya_umbrella/vaccine.py +57 -26
- maya_umbrella/vaccines/vaccine1.py +1 -1
- maya_umbrella/vaccines/vaccine2.py +12 -20
- maya_umbrella/vaccines/vaccine3.py +16 -22
- {maya_umbrella-0.4.0.dist-info → maya_umbrella-0.5.0.dist-info}/METADATA +14 -9
- maya_umbrella-0.5.0.dist-info/RECORD +21 -0
- maya_umbrella-0.4.0.dist-info/RECORD +0 -20
- {maya_umbrella-0.4.0.dist-info → maya_umbrella-0.5.0.dist-info}/LICENSE +0 -0
- {maya_umbrella-0.4.0.dist-info → maya_umbrella-0.5.0.dist-info}/WHEEL +0 -0
maya_umbrella/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.5.0"
|
maya_umbrella/constants.py
CHANGED
|
@@ -3,3 +3,16 @@ PACKAGE_NAME = "maya_umbrella"
|
|
|
3
3
|
LOG_FORMAT = "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
|
|
4
4
|
|
|
5
5
|
LOG_MAX_BYTES = 1024 * 1024 * 5
|
|
6
|
+
|
|
7
|
+
FILE_VIRUS_SIGNATURES = [
|
|
8
|
+
"import vaccine",
|
|
9
|
+
"cmds.evalDeferred.*leukocyte.+",
|
|
10
|
+
"python(.*);.+exec.+(pyCode).+;",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
JOB_SCRIPTS_VIRUS_SIGNATURES = [
|
|
14
|
+
"petri_dish_path.+cmds.internalVar.+",
|
|
15
|
+
"userSetup",
|
|
16
|
+
"fuckVirus",
|
|
17
|
+
"python(.*);.+exec.+(pyCode).+;",
|
|
18
|
+
]
|
maya_umbrella/core.py
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
# Import built-in modules
|
|
2
2
|
import logging
|
|
3
3
|
|
|
4
|
-
# Import third-party modules
|
|
5
|
-
import maya.api.OpenMaya as om
|
|
6
|
-
import maya.cmds as cmds
|
|
7
|
-
|
|
8
4
|
# Import local modules
|
|
9
5
|
from maya_umbrella.filesystem import get_hooks
|
|
10
6
|
from maya_umbrella.filesystem import get_vaccines
|
|
11
7
|
from maya_umbrella.filesystem import load_hook
|
|
12
8
|
from maya_umbrella.log import setup_logger
|
|
9
|
+
from maya_umbrella.maya_funs import is_maya_standalone
|
|
10
|
+
from maya_umbrella.maya_funs import om
|
|
13
11
|
from maya_umbrella.vaccine import MayaVirusCleaner
|
|
14
12
|
|
|
15
13
|
|
|
@@ -63,11 +61,11 @@ class MayaVirusDefender(object):
|
|
|
63
61
|
|
|
64
62
|
def run_hooks(self):
|
|
65
63
|
"""Run all hooks, only works in non-batch mode."""
|
|
66
|
-
if not
|
|
64
|
+
if not is_maya_standalone():
|
|
67
65
|
for hook_file in get_hooks():
|
|
68
66
|
self.logger.debug("run_hook: %s", hook_file)
|
|
69
67
|
try:
|
|
70
|
-
load_hook(hook_file).hook(self.virus_cleaner)
|
|
68
|
+
load_hook(hook_file).hook(virus_cleaner=self.virus_cleaner)
|
|
71
69
|
except Exception as e:
|
|
72
70
|
self.logger.error("Error running hook: %s", e)
|
|
73
71
|
|
|
@@ -76,13 +74,17 @@ class MayaVirusDefender(object):
|
|
|
76
74
|
for vaccine in self.vaccines:
|
|
77
75
|
vaccine.collect_issues()
|
|
78
76
|
|
|
77
|
+
def reset(self):
|
|
78
|
+
"""Reset internal buffer."""
|
|
79
|
+
self.virus_cleaner.reset_all_issues()
|
|
80
|
+
|
|
79
81
|
def fix(self):
|
|
80
82
|
"""Fix all issues related to the Maya virus."""
|
|
81
83
|
self.virus_cleaner.fix_all_issues()
|
|
82
84
|
|
|
83
85
|
def report(self):
|
|
84
86
|
"""Report all issues related to the Maya virus."""
|
|
85
|
-
self.
|
|
87
|
+
self.reset()
|
|
86
88
|
self.collect()
|
|
87
89
|
self.virus_cleaner.report_all_issues()
|
|
88
90
|
|
|
@@ -106,6 +108,7 @@ class MayaVirusDefender(object):
|
|
|
106
108
|
**kwargs: Arbitrary keyword arguments.
|
|
107
109
|
"""
|
|
108
110
|
if self.auto_fix:
|
|
111
|
+
self.reset()
|
|
109
112
|
self.collect()
|
|
110
113
|
self.fix()
|
|
111
114
|
self.run_hooks()
|
maya_umbrella/filesystem.py
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
# Import built-in modules
|
|
2
|
+
from contextlib import contextmanager
|
|
2
3
|
import glob
|
|
3
4
|
import importlib
|
|
4
|
-
import
|
|
5
|
+
import logging
|
|
5
6
|
import os
|
|
6
7
|
import random
|
|
8
|
+
import re
|
|
7
9
|
import shutil
|
|
8
10
|
import string
|
|
11
|
+
import sys
|
|
9
12
|
import tempfile
|
|
10
13
|
|
|
11
14
|
# Import local modules
|
|
15
|
+
from maya_umbrella.constants import FILE_VIRUS_SIGNATURES
|
|
12
16
|
from maya_umbrella.constants import PACKAGE_NAME
|
|
13
17
|
|
|
14
18
|
|
|
19
|
+
PY2 = sys.version_info[0] == 2
|
|
20
|
+
PY3 = sys.version_info[0] == 3
|
|
21
|
+
|
|
22
|
+
|
|
15
23
|
def this_root():
|
|
16
24
|
"""Return the absolute path of the current file's directory."""
|
|
17
25
|
return os.path.abspath(os.path.dirname(__file__))
|
|
@@ -21,7 +29,7 @@ def safe_remove_file(file_path):
|
|
|
21
29
|
"""Remove the file at the given path without raising an error if the file does not exist."""
|
|
22
30
|
try:
|
|
23
31
|
os.remove(file_path)
|
|
24
|
-
except OSError:
|
|
32
|
+
except (OSError, IOError): # noqa: UP024
|
|
25
33
|
pass
|
|
26
34
|
|
|
27
35
|
|
|
@@ -29,42 +37,38 @@ def safe_rmtree(path):
|
|
|
29
37
|
"""Remove the directory at the given path without raising an error if the directory does not exist."""
|
|
30
38
|
try:
|
|
31
39
|
shutil.rmtree(path)
|
|
32
|
-
except OSError:
|
|
40
|
+
except (OSError, IOError): # noqa: UP024
|
|
33
41
|
pass
|
|
34
42
|
|
|
35
43
|
|
|
36
44
|
def read_file(path):
|
|
37
45
|
"""Read the content of the file at the given path."""
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
options = {"encoding": "utf-8"} if PY3 else {}
|
|
47
|
+
with open(path, **options) as file_:
|
|
48
|
+
try:
|
|
49
|
+
content = file_.read()
|
|
50
|
+
# maya-2022 UnicodeDecodeError from `plug-ins/mayaHIK.pres.mel`
|
|
51
|
+
except UnicodeDecodeError:
|
|
52
|
+
return ""
|
|
40
53
|
return content
|
|
41
54
|
|
|
42
55
|
|
|
43
56
|
def write_file(path, content):
|
|
44
57
|
"""Write the given content to the file at the given path."""
|
|
45
|
-
|
|
58
|
+
options = {"encoding": "utf-8"} if PY3 else {}
|
|
59
|
+
with atomic_writes(path, "w", **options) as file_:
|
|
46
60
|
file_.write(content)
|
|
47
61
|
|
|
48
62
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
before_ = file_data
|
|
59
|
-
file_data = file_data.replace(key, value)
|
|
60
|
-
if before_ != file_data:
|
|
61
|
-
found_keys.append(key)
|
|
62
|
-
write_file(target, file_data)
|
|
63
|
-
|
|
64
|
-
if report_error:
|
|
65
|
-
not_found = list(set(key_values.keys()) - set(found_keys))
|
|
66
|
-
if not_found:
|
|
67
|
-
raise IndexError("Not found: {0}".format(not_found))
|
|
63
|
+
@contextmanager
|
|
64
|
+
def atomic_writes(src, mode, **options):
|
|
65
|
+
temp_path = os.path.join(os.path.dirname(src), "._{}".format(id_generator()))
|
|
66
|
+
with open(temp_path, mode, **options) as f:
|
|
67
|
+
yield f
|
|
68
|
+
try:
|
|
69
|
+
os.replace(temp_path, src)
|
|
70
|
+
except AttributeError:
|
|
71
|
+
shutil.move(temp_path, src)
|
|
68
72
|
|
|
69
73
|
|
|
70
74
|
def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
|
|
@@ -77,7 +81,7 @@ def rename(src):
|
|
|
77
81
|
dst = os.path.join(os.path.dirname(src), "._{}".format(id_generator()))
|
|
78
82
|
try:
|
|
79
83
|
os.rename(src, dst)
|
|
80
|
-
except OSError:
|
|
84
|
+
except (OSError, IOError): # noqa: UP024
|
|
81
85
|
return src
|
|
82
86
|
return dst
|
|
83
87
|
|
|
@@ -88,7 +92,7 @@ def load_hook(hook_file):
|
|
|
88
92
|
if hasattr(importlib, "machinery"):
|
|
89
93
|
# Python 3
|
|
90
94
|
# Import built-in modules
|
|
91
|
-
from importlib.util import spec_from_loader
|
|
95
|
+
from importlib.util import spec_from_loader # noqa: F401
|
|
92
96
|
|
|
93
97
|
loader = importlib.machinery.SourceFileLoader(hook_name, hook_file)
|
|
94
98
|
spec = importlib.util.spec_from_loader(loader.name, loader=loader)
|
|
@@ -110,8 +114,7 @@ def get_hooks():
|
|
|
110
114
|
|
|
111
115
|
|
|
112
116
|
def get_vaccines():
|
|
113
|
-
"""
|
|
114
|
-
Get a list of all vaccine files.
|
|
117
|
+
"""Get a list of all vaccine files.
|
|
115
118
|
|
|
116
119
|
Returns:
|
|
117
120
|
list: A list of vaccine files.
|
|
@@ -121,8 +124,7 @@ def get_vaccines():
|
|
|
121
124
|
|
|
122
125
|
|
|
123
126
|
def get_log_root():
|
|
124
|
-
"""
|
|
125
|
-
Get the log root directory.
|
|
127
|
+
"""Get the log root directory.
|
|
126
128
|
|
|
127
129
|
Returns:
|
|
128
130
|
str: The log root directory.
|
|
@@ -131,8 +133,7 @@ def get_log_root():
|
|
|
131
133
|
|
|
132
134
|
|
|
133
135
|
def get_log_file():
|
|
134
|
-
"""
|
|
135
|
-
Get the path of the log file.
|
|
136
|
+
"""Get the path of the log file.
|
|
136
137
|
|
|
137
138
|
Returns:
|
|
138
139
|
str: The path of the log file.
|
|
@@ -140,7 +141,41 @@ def get_log_file():
|
|
|
140
141
|
root = get_log_root()
|
|
141
142
|
try:
|
|
142
143
|
os.makedirs(root)
|
|
143
|
-
except OSError:
|
|
144
|
+
except (OSError, IOError): # noqa: UP024
|
|
144
145
|
pass
|
|
145
146
|
name = os.getenv("MAYA_UMBRELLA_LOG_NAME", PACKAGE_NAME)
|
|
146
147
|
return os.path.join(root, "{name}.log".format(name=name))
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def remove_virus_file_by_signature(file_path, signatures, output_file_path=None):
|
|
151
|
+
logger = logging.getLogger(__name__)
|
|
152
|
+
data = read_file(file_path)
|
|
153
|
+
if check_virus_by_signature(data, signatures):
|
|
154
|
+
logger.warning("%s: Infected by Malware!", file_path)
|
|
155
|
+
fixed_data = replace_content_by_signatures(data, signatures)
|
|
156
|
+
write_file(output_file_path or file_path, fixed_data)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def replace_content_by_signatures(content, signatures):
|
|
160
|
+
for signature in signatures:
|
|
161
|
+
content = re.sub(signature, "", content)
|
|
162
|
+
return content
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def check_virus_file_by_signature(file_path, signatures=None):
|
|
166
|
+
signatures = signatures or FILE_VIRUS_SIGNATURES
|
|
167
|
+
try:
|
|
168
|
+
data = read_file(file_path)
|
|
169
|
+
return check_virus_by_signature(data, signatures)
|
|
170
|
+
except (OSError, IOError): # noqa: UP024
|
|
171
|
+
return False
|
|
172
|
+
except UnicodeDecodeError:
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def check_virus_by_signature(content, signatures=None):
|
|
177
|
+
signatures = signatures or FILE_VIRUS_SIGNATURES
|
|
178
|
+
for signature in signatures:
|
|
179
|
+
if re.search(signature, content):
|
|
180
|
+
return True
|
|
181
|
+
return False
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
# Import
|
|
2
|
-
|
|
1
|
+
# Import local modules
|
|
2
|
+
from maya_umbrella.maya_funs import cmds
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
def hook(virus_cleaner):
|
|
@@ -16,7 +16,8 @@ def hook(virus_cleaner):
|
|
|
16
16
|
cmds.lockNode(nodeObj, lock=False)
|
|
17
17
|
except Exception:
|
|
18
18
|
virus_cleaner.logger.warning(
|
|
19
|
-
"The node is locked and cannot be unlocked. skip {}".format(nodeObj)
|
|
19
|
+
"The node is locked and cannot be unlocked. skip {}".format(nodeObj)
|
|
20
|
+
)
|
|
20
21
|
continue
|
|
21
22
|
try:
|
|
22
23
|
cmds.delete(nodeObj)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""In order to facilitate unit testing in CI.
|
|
2
|
+
|
|
3
|
+
we extracted all the interfaces used in Maya and mocked them uniformly.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# Import third-party modules
|
|
8
|
+
try:
|
|
9
|
+
# Import third-party modules
|
|
10
|
+
import maya.api.OpenMaya as om
|
|
11
|
+
import maya.cmds as cmds
|
|
12
|
+
import maya.mel as mel
|
|
13
|
+
except ImportError:
|
|
14
|
+
# Backward compatibility to support test in uinstalled maya.
|
|
15
|
+
try:
|
|
16
|
+
# Import built-in modules
|
|
17
|
+
from unittest.mock import MagicMock
|
|
18
|
+
except ImportError:
|
|
19
|
+
# Import third-party modules
|
|
20
|
+
from mock import MagicMock # noqa: UP026
|
|
21
|
+
cmds = MagicMock()
|
|
22
|
+
om = MagicMock()
|
|
23
|
+
mel = MagicMock()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def is_maya_standalone():
|
|
27
|
+
"""Return True if Maya is standalone."""
|
|
28
|
+
return cmds.about(batch=True)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def check_reference_node_exists(node_name):
|
|
32
|
+
"""Check if reference node exists."""
|
|
33
|
+
try:
|
|
34
|
+
return cmds.referenceQuery(node_name, isNodeReferenced=True)
|
|
35
|
+
except RuntimeError:
|
|
36
|
+
return False
|
maya_umbrella/vaccine.py
CHANGED
|
@@ -3,17 +3,20 @@ from collections import defaultdict
|
|
|
3
3
|
import glob
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
6
|
-
|
|
7
|
-
# Import third-party modules
|
|
8
|
-
import maya.cmds as cmds
|
|
6
|
+
import re
|
|
9
7
|
|
|
10
8
|
# Import local modules
|
|
9
|
+
from maya_umbrella.constants import FILE_VIRUS_SIGNATURES
|
|
10
|
+
from maya_umbrella.filesystem import remove_virus_file_by_signature
|
|
11
11
|
from maya_umbrella.filesystem import safe_remove_file
|
|
12
12
|
from maya_umbrella.filesystem import safe_rmtree
|
|
13
|
+
from maya_umbrella.maya_funs import check_reference_node_exists
|
|
14
|
+
from maya_umbrella.maya_funs import cmds
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
class MayaVirusCleaner(object):
|
|
16
18
|
_bad_files = []
|
|
19
|
+
_infected_files = []
|
|
17
20
|
_bad_nodes = []
|
|
18
21
|
_bad_script_nodes = []
|
|
19
22
|
_bad_script_jobs = []
|
|
@@ -32,7 +35,7 @@ class MayaVirusCleaner(object):
|
|
|
32
35
|
@property
|
|
33
36
|
def maya_file(self):
|
|
34
37
|
"""Return the current Maya file."""
|
|
35
|
-
return cmds.file(
|
|
38
|
+
return cmds.file(query=True, sceneName=True, shortName=True) or "empty/scene"
|
|
36
39
|
|
|
37
40
|
@property
|
|
38
41
|
def maya_install_root(self):
|
|
@@ -69,17 +72,26 @@ class MayaVirusCleaner(object):
|
|
|
69
72
|
"""Return a list of bad script jobs."""
|
|
70
73
|
return list(set(self._bad_script_jobs))
|
|
71
74
|
|
|
75
|
+
@property
|
|
76
|
+
def infected_files(self):
|
|
77
|
+
return self._infected_files
|
|
78
|
+
|
|
72
79
|
def callback_remove_rename_temp_files(self, *args, **kwargs):
|
|
73
|
-
"""
|
|
74
|
-
Remove temporary files in the local script path.
|
|
75
|
-
"""
|
|
80
|
+
"""Remove temporary files in the local script path."""
|
|
76
81
|
self.logger.info("Removing temporary files in %s", self.local_script_path)
|
|
77
|
-
|
|
82
|
+
for temp_file in glob.glob(os.path.join(self.local_script_path, "._*")):
|
|
83
|
+
safe_remove_file(temp_file)
|
|
78
84
|
|
|
79
85
|
@property
|
|
80
86
|
def registered_callbacks(self):
|
|
81
87
|
return self._registered_callbacks
|
|
82
88
|
|
|
89
|
+
def add_infected_files(self, files):
|
|
90
|
+
self._infected_files.extend(files)
|
|
91
|
+
|
|
92
|
+
def add_infected_file(self, file):
|
|
93
|
+
self._infected_files.append(file)
|
|
94
|
+
|
|
83
95
|
def add_bad_files(self, files):
|
|
84
96
|
self._bad_files.extend(files)
|
|
85
97
|
|
|
@@ -145,12 +157,10 @@ class MayaVirusCleaner(object):
|
|
|
145
157
|
def add_fix_function(self, func):
|
|
146
158
|
self._fix_funcs.append(func)
|
|
147
159
|
|
|
148
|
-
# fix
|
|
149
|
-
|
|
150
160
|
def fix_script_jobs(self):
|
|
151
161
|
for script_job in self.bad_script_jobs:
|
|
152
|
-
script_num = int(
|
|
153
|
-
self.logger.info("Kill script job
|
|
162
|
+
script_num = int(re.findall(r"^(\d+):", script_job)[0])
|
|
163
|
+
self.logger.info("Kill script job %s", script_job)
|
|
154
164
|
cmds.scriptJob(kill=script_num, force=True)
|
|
155
165
|
self._bad_script_jobs.remove(script_job)
|
|
156
166
|
|
|
@@ -158,40 +168,60 @@ class MayaVirusCleaner(object):
|
|
|
158
168
|
for file_ in self.bad_files:
|
|
159
169
|
if os.path.exists(file_):
|
|
160
170
|
if os.path.isfile(file_):
|
|
161
|
-
self.logger.info("Removing
|
|
171
|
+
self.logger.info("Removing %s", file_)
|
|
162
172
|
safe_remove_file(file_)
|
|
163
173
|
self._bad_files.remove(file_)
|
|
164
174
|
else:
|
|
165
|
-
self.logger.info("Removing folder
|
|
175
|
+
self.logger.info("Removing folder %s", file_)
|
|
166
176
|
safe_rmtree(file_)
|
|
167
177
|
self._bad_files.remove(file_)
|
|
168
178
|
|
|
169
179
|
def fix_bad_nodes(self):
|
|
170
180
|
for node in self.bad_nodes:
|
|
171
181
|
self.logger.info("Deleting %s", node)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
182
|
+
is_referenced = check_reference_node_exists(node)
|
|
183
|
+
if is_referenced:
|
|
184
|
+
try:
|
|
185
|
+
cmds.setAttr("{node}.before".format(node=node), "", type="string")
|
|
186
|
+
cmds.setAttr("{node}.after".format(node=node), "", type="string")
|
|
187
|
+
cmds.setAttr("{node}.scriptType".format(node=node), 0)
|
|
188
|
+
self._bad_nodes.remove(node)
|
|
189
|
+
except Exception as e:
|
|
190
|
+
self.logger.debug(e)
|
|
191
|
+
else:
|
|
192
|
+
try:
|
|
193
|
+
cmds.lockNode(node, lock=False)
|
|
194
|
+
except ValueError:
|
|
195
|
+
pass
|
|
196
|
+
try:
|
|
197
|
+
cmds.delete(node)
|
|
198
|
+
except ValueError:
|
|
199
|
+
pass
|
|
200
|
+
self._bad_nodes.remove(node)
|
|
201
|
+
|
|
202
|
+
def fix_infected_files(self):
|
|
203
|
+
for file_path in self.infected_files:
|
|
204
|
+
self.logger.info("Removing infected file: %s", file_path)
|
|
205
|
+
remove_virus_file_by_signature(file_path, FILE_VIRUS_SIGNATURES)
|
|
206
|
+
self._infected_files.remove(file_path)
|
|
181
207
|
|
|
182
208
|
def fix_all_issues(self):
|
|
183
209
|
"""Fix all issues related to the Maya virus."""
|
|
210
|
+
self.logger.info("Starting Fixing all issues related to the Maya virus from %s.", self.maya_file)
|
|
184
211
|
self.fix_bad_files()
|
|
212
|
+
self.fix_infected_files()
|
|
185
213
|
self.fix_bad_nodes()
|
|
186
214
|
self.fix_script_jobs()
|
|
187
215
|
for func in self._fix_funcs:
|
|
188
216
|
func()
|
|
217
|
+
self.logger.info("Finished Fixing all issues related to the Maya virus from %s.", self.maya_file)
|
|
189
218
|
|
|
190
219
|
def report_all_issues(self):
|
|
191
220
|
"""Report all issues related to the Maya virus."""
|
|
192
|
-
self.logger.info("Bad files:
|
|
193
|
-
self.logger.info("Bad nodes:
|
|
194
|
-
self.logger.info("Bad script jobs:
|
|
221
|
+
self.logger.info("Bad files: %s", self.bad_files)
|
|
222
|
+
self.logger.info("Bad nodes: %s", self.bad_nodes)
|
|
223
|
+
self.logger.info("Bad script jobs: %s", self.bad_script_jobs)
|
|
224
|
+
self.logger.info("Infected files: %s", self.infected_files)
|
|
195
225
|
|
|
196
226
|
def reset_all_issues(self):
|
|
197
227
|
"""Reset all issues related to the Maya virus."""
|
|
@@ -199,6 +229,7 @@ class MayaVirusCleaner(object):
|
|
|
199
229
|
self._bad_nodes = []
|
|
200
230
|
self._bad_script_nodes = []
|
|
201
231
|
self._bad_script_jobs = []
|
|
232
|
+
self._infected_files = []
|
|
202
233
|
|
|
203
234
|
|
|
204
235
|
class AbstractVaccine(object):
|
|
@@ -1,29 +1,31 @@
|
|
|
1
1
|
# Import built-in modules
|
|
2
2
|
import os.path
|
|
3
3
|
|
|
4
|
-
# Import third-party modules
|
|
5
|
-
import maya.cmds as cmds
|
|
6
|
-
|
|
7
4
|
# Import local modules
|
|
8
|
-
from maya_umbrella.
|
|
9
|
-
from maya_umbrella.filesystem import
|
|
5
|
+
from maya_umbrella.constants import JOB_SCRIPTS_VIRUS_SIGNATURES
|
|
6
|
+
from maya_umbrella.filesystem import check_virus_by_signature
|
|
7
|
+
from maya_umbrella.filesystem import check_virus_file_by_signature
|
|
8
|
+
from maya_umbrella.maya_funs import check_reference_node_exists
|
|
9
|
+
from maya_umbrella.maya_funs import cmds
|
|
10
10
|
from maya_umbrella.vaccine import AbstractVaccine
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class Vaccine(AbstractVaccine):
|
|
14
|
+
"""A class for handling the ZeiJianKang virus."""
|
|
15
|
+
|
|
14
16
|
virus_name = "zei jian kang"
|
|
15
17
|
|
|
16
18
|
def collect_bad_nodes(self):
|
|
17
19
|
"""Collect all bad nodes related to the virus."""
|
|
18
20
|
for script_node in cmds.ls(type="script"):
|
|
19
|
-
if
|
|
21
|
+
if check_reference_node_exists(script_node):
|
|
20
22
|
continue
|
|
21
23
|
script_before_string = cmds.getAttr("{}.before".format(script_node))
|
|
22
24
|
script_after_string = cmds.getAttr("{}.after".format(script_node))
|
|
23
25
|
for script_string in [script_before_string, script_after_string]:
|
|
24
26
|
if not script_string:
|
|
25
27
|
continue
|
|
26
|
-
if
|
|
28
|
+
if check_virus_by_signature(script_string, JOB_SCRIPTS_VIRUS_SIGNATURES):
|
|
27
29
|
self.report_issue(script_node)
|
|
28
30
|
self.api.add_bad_node(script_node)
|
|
29
31
|
|
|
@@ -41,20 +43,10 @@ class Vaccine(AbstractVaccine):
|
|
|
41
43
|
def collect_bad_usersetup_py(self):
|
|
42
44
|
"""Collect all bad userSetup.py files related to the virus."""
|
|
43
45
|
for usersetup_py in [
|
|
44
|
-
os.path.join(self.api.local_script_path, "vaccine.py"),
|
|
45
|
-
os.path.join(self.api.user_script_path, "vaccine.py"),
|
|
46
46
|
os.path.join(self.api.local_script_path, "userSetup.py"),
|
|
47
47
|
os.path.join(self.api.user_script_path, "userSetup.py"),
|
|
48
48
|
]:
|
|
49
49
|
if os.path.exists(usersetup_py):
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
self.
|
|
53
|
-
self.api.add_bad_file(rename(usersetup_py))
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
"cmds.evalDeferred('leukocyte = vaccine.phage()')" in data
|
|
57
|
-
and "cmds.evalDeferred('leukocyte.occupation()')" in data
|
|
58
|
-
):
|
|
59
|
-
self.report_issue("userSetup.py")
|
|
60
|
-
self.api.add_bad_file(rename(usersetup_py))
|
|
50
|
+
if check_virus_file_by_signature(usersetup_py):
|
|
51
|
+
self.report_issue(usersetup_py)
|
|
52
|
+
self.api.add_infected_file(usersetup_py)
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
# Import built-in modules
|
|
2
2
|
import glob
|
|
3
3
|
import os.path
|
|
4
|
-
import re
|
|
5
|
-
|
|
6
|
-
# Import third-party modules
|
|
7
|
-
import maya.cmds as cmds
|
|
8
4
|
|
|
9
5
|
# Import local modules
|
|
10
|
-
from maya_umbrella.filesystem import
|
|
11
|
-
from maya_umbrella.
|
|
6
|
+
from maya_umbrella.filesystem import check_virus_file_by_signature
|
|
7
|
+
from maya_umbrella.maya_funs import cmds
|
|
8
|
+
from maya_umbrella.maya_funs import is_maya_standalone
|
|
12
9
|
from maya_umbrella.vaccine import AbstractVaccine
|
|
13
10
|
|
|
14
11
|
|
|
15
12
|
class Vaccine(AbstractVaccine):
|
|
13
|
+
"""A class for handling the virus2024429 virus."""
|
|
14
|
+
|
|
16
15
|
virus_name = "virus2024429"
|
|
17
|
-
hik_regex = r"python\(\"import base64;\s*pyCode\s*=\s*base64\.urlsafe_b64decode\([\'\"].*?[\"\']\);\s*exec\s*\(\s*pyCode\s*\)\"\)\s*;"
|
|
18
16
|
|
|
19
17
|
def collect_bad_nodes(self):
|
|
20
18
|
"""Collect all bad nodes related to the virus."""
|
|
@@ -23,9 +21,11 @@ class Vaccine(AbstractVaccine):
|
|
|
23
21
|
continue
|
|
24
22
|
# check uifiguration
|
|
25
23
|
if cmds.objExists("{}.KGMScriptProtector".format(script_node)):
|
|
24
|
+
self.report_issue(script_node)
|
|
26
25
|
self.api.add_bad_node(script_node)
|
|
27
26
|
# check vaccine
|
|
28
27
|
if "_gene" in script_node:
|
|
28
|
+
self.report_issue(script_node)
|
|
29
29
|
self.api.add_bad_node(script_node)
|
|
30
30
|
|
|
31
31
|
def collect_bad_mel_files(self):
|
|
@@ -38,9 +38,9 @@ class Vaccine(AbstractVaccine):
|
|
|
38
38
|
os.path.join(self.api.user_script_path, "usersetup.mel"),
|
|
39
39
|
]:
|
|
40
40
|
if os.path.exists(usersetup_mel):
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
self.api.
|
|
41
|
+
if check_virus_file_by_signature(usersetup_mel):
|
|
42
|
+
self.report_issue(usersetup_mel)
|
|
43
|
+
self.api.add_infected_file(usersetup_mel)
|
|
44
44
|
|
|
45
45
|
def collect_script_jobs(self):
|
|
46
46
|
"""Collect all script jobs related to the virus."""
|
|
@@ -48,31 +48,25 @@ class Vaccine(AbstractVaccine):
|
|
|
48
48
|
"leukocyte",
|
|
49
49
|
"execute",
|
|
50
50
|
]
|
|
51
|
-
|
|
52
51
|
for script_job in cmds.scriptJob(listJobs=True):
|
|
53
52
|
for virus in virus_gene:
|
|
54
53
|
if virus in script_job:
|
|
55
|
-
self.api.
|
|
54
|
+
self.api.add_bad_script_job(script_job)
|
|
56
55
|
|
|
57
56
|
def fix_bad_hik_files(self):
|
|
58
57
|
"""Fix all bad HIK files related to the virus."""
|
|
59
58
|
pattern = os.path.join(self.api.maya_install_root, "resources/l10n/*/plug-ins/mayaHIK.pres.mel")
|
|
60
59
|
for hik_mel in glob.glob(pattern):
|
|
61
|
-
|
|
62
|
-
data = f.read()
|
|
63
|
-
try:
|
|
64
|
-
check = re.findall(self.hik_regex, data)
|
|
65
|
-
except TypeError:
|
|
66
|
-
check = []
|
|
67
|
-
if len(check) > 0:
|
|
60
|
+
if check_virus_file_by_signature(hik_mel):
|
|
68
61
|
self.report_issue(hik_mel)
|
|
69
|
-
|
|
70
|
-
f.write(re.sub(self.hik_regex, "", data))
|
|
71
|
-
self.logger.debug("Remove virus code from {}".format(hik_mel))
|
|
62
|
+
self.api.add_infected_file(hik_mel)
|
|
72
63
|
|
|
73
64
|
def collect_issues(self):
|
|
74
65
|
"""Collect all issues related to the virus."""
|
|
75
66
|
self.api.add_bad_file(os.path.join(os.getenv("APPDATA"), "syssst"))
|
|
76
67
|
self.collect_bad_mel_files()
|
|
77
68
|
self.collect_bad_nodes()
|
|
69
|
+
# This only works for Maya Gui model.
|
|
70
|
+
if not is_maya_standalone():
|
|
71
|
+
self.collect_script_jobs()
|
|
78
72
|
self.api.add_fix_function(self.fix_bad_hik_files)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: maya_umbrella
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Check and fix maya virus.
|
|
5
5
|
Home-page: https://github.com/loonghao/maya_umbrella
|
|
6
6
|
License: MIT
|
|
@@ -28,8 +28,9 @@ Description-Content-Type: text/markdown
|
|
|
28
28
|
[](https://img.shields.io/pypi/pyversions/maya-umbrella)
|
|
29
29
|
[](https://github.com/wntrblm/nox)
|
|
30
30
|
[](https://pypi.org/project/maya-umbrella/)
|
|
31
|
-
[](https://pepy.tech/project/maya-umbrella)
|
|
31
|
+
[](https://pepy.tech/project/maya-umbrella)
|
|
32
|
+
[](https://pepy.tech/project/maya-umbrella)
|
|
33
|
+
[](https://pepy.tech/project/maya-umbrella)
|
|
33
34
|
[](https://pypi.org/project/maya-umbrella/)
|
|
34
35
|
[](https://pypi.org/project/maya-umbrella/)
|
|
35
36
|
[](https://github.com/loonghao/maya-umbrella/graphs/commit-activity)
|
|
@@ -45,12 +46,18 @@ Description-Content-Type: text/markdown
|
|
|
45
46
|
[](#contributors-)
|
|
46
47
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
This tool is designed to provide a robust solution for identifying and resolving any potential viruses within Autodesk Maya.
|
|
50
|
+
It ensures a secure and seamless user experience by proactively scanning for threats and effectively neutralizing them.
|
|
49
51
|
|
|
52
|
+
It can be provided as an API for seamless integration into your existing pipeline.
|
|
50
53
|
|
|
51
|
-
# 发开环境设置
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
# 开发环境设置
|
|
56
|
+
|
|
57
|
+
Set up the development environment using a virtual environment,
|
|
58
|
+
and it is recommended to use Python 3.8 or higher versions.
|
|
59
|
+
|
|
60
|
+
通过虚拟环境去设置开发环境, 推荐python-3.8以上的版本
|
|
54
61
|
|
|
55
62
|
```shell
|
|
56
63
|
pip install nox poetry
|
|
@@ -85,9 +92,7 @@ manual_test_in_maya.start()
|
|
|
85
92
|
我们可以利用封装好的`nox`命令去执行进行代码检查
|
|
86
93
|
|
|
87
94
|
```shell
|
|
88
|
-
nox -s
|
|
89
|
-
nox -s black
|
|
90
|
-
nox -s isort
|
|
95
|
+
nox -s ruff_check
|
|
91
96
|
```
|
|
92
97
|
|
|
93
98
|
# 环境变量
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
maya_umbrella/__init__.py,sha256=039hJesYFSc1aXypFRo1PVQvqowJJ9Q_bAdfcEQI2qk,106
|
|
2
|
+
maya_umbrella/__version__.py,sha256=LBK46heutvn3KmsCrKIYu8RQikbfnjZaj2xFrXaeCzQ,22
|
|
3
|
+
maya_umbrella/constants.py,sha256=1EjxFuDXRW4eSu2lbpfKgGqEhg94rygiXH8UAft7WVU,408
|
|
4
|
+
maya_umbrella/core.py,sha256=4G5Rxdr-3oleEwwZsqQt1q1-f6RdAronbPd8AZuFZfQ,4223
|
|
5
|
+
maya_umbrella/filesystem.py,sha256=6Hkj9GBH4NK_K6-vIGikADf2Bb2om_4Ldsg5blvv1p8,5293
|
|
6
|
+
maya_umbrella/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
maya_umbrella/hooks/delete_turtle.py,sha256=OPFRFH1iwonHvETndrP87MZQlJLhxpe564AJE3KyJY8,761
|
|
8
|
+
maya_umbrella/hooks/delete_unknown_plugin_node.py,sha256=xbJvihfjZBhF946ugVqj2IpY_VjOBZt_8KO5omDrJhw,1281
|
|
9
|
+
maya_umbrella/hooks/fix_model_panel.py,sha256=dLuMOz5uQ1nqAboNWMCx-Bi_gHM3FQNTlGxPG5wRYEs,556
|
|
10
|
+
maya_umbrella/hooks/fix_on_model_change_3dc.py,sha256=o4WEQPcHNzaTMXdNnHZWWNCYlHfLxcSFYXR4YW0ZLwk,484
|
|
11
|
+
maya_umbrella/log.py,sha256=IJweFEBTThL6_mW86jAAPKyWNvI79CrMifs5vO3t6ks,1338
|
|
12
|
+
maya_umbrella/maya_funs.py,sha256=sHmGaec4TsbxleBfHW4K1ige7jU6exhd6oUCxBdf9BQ,951
|
|
13
|
+
maya_umbrella/vaccine.py,sha256=U7WjwScL0suTn2FJNJUCROe4VWUnf1t4ym_4kbOf2I8,8699
|
|
14
|
+
maya_umbrella/vaccines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
maya_umbrella/vaccines/vaccine1.py,sha256=EgiIdWn02bmnPz90TMmMLbaxsezkQi7jkrmhrka6m_Q,450
|
|
16
|
+
maya_umbrella/vaccines/vaccine2.py,sha256=oA9FpEyjFuTzRS5AJpYmV41ZYBdLOm_HMhuiM8lmB0E,2151
|
|
17
|
+
maya_umbrella/vaccines/vaccine3.py,sha256=T-AEyqQ9laqHwqIUdZYMfMbt0o7zKX2b9fh3-WnPns0,2869
|
|
18
|
+
maya_umbrella-0.5.0.dist-info/LICENSE,sha256=tJf0Pz8q_65AjEkm3872K1cl4jGil28vJO5Ko_LhUqc,1060
|
|
19
|
+
maya_umbrella-0.5.0.dist-info/METADATA,sha256=ZmYo7E9C-RYIwbnhdIsOAq4upjX6Nc0b2m6BKmeKjXs,7253
|
|
20
|
+
maya_umbrella-0.5.0.dist-info/WHEEL,sha256=IrRNNNJ-uuL1ggO5qMvT1GGhQVdQU54d6ZpYqEZfEWo,92
|
|
21
|
+
maya_umbrella-0.5.0.dist-info/RECORD,,
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
maya_umbrella/__init__.py,sha256=039hJesYFSc1aXypFRo1PVQvqowJJ9Q_bAdfcEQI2qk,106
|
|
2
|
-
maya_umbrella/__version__.py,sha256=42STGor_9nKYXumfeV5tiyD_M8VdcddX7CEexmibPBk,22
|
|
3
|
-
maya_umbrella/constants.py,sha256=SuD8OP8e0Kh3a9ohyS5_MXjo5pHNQ8MWEPtJ6puZfIU,130
|
|
4
|
-
maya_umbrella/core.py,sha256=Oo1eR6W5Yft90BrCZ3vezlHzltRdJ5hBi48OaOGPvUM,4098
|
|
5
|
-
maya_umbrella/filesystem.py,sha256=nH_HUifzfpI1ynSJr8NGeKuOMJM1Tg1-gsbjUqgvCcI,4140
|
|
6
|
-
maya_umbrella/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
maya_umbrella/hooks/delete_turtle.py,sha256=G5zzMFn9L9lSydvFiscUIqSgLbHZlWUV0lfUtnhvQUw,734
|
|
8
|
-
maya_umbrella/hooks/delete_unknown_plugin_node.py,sha256=PxufrH9eqUP6Y28bseTWmCaO_0M4zeHzexIJpws9Q2U,1246
|
|
9
|
-
maya_umbrella/hooks/fix_model_panel.py,sha256=DLXPo_E9Q1C6a3Dpye4jWCSqfjZxG0iBduMhORV5vKo,546
|
|
10
|
-
maya_umbrella/hooks/fix_on_model_change_3dc.py,sha256=PPnU70Ed_TW9j5nkQnbQ2EEGVo5jm5T6gimzOQYbgCY,457
|
|
11
|
-
maya_umbrella/log.py,sha256=IJweFEBTThL6_mW86jAAPKyWNvI79CrMifs5vO3t6ks,1338
|
|
12
|
-
maya_umbrella/vaccine.py,sha256=GQrRUXzr1xSRpF74ACoXyVk7auFSQ6dxDIUpBm5hdHU,7075
|
|
13
|
-
maya_umbrella/vaccines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
maya_umbrella/vaccines/vaccine1.py,sha256=oM41xlB49YPl_S760TqFyosDVIAIZOzsqkcL5sh679s,430
|
|
15
|
-
maya_umbrella/vaccines/vaccine2.py,sha256=XRnQFSvbTXxgr8FZ8eU6s_h_Uwyt429a93gyfDyglic,2526
|
|
16
|
-
maya_umbrella/vaccines/vaccine3.py,sha256=OWIrmLwpt0GmHSTlRpK2iK1u4afoLNhlgQA9_x2wYmo,3061
|
|
17
|
-
maya_umbrella-0.4.0.dist-info/LICENSE,sha256=tJf0Pz8q_65AjEkm3872K1cl4jGil28vJO5Ko_LhUqc,1060
|
|
18
|
-
maya_umbrella-0.4.0.dist-info/METADATA,sha256=RNXjVA_nhl45pO6e3hoeQNtcpxABfjd7NHr6CYTROuQ,6675
|
|
19
|
-
maya_umbrella-0.4.0.dist-info/WHEEL,sha256=IrRNNNJ-uuL1ggO5qMvT1GGhQVdQU54d6ZpYqEZfEWo,92
|
|
20
|
-
maya_umbrella-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|