Skip to content

Commit 89c9de6

Browse files
authored
Merge pull request #103 from Integration-Automation/dev
fix: resolve Codacy issues across XML parsing, imports, and security
2 parents 9e33088 + 10fefc5 commit 89c9de6

15 files changed

Lines changed: 68 additions & 39 deletions

File tree

.codacy.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
engines:
3+
bandit:
4+
enabled: true
5+
exclude_paths:
6+
- 'test/**'
7+
- 'tests/**'
8+
prospector:
9+
enabled: true
10+
pylint:
11+
enabled: true
12+
pmd:
13+
enabled: false
14+
exclude_paths:
15+
- '.venv/**'
16+
- 'build/**'
17+
- 'dist/**'
18+
- 'docs/build/**'

dev.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Rename to dev version
22
# This is dev version
33
[build-system]
4-
requires = ["setuptools>=61.0"]
4+
requires = ["setuptools>=82.0.1"]
55
build-backend = "setuptools.build_meta"
66

77
[project]
@@ -38,4 +38,4 @@ content-type = "text/markdown"
3838
find = { namespaces = false }
3939

4040
[project.optional-dependencies]
41-
gui = ["PySide6==6.10.0", "qt-material"]
41+
gui = ["PySide6==6.11.0", "qt-material"]

dev_requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
je_load_density_dev
2+
defusedxml>=0.7.1
23
sphinx
34
twine
45
sphinx-rtd-theme

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
# -- Project information -----------------------------------------------------
1313
project = 'LoadDensity'
14-
copyright = '2022, JE-Chen'
14+
project_copyright = '2022, JE-Chen'
1515
author = 'JE-Chen'
1616

1717
# The full version, including alpha/beta/rc tags

je_load_density/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# hook
2-
from je_load_density.wrapper.event.request_hook import request_hook
1+
# hook (side-effect import: registers Locust request hooks)
2+
from je_load_density.wrapper.event.request_hook import request_hook # noqa: F401
33
# env
44
from je_load_density.utils.executor.action_executor import add_command_to_executor
55
# executor

je_load_density/gui/main_widget.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import logging
2-
import queue
32
from typing import Optional
43

54
from PySide6.QtCore import QTimer

je_load_density/utils/generate_report/generate_xml_report.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sys
22
from threading import Lock
3-
from xml.dom.minidom import parseString
3+
from defusedxml.minidom import parseString
44
from typing import Tuple
55

66
from je_load_density.utils.generate_report.generate_json_report import generate_json

je_load_density/utils/package_manager/package_manager_class.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import re
12
from importlib import import_module
23
from importlib.util import find_spec
34
from inspect import getmembers, isfunction
45
from sys import stderr
56
from typing import Optional, Any
67

8+
_VALID_PACKAGE_NAME = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$")
9+
710

811
class PackageManager:
912
"""
@@ -28,15 +31,17 @@ def load_package_if_available(self, package: str) -> Optional[Any]:
2831
:return: 套件模組或 None (Loaded module or None)
2932
"""
3033
if package not in self.installed_package_dict:
34+
if not _VALID_PACKAGE_NAME.fullmatch(package):
35+
print(f"Rejected invalid package name: {package!r}", file=stderr)
36+
return None
3137
found_spec = find_spec(package)
32-
if found_spec is not None:
33-
try:
34-
installed_package = import_module(found_spec.name)
35-
self.installed_package_dict[found_spec.name] = installed_package
36-
except ModuleNotFoundError as error:
37-
print(repr(error), file=stderr)
38-
return None
39-
else:
38+
if found_spec is None or not _VALID_PACKAGE_NAME.fullmatch(found_spec.name):
39+
return None
40+
try:
41+
installed_package = import_module(found_spec.name) # nosemgrep: python.lang.security.audit.non-literal-import.non-literal-import
42+
self.installed_package_dict[found_spec.name] = installed_package
43+
except ModuleNotFoundError as error:
44+
print(repr(error), file=stderr)
4045
return None
4146
return self.installed_package_dict.get(package)
4247

je_load_density/utils/xml/change_xml_structure/change_xml_structure.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from collections import defaultdict
2-
from xml.etree import ElementTree
3-
from typing import Union, Dict, Any
2+
from typing import Any, Dict
3+
from xml.etree import ElementTree as _ElementTreeBuilder # nosec B405 - construction only, no parsing # nosemgrep: python.lang.security.use-defused-xml.use-defused-xml
44

55

6-
def elements_tree_to_dict(elements_tree: ElementTree.Element) -> Dict[str, Any]:
6+
def elements_tree_to_dict(elements_tree: _ElementTreeBuilder.Element) -> Dict[str, Any]:
77
"""
88
將 XML ElementTree 轉換為字典
99
Convert XML ElementTree to dictionary
@@ -52,7 +52,7 @@ def dict_to_elements_tree(json_dict: Dict[str, Any]) -> str:
5252
:return: XML 字串 (XML string)
5353
"""
5454

