Skip to content

Commit efb53ed

Browse files
REFACTOR: Updates related to vulnerabilities and documentation (#6110)
Co-authored-by: pyansys-ci-bot <[email protected]>
1 parent 900a27d commit efb53ed

File tree

15 files changed

+138
-41
lines changed

15 files changed

+138
-41
lines changed

doc/changelog.d/6110.miscellaneous.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Updates related to vulnerabilities and documentation

doc/source/User_guide/security_consideration.rst

+20
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. _ref_security_consideration:
2+
13
Security considerations
24
=======================
35

@@ -6,6 +8,24 @@ of PyAEDT. It is important to understand the capabilities which PyAEDT
68
provides, especially when using it to build applications or scripts that
79
accept untrusted input.
810

11+
If a function displays a warning that redirects to this page, it indicates
12+
that the function may expose security risks when used improperly.
13+
In such cases, it is essential to pay close attention to:
14+
15+
- **Function arguments**: Ensure that arguments passed to the function are
16+
properly validated and do not contain untrusted content such as arbitrary
17+
file paths, shell commands, or serialized data.
18+
- **Environment variables**: Be cautious of environment variables that can
19+
influence the behavior of the function, particularly if they are user-defined
20+
or inherited from an untrusted execution context.
21+
- **Global settings (`settings`)**: PyAEDT settings control various aspects of
22+
runtime behavior such as AEDT features, use of LSF cluster or remote server
23+
connections. Review these settings to avoid unexpected side effects or security
24+
vulnerabilities.
25+
26+
Always validate external input, avoid executing arbitrary commands or code,
27+
and follow the principle of least privilege when developing with PyAEDT.
28+
929
.. _security_launch_aedt:
1030

1131
Launching AEDT

src/ansys/aedt/core/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ def custom_show_warning(message, category, filename, lineno, file=None, line=Non
8484
if not (".NETFramework" in sys.version): # pragma: no cover
8585
import ansys.aedt.core.examples.downloads as downloads
8686

87-
from ansys.aedt.core.edb import Edb # nosec
88-
from ansys.aedt.core.edb import Siwave # nosec
87+
from ansys.aedt.core.edb import Edb
88+
from ansys.aedt.core.edb import Siwave
8989
from ansys.aedt.core.generic import constants
9090
import ansys.aedt.core.generic.data_handlers as data_handler
9191
from ansys.aedt.core.generic.design_types import Circuit

src/ansys/aedt/core/application/analysis.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import os
3333
import re
3434
import shutil
35-
import subprocess # nosec
3635
import tempfile
3736
import time
3837
from typing import Dict
@@ -1921,9 +1920,10 @@ def solve_in_batch(
19211920
To use this function, the project must be closed.
19221921
19231922
.. warning::
1924-
Do not execute this function with untrusted input parameters.
1925-
See the :ref:`security guide<https://aedt.docs.pyansys.com/version/stable/User_guide/security_consideration.html>`
1926-
for details.
1923+
1924+
Do not execute this function with untrusted function argument, environment
1925+
variables or pyaedt global settings.
1926+
See the :ref:`security guide<ref_security_consideration>` for details.
19271927
19281928
Parameters
19291929
----------
@@ -1951,6 +1951,8 @@ def solve_in_batch(
19511951
bool
19521952
``True`` when successful, ``False`` when failed.
19531953
"""
1954+
import subprocess # nosec
1955+
19541956
try:
19551957
cores = int(cores)
19561958
except ValueError:

src/ansys/aedt/core/desktop.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ def launch_aedt(
9292
9393
.. warning::
9494
95-
Do not execute this function with untrusted input parameters.
95+
Do not execute this function with untrusted function argument, environment
96+
variables or pyaedt global settings.
9697
See the :ref:`security guide<security_launch_aedt>` for details.
9798
"""
9899

@@ -1803,7 +1804,8 @@ def get_ansyscloud_job_info(self, job_id=None, job_name=None): # pragma: no cov
18031804
18041805
.. warning::
18051806
1806-
Do not execute this function with untrusted environment variables.
1807+
Do not execute this function with untrusted function argument, environment
1808+
variables or pyaedt global settings.
18071809
See the :ref:`security guide<security_ansys_cloud>` for details.
18081810
18091811
Parameters
@@ -1923,7 +1925,8 @@ def get_available_cloud_config(self, region="westeurope"): # pragma: no cover
19231925
19241926
.. warning::
19251927
1926-
Do not execute this function with untrusted environment variables.
1928+
Do not execute this function with untrusted function argument, environment
1929+
variables or pyaedt global settings.
19271930
See the :ref:`security guide<security_ansys_cloud>` for details.
19281931
19291932
Parameters

src/ansys/aedt/core/examples/downloads.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ def _download_file(
100100
if not local_path.exists():
101101
ssl_context = ssl.create_default_context()
102102
pyaedt_logger.debug(f"Downloading file from URL {url}")
103-
with urllib.request.urlopen(url, context=ssl_context) as response, open(
103+
with urllib.request.urlopen(url, context=ssl_context) as response, open( # nosec
104104
local_path, "wb"
105-
) as out_file: # nosec
105+
) as out_file:
106106
shutil.copyfileobj(response, out_file)
107107
else:
108108
pyaedt_logger.debug(f"File already exists in {local_path}. Skipping download.")

src/ansys/aedt/core/extensions/customize_automation_tab.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,15 @@ def __tab_map(product): # pragma: no cover
357357

358358

359359
def run_command(command: List[str], desktop_object): # pragma: no cover
360-
"""Run a command through subprocess."""
360+
"""Run a command through subprocess.
361+
362+
.. warning::
363+
364+
Do not execute this function with untrusted function argument, environment
365+
variables or pyaedt global settings.
366+
See the :ref:`security guide<ref_security_consideration>` for details.
367+
368+
"""
361369
try:
362370
subprocess.run(command, check=True, capture_output=True, text=True) # nosec
363371
except subprocess.CalledProcessError as e:
@@ -370,6 +378,12 @@ def run_command(command: List[str], desktop_object): # pragma: no cover
370378
def add_custom_toolkit(desktop_object, toolkit_name, wheel_toolkit=None, install=True): # pragma: no cover
371379
"""Add toolkit to AEDT Automation Tab.
372380
381+
.. warning::
382+
383+
Do not execute this function with untrusted function argument, environment
384+
variables or pyaedt global settings.
385+
See the :ref:`security guide<ref_security_consideration>` for details.
386+
373387
Parameters
374388
----------
375389
desktop_object : :class:ansys.aedt.core.desktop.Desktop

src/ansys/aedt/core/extensions/project/create_report.py

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ def main(extension_args):
8282
if is_windows and not extension_args["is_test"]: # pragma: no cover
8383
try:
8484
if os.path.isfile(pdf_path) and pdf_path.endswith(".pdf"):
85+
# FIXME: Should be replaced with a cross-platform solution ? Like webbrowser.open(pdf_path)
8586
os.startfile(pdf_path) # nosec
8687
except Exception:
8788
aedtapp.logger.warning(f"Failed to open {pdf_path}")

src/ansys/aedt/core/generic/file_utils.py

+14-6
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from ansys.aedt.core.generic.numbers import Quantity
4444
from ansys.aedt.core.generic.settings import settings
4545
from ansys.aedt.core.internal.aedt_versions import aedt_versions
46+
from ansys.aedt.core.internal.errors import AEDTRuntimeError
4647

4748
is_linux = os.name == "posix"
4849
is_windows = not is_linux
@@ -918,6 +919,13 @@ def available_license_feature(
918919
feature: str = "electronics_desktop", input_dir: Union[str, Path] = None, port: int = 1055, name: str = "127.0.0.1"
919920
) -> int: # pragma: no cover
920921
"""Check available license feature.
922+
923+
.. warning::
924+
925+
Do not execute this function with untrusted function argument, environment
926+
variables or pyaedt global settings.
927+
See the :ref:`security guide<ref_security_consideration>` for details.
928+
921929
The method retrieves the port and name values from the ``ANSYSLMD_LICENSE_FILE`` environment variable if available.
922930
If not, the default values are applied.
923931
@@ -939,7 +947,7 @@ def available_license_feature(
939947
int
940948
Number of available license features, ``False`` when license server is down.
941949
"""
942-
import subprocess # nosec B404
950+
import subprocess # nosec
943951

944952
if os.getenv("ANSYSLMD_LICENSE_FILE", None):
945953
name_env = os.getenv("ANSYSLMD_LICENSE_FILE")
@@ -972,11 +980,11 @@ def available_license_feature(
972980

973981
cmd = [str(ansysli_util_path), "lmstat", "-f", feature, "-c", str(port) + "@" + str(name)]
974982

975-
f = open(str(tempfile_checkout), "w")
976-
977-
subprocess.Popen(cmd, stdout=f, stderr=f, env=my_env).wait() # nosec
978-
979-
f.close()
983+
try:
984+
with tempfile_checkout.open("w") as f:
985+
subprocess.run(cmd, stdout=f, stderr=f, env=my_env, check=True) # nosec
986+
except Exception as e:
987+
raise AEDTRuntimeError("Failed to check available licenses") from e
980988

981989
available_licenses = 0
982990
pattern_license = r"Total of\s+(\d+)\s+licenses? issued;\s+Total of\s+(\d+)\s+licenses? in use"

src/ansys/aedt/core/generic/general_methods.py

+14-7
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from ansys.aedt.core.generic.numbers import _units_assignment
4242
from ansys.aedt.core.generic.settings import inner_project_settings # noqa: F401
4343
from ansys.aedt.core.generic.settings import settings
44+
from ansys.aedt.core.internal.errors import AEDTRuntimeError
4445
from ansys.aedt.core.internal.errors import GrpcApiError
4546
from ansys.aedt.core.internal.errors import MethodNotSupportedError
4647
import psutil
@@ -998,6 +999,11 @@ def install_with_pip(package_name, package_path=None, upgrade=False, uninstall=F
998999
9991000
This method is useful for installing a package from the AEDT Console without launching the Python environment.
10001001
1002+
.. warning::
1003+
1004+
Do not execute this function with untrusted environment variables.
1005+
See the :ref:`security guide<ref_security_consideration>` for details.
1006+
10011007
Parameters
10021008
----------
10031009
package_name : str
@@ -1011,8 +1017,10 @@ def install_with_pip(package_name, package_path=None, upgrade=False, uninstall=F
10111017
"""
10121018
import subprocess # nosec B404
10131019

1014-
executable = f'"{sys.executable}"' if is_windows else sys.executable
1020+
if not package_name or not isinstance(package_name, str):
1021+
raise ValueError("A valid package name must be provided.")
10151022

1023+
executable = sys.executable
10161024
commands = []
10171025
if uninstall:
10181026
commands.append([executable, "-m", "pip", "uninstall", "--yes", package_name])
@@ -1024,14 +1032,13 @@ def install_with_pip(package_name, package_path=None, upgrade=False, uninstall=F
10241032
command = [executable, "-m", "pip", "install", package_name]
10251033
if upgrade:
10261034
command.append("-U")
1027-
10281035
commands.append(command)
1036+
10291037
for command in commands:
1030-
if is_linux:
1031-
p = subprocess.Popen(command)
1032-
else:
1033-
p = subprocess.Popen(" ".join(command))
1034-
p.wait()
1038+
try:
1039+
subprocess.run(command, check=True) # nosec
1040+
except subprocess.CalledProcessError as e: # nosec
1041+
raise AEDTRuntimeError("An error occurred while installing with pip") from e
10351042

10361043

10371044
class Help: # pragma: no cover

src/ansys/aedt/core/icepak.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import os
2929
from pathlib import Path
3030
import re
31-
import subprocess # nosec
3231
import warnings
3332

3433
from ansys.aedt.core.application.analysis_icepak import FieldAnalysisIcepak
@@ -2865,6 +2864,12 @@ def generate_fluent_mesh(
28652864
): # pragma: no cover
28662865
"""Generate a Fluent mesh for a list of selected objects and assign the mesh automatically to the objects.
28672866
2867+
.. warning::
2868+
2869+
Do not execute this function with untrusted function argument, environment
2870+
variables or pyaedt global settings.
2871+
See the :ref:`security guide<ref_security_consideration>` for details.
2872+
28682873
Parameters
28692874
----------
28702875
object_lists : list, optional
@@ -2887,6 +2892,8 @@ def generate_fluent_mesh(
28872892
-------
28882893
:class:`ansys.aedt.core.modules.mesh.MeshOperation`
28892894
"""
2895+
import subprocess # nosec
2896+
28902897
version = self.aedt_version_id[-3:]
28912898
ansys_install_dir = os.environ.get(f"ANSYS{version}_DIR", "")
28922899
if not ansys_install_dir:

src/ansys/aedt/core/modeler/cad/elements_3d.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1366,8 +1366,8 @@ def _update_children(self):
13661366
self._children[i] = BinaryTreeNode(
13671367
i, self.child_object.GetChildObject(i), root_name=self._saved_root_name, app=self._app
13681368
)
1369-
except Exception: # nosec
1370-
pass
1369+
except Exception:
1370+
settings.logger.debug(f"Failed to instantiate BinaryTreeNode for {i}")
13711371
else:
13721372
names = self.child_object.GetChildObject(i).GetChildNames()
13731373
for name in names:

src/ansys/aedt/core/rpc/rpyc_services.py

+18
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,12 @@ def _beta(self):
264264
def exposed_run_script(self, script, aedt_version="2021.2", ansysem_path=None, non_graphical=True):
265265
"""Run script on AEDT in the server.
266266
267+
.. warning::
268+
269+
Do not execute this function with untrusted function argument, environment
270+
variables or pyaedt global settings.
271+
See the :ref:`security guide<ref_security_consideration>` for details.
272+
267273
Parameters
268274
----------
269275
script : str or list
@@ -893,6 +899,12 @@ def exposed_restore(self):
893899
def aedt_grpc(port=None, beta_options: List[str]=None, use_aedt_relative_path=False, non_graphical=True, check_interval=2):
894900
"""Start a new AEDT session on a specified gRPC port.
895901
902+
.. warning::
903+
904+
Do not execute this function with untrusted function argument, environment
905+
variables or pyaedt global settings.
906+
See the :ref:`security guide<ref_security_consideration>` for details.
907+
896908
Returns
897909
-------
898910
port : int
@@ -1129,6 +1141,12 @@ def on_disconnect(self, connection):
11291141
def start_service(self, port):
11301142
"""Connect to remove service manager and run a new server on specified port.
11311143
1144+
.. warning::
1145+
1146+
Do not execute this function with untrusted function argument, environment
1147+
variables or pyaedt global settings.
1148+
See the :ref:`security guide<ref_security_consideration>` for details.
1149+
11321150
Parameters
11331151
----------
11341152
aedt_client_port : int

src/ansys/aedt/core/visualization/advanced/touchstone_parser.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import itertools
2727
import os
2828
import re
29-
import subprocess # nosec
3029
import warnings
3130

3231
from ansys.aedt.core import Edb
@@ -597,6 +596,12 @@ def read_touchstone(input_file):
597596
def check_touchstone_files(input_dir="", passivity=True, causality=True):
598597
"""Check passivity and causality for all Touchstone files included in the folder.
599598
599+
.. warning::
600+
601+
Do not execute this function with untrusted function argument, environment
602+
variables or pyaedt global settings.
603+
See the :ref:`security guide<ref_security_consideration>` for details.
604+
600605
Parameters
601606
----------
602607
input_dir : str
@@ -615,6 +620,8 @@ def check_touchstone_files(input_dir="", passivity=True, causality=True):
615620
is a string with the log information.
616621
617622
"""
623+
import subprocess # nosec
624+
618625
out = {}
619626
snp_files = find_touchstone_files(input_dir)
620627
if not snp_files:

0 commit comments

Comments
 (0)