import abc
import inspect
import json
import re
from typing import Dict
class JsonDict(metaclass=abc.ABCMeta):
@staticmethod
def _is_json_dict_in_typing_list(instance, sign_class):
return isinstance(instance, list) and \
isinstance(getattr(sign_class, "__args__"), tuple) and \
len(sign_class.__args__) == 1 and \
issubclass(sign_class.__args__[0], JsonDict)
@classmethod
def from_dict(cls, json_dict: Dict):
sigs = inspect.signature(cls.__init__)
args = []
for arg_name, parameter in sigs.parameters.items():
if arg_name == "self":
continue
value = json_dict.get(arg_name)
if value is None:
args.append(None)
continue
sign_class = parameter.annotation
if isinstance(sign_class, type) and issubclass(sign_class, JsonDict):
value = sign_class.from_dict(value)
elif cls._is_json_dict_in_typing_list(value, sign_class):
value = [sign_class.__args__[0].from_dict(item) for item in value]
args.append(value)
return cls(*args)
@classmethod
def from_json(cls, json_dict_str):
return cls.from_dict(json.loads(json_dict_str))
def to_dict(self):
members = {}
for key, value in vars(self).items():
if key.startswith('__'):
continue
if isinstance(value, JsonDict):
value = value.to_dict()
elif isinstance(value, list):
new_value = []
for item in value:
if isinstance(item, JsonDict):
new_value.append(item.to_dict())
else:
new_value.append(item)
value = new_value
members[key] = value
return members
def to_json(self):
return json.dumps(self.to_dict())
def check_safe_command_arg(arg: str):
"""
校验外部参数是否安全,避免 Shell 注入风险(适用于 shell=True 场景)。
安全规则:
1. 仅允许字母(a-z, A-Z)、数字(0-9)
2. 允许部分安全符号:- _ . / : @ (常见于路径、URL、文件名)
3. 禁止所有 Shell 元字符(; & | > < ' " \ ( ) $ ` * ? [ ] { })
Args:
arg: 待校验的外部参数(字符串类型)
Returns:
bool: True 表示安全,False 表示存在注入风险
"""
unsafe_pattern = r'^[;&\|]+$'
if re.search(unsafe_pattern, str(arg)):
raise RuntimeError("Parameter contains command injection: {}".format(arg))