55-
def _to_elements_tree(json_dict: Any, root: ElementTree.Element) -> None:
55+
def _to_elements_tree(json_dict: Any, root: _ElementTreeBuilder.Element) -> None:
5656
if isinstance(json_dict, str):
5757
root.text = json_dict
5858
elif isinstance(json_dict, dict):
@@ -67,16 +67,16 @@ def _to_elements_tree(json_dict: Any, root: ElementTree.Element) -> None:
6767
root.set(key[1:], value)
6868
elif isinstance(value, list): # 處理子節點清單
6969
for element in value:
70-
_to_elements_tree(element, ElementTree.SubElement(root, key))
70+
_to_elements_tree(element, _ElementTreeBuilder.SubElement(root, key))
7171
else: # 處理單一子節點
72-
_to_elements_tree(value, ElementTree.SubElement(root, key))
72+
_to_elements_tree(value, _ElementTreeBuilder.SubElement(root, key))
7373
else:
7474
raise TypeError(f"Invalid type in dict_to_elements_tree: {type(json_dict)}")
7575

7676
if not isinstance(json_dict, dict) or len(json_dict) != 1:
7777
raise ValueError("Input must be a dictionary with a single root element")
7878

7979
tag, body = next(iter(json_dict.items()))
80-
node = ElementTree.Element(tag)
80+
node = _ElementTreeBuilder.Element(tag)
8181
_to_elements_tree(body, node)
82-
return ElementTree.tostring(node, encoding="utf-8").decode("utf-8")
82+
return _ElementTreeBuilder.tostring(node, encoding="utf-8").decode("utf-8")

je_load_density/utils/xml/xml_file/xml_file.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import xml.dom.minidom
2-
from xml.etree import ElementTree
3-
from xml.etree.ElementTree import ParseError
41
from typing import Optional
2+
from xml.etree import ElementTree as _SafeElementTree # nosec B405 - used only for typing/wrapping; parsing routes through defusedxml # nosemgrep: python.lang.security.use-defused-xml.use-defused-xml
3+
from xml.etree.ElementTree import ParseError # nosec B405 # nosemgrep: python.lang.security.use-defused-xml.use-defused-xml
4+
5+
import defusedxml.ElementTree as ElementTree
6+
from defusedxml.minidom import parseString as _parse_xml_string
57

68
from je_load_density.utils.exception.exception_tags import cant_read_xml_error, xml_type_error
79
from je_load_density.utils.exception.exceptions import XMLException, XMLTypeException
@@ -15,7 +17,7 @@ def reformat_xml_file(xml_string: str) -> str:
1517
:param xml_string: 原始 XML 字串 (Raw XML string)
1618
:return: 格式化後的 XML 字串 (Pretty-printed XML string)
1719
"""
18-
dom = xml.dom.minidom.parseString(xml_string)
20+
dom = _parse_xml_string(xml_string)
1921
return dom.toprettyxml(indent=" ")
2022

2123

@@ -36,8 +38,8 @@ def __init__(self, xml_string: str, xml_type: str = "string") -> None:
3638
:param xml_string: XML 字串或檔案路徑 (XML string or file path)
3739
:param xml_type: "string" 或 "file" (Parse from string or file)
3840
"""
39-
self.tree: Optional[ElementTree.ElementTree] = None
40-
self.xml_root: Optional[ElementTree.Element] = None
41+
self.tree: Optional[_SafeElementTree.ElementTree] = None
42+
self.xml_root: Optional[_SafeElementTree.Element] = None
4143
self.xml_from_type: str = "string"
4244
self.xml_string: str = xml_string.strip()
4345

@@ -50,7 +52,7 @@ def __init__(self, xml_string: str, xml_type: str = "string") -> None:
5052
else:
5153
self.xml_parser_from_file()
5254

53-
def xml_parser_from_string(self, **kwargs) -> ElementTree.Element:
55+
def xml_parser_from_string(self, **kwargs) -> _SafeElementTree.Element:
5456
"""
5557
從字串解析 XML
5658
Parse XML from string
@@ -64,7 +66,7 @@ def xml_parser_from_string(self, **kwargs) -> ElementTree.Element:
6466
raise XMLException(f"{cant_read_xml_error}: {error}")
6567
return self.xml_root
6668

67-
def xml_parser_from_file(self, **kwargs) -> ElementTree.Element:
69+
def xml_parser_from_file(self, **kwargs) -> _SafeElementTree.Element:
6870
"""
6971
從檔案解析 XML
7072
Parse XML from file
@@ -90,7 +92,7 @@ def write_xml(self, write_xml_filename: str, write_content: str) -> None:
9092
"""
9193
try:
9294
content = ElementTree.fromstring(write_content.strip())
93-
tree = ElementTree.ElementTree(content)
95+
tree = _SafeElementTree.ElementTree(content)
9496
tree.write(write_xml_filename, encoding="utf-8", xml_declaration=True)
9597
except ParseError as error:
9698
raise XMLException(f"{cant_read_xml_error}: {error}")

0 commit comments

Comments
 (0)