Skip to content

Commit 318ba5b

Browse files
committed
REFACTOR: Rpyc services to remove vulnerabilities
1 parent 8d94ca8 commit 318ba5b

File tree

2 files changed

+57
-13
lines changed

2 files changed

+57
-13
lines changed

pyaedt/misc/misc.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,21 @@ def current_student_version():
9999
if "SV" in version_key:
100100
return version_key
101101
return ""
102+
103+
104+
def is_safe_path(path, allowed_extensions=None):
105+
"""Validate if a path is safe to use."""
106+
# Ensure path is an existing file or directory
107+
if not os.path.exists(path) or not os.path.isfile(path):
108+
return False
109+
110+
# Restrict to allowed file extensions:
111+
if allowed_extensions:
112+
if not any(path.endswith(extension) for extension in allowed_extensions):
113+
return False
114+
115+
# Ensure path does not contain dangerous characters
116+
if any(char in path for char in (";", "|", "&", "$", "<", ">", "`")):
117+
return False
118+
119+
return True

pyaedt/rpc/rpyc_services.py

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
from pyaedt.generic.general_methods import is_ironpython
1515
from pyaedt.generic.settings import is_linux
1616
from pyaedt import is_windows
17+
from pyaedt.misc.misc import is_safe_path
1718

1819
if is_linux and is_ironpython:
19-
import subprocessdotnet as subprocess
20+
import subprocessdotnet as subprocess # nosec
2021
else:
21-
import subprocess
22+
import subprocess # nosec
2223

2324
if not is_ironpython:
2425
import rpyc
@@ -270,24 +271,37 @@ def exposed_run_script(self, script, aedt_version="2021.2", ansysem_path=None, n
270271
elif os.path.exists(script):
271272
script_file = script
272273
else:
273-
return "File wrong or wrong commands."
274+
return "Wrong file or wrong commands."
275+
if not is_safe_path(script_file):
276+
return "Script file {} not safe.".format(script_file)
274277
executable = "ansysedt.exe"
275278
if is_linux and not ansysem_path and not env_path(aedt_version):
276279
ansysem_path = os.getenv("PYAEDT_SERVER_AEDT_PATH", "")
277280
if env_path(aedt_version) or ansysem_path:
278281
if not ansysem_path:
279282
ansysem_path = env_path(aedt_version)
280-
281-
ng_feature = " -features=SF159726_SCRIPTOBJECT"
283+
exe_path = os.path.join(ansysem_path, executable)
284+
if not is_safe_path(exe_path):
285+
return "Ansys EM path not safe."
286+
command = [exe_path]
287+
if non_graphical:
288+
command.append("-ng")
289+
ng_feature = "-features=SF159726_SCRIPTOBJECT"
282290
if self._beta_options:
283291
for opt in range(self._beta_options.__len__()):
284292
if self._beta_options[opt] not in ng_feature:
285293
ng_feature += "," + self._beta_options[opt]
286294
if non_graphical:
287-
ng_feature += ",SF6694_NON_GRAPHICAL_COMMAND_EXECUTION -ng"
288-
command = os.path.join(ansysem_path, executable) + ng_feature + " -RunScriptAndExit " + script_file
289-
p = subprocess.Popen(command)
290-
p.wait()
295+
ng_feature += ",SF6694_NON_GRAPHICAL_COMMAND_EXECUTION"
296+
command.append(ng_feature)
297+
command = [exe_path, ng_feature, "-RunScriptAndExit", script_file]
298+
try:
299+
p = subprocess.Popen(command) # nosec
300+
p.wait()
301+
except subprocess.CalledProcessError as e:
302+
msg = "Command failed with error: {}".format(e)
303+
logger.error(msg)
304+
return msg
291305
return "Script Executed."
292306

293307
else:
@@ -871,7 +885,13 @@ def aedt_grpc(port=None, beta_options=None, use_aedt_relative_path=False, non_gr
871885
from pyaedt.generic.general_methods import grpc_active_sessions
872886
sessions = grpc_active_sessions()
873887
if not port:
874-
port = check_port(random.randint(18500, 20000))
888+
# TODO: Remove once IronPython is deprecated
889+
if is_ironpython:
890+
port = check_port(random.randint(18500, 20000)) # nosec
891+
else:
892+
import secrets
893+
secure_random = secrets.SystemRandom()
894+
port = check_port(secure_random.randint(18500, 20000))
875895

876896
if port == 0:
877897
print("Error. No ports are available.")
@@ -1097,7 +1117,7 @@ def on_disconnect(self, connection):
10971117
try:
10981118
edb.close_edb()
10991119
except Exception:
1100-
pass
1120+
logger.warning("Error when trying to close EDB.")
11011121

11021122
def start_service(self, port):
11031123
"""Connect to remove service manager and run a new server on specified port.
@@ -1157,5 +1177,11 @@ def exposed_stop_service(self, port):
11571177

11581178
@staticmethod
11591179
def exposed_check_port():
1160-
port_number = random.randint(18500, 20000)
1161-
return check_port(port_number)
1180+
# TODO: Remove once IronPython is deprecated
1181+
if is_ironpython: # nosec
1182+
port = check_port(random.randint(18500, 20000)) # nosec
1183+
else:
1184+
import secrets
1185+
secure_random = secrets.SystemRandom()
1186+
port = check_port(secure_random.randint(18500, 20000))
1187+
return port

0 commit comments

Comments
 (0)