compliance/examples/rule_execution_demo.py
2025-05-16 15:18:02 +08:00

755 lines
24 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
规则执行演示脚本
本脚本演示如何使用增强后的规则库执行API测试包括性能规则、安全规则、RESTful设计规则和错误处理规则的执行。
脚本模拟了一系列API请求和响应场景并使用规则执行引擎验证这些场景是否符合预定义的规则。
"""
import time
import json
import logging
import argparse
from dataclasses import dataclass, asdict
from typing import Dict, List, Any, Optional
import requests
from urllib.parse import urlparse
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# ------------------ 模拟 API 调用相关类 ------------------
@dataclass
class APIRequest:
"""API请求类用于构建和发送API请求"""
method: str
url: str
headers: Optional[Dict[str, str]] = None
params: Optional[Dict[str, Any]] = None
json_data: Optional[Any] = None
body: Optional[Any] = None
data: Optional[Dict[str, Any]] = None
@dataclass
class APIResponse:
"""API响应类用于存储API响应结果"""
status_code: int
headers: Dict[str, str]
content: str
elapsed_time: float
json_content: Optional[Dict[str, Any]] = None
request: Optional[APIRequest] = None
def __post_init__(self):
if self.content and (self.headers.get('Content-Type', '').startswith('application/json') or
self.content.strip().startswith('{')):
try:
self.json_content = json.loads(self.content)
except json.JSONDecodeError:
self.json_content = None
class APICaller:
"""API调用器用于发送API请求并获取响应"""
def call_api(self, request: APIRequest) -> APIResponse:
"""模拟调用API返回响应结果"""
logger.info(f"发送 {request.method} 请求到 {request.url}")
# 这里只是模拟实际项目中应使用真实的HTTP请求
# 根据URL创建不同的模拟响应
start_time = time.time()
# 模拟不同的API响应场景
if "users" in request.url:
response = self._mock_user_api_response(request)
elif "slow" in request.url:
# 模拟慢响应
time.sleep(0.6) # 模拟600ms响应时间
response = self._mock_slow_api_response(request)
elif "error" in request.url:
# 模拟错误响应
response = self._mock_error_api_response(request)
elif "http://" in request.url:
# 模拟HTTP响应非HTTPS
response = self._mock_http_api_response(request)
else:
# 默认响应
response = self._mock_default_api_response(request)
elapsed_time = time.time() - start_time
response.elapsed_time = elapsed_time
response.request = request
return response
def _mock_user_api_response(self, request: APIRequest) -> APIResponse:
"""模拟用户API响应"""
headers = {
'Content-Type': 'application/json',
'X-Request-ID': '12345',
'X-Rate-Limit-Remaining': '1000'
}
content = json.dumps({
"id": 123,
"name": "Test User",
"email": "user@example.com",
"created_at": "2023-01-01T00:00:00Z",
"status": "active"
})
return APIResponse(
status_code=200,
headers=headers,
content=content,
elapsed_time=0.1
)
def _mock_slow_api_response(self, request: APIRequest) -> APIResponse:
"""模拟慢响应API"""
headers = {
'Content-Type': 'application/json',
'X-Request-ID': '54321',
'X-Rate-Limit-Remaining': '999'
}
content = json.dumps({
"id": 456,
"name": "Test User",
"email": "slow@example.com",
"created_at": "2023-01-02T00:00:00Z",
"status": "active",
"data": [1, 2, 3, 4, 5] * 100 # 添加一些数据使响应大一些
})
return APIResponse(
status_code=200,
headers=headers,
content=content,
elapsed_time=0.6 # 模拟600ms响应时间
)
def _mock_error_api_response(self, request: APIRequest) -> APIResponse:
"""模拟错误响应API"""
headers = {
'Content-Type': 'application/json',
'X-Request-ID': '67890'
}
# 标准错误响应格式
content = json.dumps({
"code": "user_not_found",
"message": "User not found",
"details": {
"resource": "User",
"id": request.params.get("id", "unknown") if request.params else "unknown"
}
})
return APIResponse(
status_code=404,
headers=headers,
content=content,
elapsed_time=0.05
)
def _mock_http_api_response(self, request: APIRequest) -> APIResponse:
"""模拟HTTP非HTTPSAPI响应"""
headers = {
'Content-Type': 'application/json',
'X-Request-ID': '13579'
}
content = json.dumps({
"message": "This is an insecure HTTP response",
"timestamp": time.time()
})
return APIResponse(
status_code=200,
headers=headers,
content=content,
elapsed_time=0.08
)
def _mock_default_api_response(self, request: APIRequest) -> APIResponse:
"""模拟默认API响应"""
headers = {
'Content-Type': 'application/json',
'X-Request-ID': '24680'
}
content = json.dumps({
"message": "Default response",
"method": request.method,
"path": urlparse(request.url).path,
"timestamp": time.time()
})
return APIResponse(
status_code=200,
headers=headers,
content=content,
elapsed_time=0.03
)
# ------------------ 规则模型类 ------------------
@dataclass
class RuleResult:
"""规则执行结果类"""
rule_id: str
rule_name: str
is_valid: bool
message: str
details: Optional[Dict[str, Any]] = None
category: Optional[str] = None
severity: Optional[str] = None
class RuleCategory:
"""规则类别枚举"""
PERFORMANCE = "Performance"
SECURITY = "Security"
API_DESIGN = "APIDesign"
ERROR_HANDLING = "ErrorHandling"
GENERAL = "General"
class RuleLifecycle:
"""规则生命周期枚举"""
REQUEST_PREPARATION = "RequestPreparation"
REQUEST_EXECUTION = "RequestExecution"
RESPONSE_VALIDATION = "ResponseValidation"
POST_VALIDATION = "PostValidation"
ANY_STAGE = "AnyStage"
class RuleScope:
"""规则作用域枚举"""
REQUEST_URL = "RequestURL"
REQUEST_HEADERS = "RequestHeaders"
REQUEST_PARAMS = "RequestParams"
REQUEST_BODY = "RequestBody"
RESPONSE_STATUS = "ResponseStatus"
RESPONSE_HEADERS = "ResponseHeaders"
RESPONSE_BODY = "ResponseBody"
RESPONSE_TIME = "ResponseTime"
SECURITY = "Security"
PERFORMANCE = "Performance"
ANY_SCOPE = "AnyScope"
class TargetType:
"""规则目标类型枚举"""
API_REQUEST = "APIRequest"
API_RESPONSE = "APIResponse"
ANY = "Any"
@dataclass
class BaseRule:
"""基础规则类"""
id: str
name: str
description: str
category: str = RuleCategory.GENERAL
version: str = "1.0.0"
severity: str = "warning"
is_enabled: bool = True
tags: List[str] = None
target_type: str = TargetType.ANY
lifecycle: str = RuleLifecycle.ANY_STAGE
scope: str = RuleScope.ANY_SCOPE
def __post_init__(self):
if self.tags is None:
self.tags = []
def to_dict(self) -> Dict[str, Any]:
"""将规则转换为字典"""
return asdict(self)
def validate(self, context: Dict[str, Any]) -> Dict[str, Any]:
"""验证逻辑,由子类实现"""
raise NotImplementedError("子类必须实现validate方法")
@dataclass
class PerformanceRule(BaseRule):
"""性能规则类"""
category: str = RuleCategory.PERFORMANCE
target_type: str = TargetType.API_RESPONSE
lifecycle: str = RuleLifecycle.RESPONSE_VALIDATION
scope: str = RuleScope.RESPONSE_TIME
threshold: float = 500.0 # 默认阈值500毫秒
metric: str = "response_time"
unit: str = "ms"
def validate(self, context: Dict[str, Any]) -> Dict[str, Any]:
"""验证API响应时间是否超过阈值"""
response = context.get('api_response')
if not response:
return {
'is_valid': False,
'message': '缺少API响应对象',
'details': {}
}
response_time = response.elapsed_time * 1000 # 转换为毫秒
if response_time > self.threshold:
return {
'is_valid': False,
'message': f'响应时间 {response_time:.2f}ms 超过阈值 {self.threshold}ms',
'details': {
'actual_time': response_time,
'threshold': self.threshold,
'unit': self.unit
}
}
return {
'is_valid': True,
'message': f'响应时间 {response_time:.2f}ms 在阈值 {self.threshold}ms 内',
'details': {
'actual_time': response_time,
'threshold': self.threshold,
'unit': self.unit
}
}
@dataclass
class SecurityRule(BaseRule):
"""安全规则类"""
category: str = RuleCategory.SECURITY
target_type: str = TargetType.API_REQUEST
lifecycle: str = RuleLifecycle.REQUEST_PREPARATION
scope: str = RuleScope.SECURITY
check_type: str = "transport_security" # 传输安全检查
expected_value: str = "https" # 期望值为https
def validate(self, context: Dict[str, Any]) -> Dict[str, Any]:
"""验证API请求是否使用了HTTPS协议"""
request = context.get('api_request')
if not request:
return {
'is_valid': False,
'message': '缺少API请求对象',
'details': {}
}
url = str(request.url)
if not url.startswith('https://'):
return {
'is_valid': False,
'message': 'API请求必须使用HTTPS协议',
'details': {
'current_url': url,
'expected_protocol': 'https'
}
}
return {
'is_valid': True,
'message': 'API请求使用了HTTPS协议',
'details': {
'url': url
}
}
@dataclass
class RESTfulDesignRule(BaseRule):
"""RESTful设计规则类"""
category: str = RuleCategory.API_DESIGN
target_type: str = TargetType.API_REQUEST
lifecycle: str = RuleLifecycle.REQUEST_PREPARATION
scope: str = RuleScope.REQUEST_URL
design_aspect: str = "URL设计"
pattern: str = r"^/api/v\d+/[a-z0-9-]+(/[a-z0-9-]+)*$"
def validate(self, context: Dict[str, Any]) -> Dict[str, Any]:
"""验证API URL是否符合RESTful设计规范"""
import re
request = context.get('api_request')
if not request:
return {
'is_valid': False,
'message': '缺少API请求对象',
'details': {}
}
url = str(request.url)
# 解析URL获取路径部分
parsed_url = urlparse(url)
path = parsed_url.path
# 使用正则表达式验证路径
if not re.match(self.pattern, path):
return {
'is_valid': False,
'message': 'API URL不符合RESTful设计规范',
'details': {
'current_path': path,
'expected_pattern': self.pattern,
'suggestion': '路径应该遵循 /api/v{version}/{资源}[/{id}] 格式'
}
}
return {
'is_valid': True,
'message': 'API URL符合RESTful设计规范',
'details': {
'path': path
}
}
@dataclass
class ErrorHandlingRule(BaseRule):
"""错误处理规则类"""
category: str = RuleCategory.ERROR_HANDLING
target_type: str = TargetType.API_RESPONSE
lifecycle: str = RuleLifecycle.RESPONSE_VALIDATION
scope: str = RuleScope.RESPONSE_BODY
error_code: str = "*" # 匹配所有错误码
expected_status: int = -1 # 不验证状态码
def validate(self, context: Dict[str, Any]) -> Dict[str, Any]:
"""验证API错误响应是否符合标准格式"""
response = context.get('api_response')
if not response:
return {
'is_valid': False,
'message': '缺少API响应对象',
'details': {}
}
# 只检查4xx和5xx状态码的响应
if response.status_code < 400:
return {
'is_valid': True,
'message': '非错误响应,跳过验证',
'details': {
'status_code': response.status_code
}
}
# 确保响应包含JSON内容
if not response.json_content:
return {
'is_valid': False,
'message': '错误响应不是有效的JSON格式',
'details': {
'status_code': response.status_code,
'content_type': response.headers.get('Content-Type', '未知')
}
}
# 检查错误响应的必要字段
required_fields = ['code', 'message']
missing_fields = [field for field in required_fields if field not in response.json_content]
if missing_fields:
return {
'is_valid': False,
'message': '错误响应缺少必要字段',
'details': {
'missing_fields': missing_fields,
'required_fields': required_fields,
'response': response.json_content
}
}
return {
'is_valid': True,
'message': '错误响应符合标准格式',
'details': {
'status_code': response.status_code,
'error_code': response.json_content.get('code'),
'error_message': response.json_content.get('message')
}
}
# ------------------ 规则执行引擎 ------------------
class RuleExecutor:
"""规则执行引擎"""
def __init__(self, rules: List[BaseRule] = None):
self.rules = rules or []
def add_rule(self, rule: BaseRule):
"""添加规则"""
self.rules.append(rule)
def execute_rule(self, rule: BaseRule, context: Dict[str, Any]) -> RuleResult:
"""执行单个规则"""
logger.info(f"执行规则 '{rule.name}' (ID: {rule.id})")
try:
result = rule.validate(context)
# 构建规则执行结果
rule_result = RuleResult(
rule_id=rule.id,
rule_name=rule.name,
is_valid=result.get('is_valid', False),
message=result.get('message', ''),
details=result.get('details', {}),
category=rule.category,
severity=rule.severity
)
# 记录执行结果
log_level = logging.INFO if rule_result.is_valid else logging.WARNING
logger.log(log_level, f"规则 '{rule.name}' 结果: {'通过' if rule_result.is_valid else '失败'} - {rule_result.message}")
return rule_result
except Exception as e:
# 记录执行异常
logger.error(f"执行规则 '{rule.name}' 时发生异常: {str(e)}", exc_info=True)
# 返回异常结果
return RuleResult(
rule_id=rule.id,
rule_name=rule.name,
is_valid=False,
message=f"规则执行异常: {str(e)}",
details={'exception': str(e)},
category=rule.category,
severity=rule.severity
)
def execute_rules_for_lifecycle(self, lifecycle: str, context: Dict[str, Any]) -> List[RuleResult]:
"""执行特定生命周期阶段的所有规则"""
logger.info(f"执行 {lifecycle} 阶段的规则")
# 筛选符合生命周期的规则
lifecycle_rules = [rule for rule in self.rules if rule.lifecycle == lifecycle or rule.lifecycle == RuleLifecycle.ANY_STAGE]
# 执行筛选出的规则
results = []
for rule in lifecycle_rules:
result = self.execute_rule(rule, context)
results.append(result)
# 统计执行结果
passed = sum(1 for result in results if result.is_valid)
failed = len(results) - passed
logger.info(f"{lifecycle} 阶段规则执行完成: {passed} 条通过, {failed} 条失败")
return results
def execute_all_rules(self, context: Dict[str, Any]) -> List[RuleResult]:
"""执行所有规则"""
logger.info(f"执行所有规则")
results = []
for rule in self.rules:
result = self.execute_rule(rule, context)
results.append(result)
# 统计执行结果
passed = sum(1 for result in results if result.is_valid)
failed = len(results) - passed
logger.info(f"所有规则执行完成: {passed} 条通过, {failed} 条失败")
return results
# ------------------ 演示函数 ------------------
def create_demo_rules() -> List[BaseRule]:
"""创建演示用的规则"""
rules = []
# 添加性能规则
performance_rule = PerformanceRule(
id="response-time-max-500ms",
name="响应时间不超过500毫秒",
description="验证API响应时间不超过500毫秒",
threshold=500,
metric="response_time",
unit="ms"
)
rules.append(performance_rule)
# 添加安全规则
security_rule = SecurityRule(
id="https-only-rule",
name="HTTPS强制使用规则",
description="验证API是否只使用HTTPS协议",
severity="error",
check_type="transport_security",
expected_value="https"
)
rules.append(security_rule)
# 添加RESTful设计规则
restful_rule = RESTfulDesignRule(
id="restful-url-pattern",
name="RESTful URL设计规则",
description="验证API URL是否符合RESTful设计规范",
pattern=r"^/api/v\d+/[a-z0-9-]+(/[a-z0-9-]+)*$"
)
rules.append(restful_rule)
# 添加错误处理规则
error_rule = ErrorHandlingRule(
id="standard-error-response",
name="标准错误响应格式规则",
description="验证API错误响应是否符合标准格式",
error_code="*"
)
rules.append(error_rule)
return rules
def demo_rule_execution():
"""演示规则执行过程"""
logger.info("开始规则执行演示")
# 创建规则
rules = create_demo_rules()
# 创建规则执行引擎
executor = RuleExecutor(rules)
# 创建API调用器
api_caller = APICaller()
# === 场景1: 正常响应场景 ===
logger.info("\n=== 场景1: 正常响应场景 ===")
# 创建API请求
request1 = APIRequest(
method="GET",
url="https://api.example.com/api/v1/users/123",
headers={"Content-Type": "application/json"}
)
# 执行请求准备阶段的规则
context1 = {"api_request": request1}
prep_results1 = executor.execute_rules_for_lifecycle(RuleLifecycle.REQUEST_PREPARATION, context1)
# 发送API请求
response1 = api_caller.call_api(request1)
# 执行响应验证阶段的规则
context1["api_response"] = response1
resp_results1 = executor.execute_rules_for_lifecycle(RuleLifecycle.RESPONSE_VALIDATION, context1)
# === 场景2: 慢响应场景 ===
logger.info("\n=== 场景2: 慢响应场景 ===")
# 创建API请求
request2 = APIRequest(
method="GET",
url="https://api.example.com/api/v1/users/slow",
headers={"Content-Type": "application/json"}
)
# 执行请求准备阶段的规则
context2 = {"api_request": request2}
prep_results2 = executor.execute_rules_for_lifecycle(RuleLifecycle.REQUEST_PREPARATION, context2)
# 发送API请求
response2 = api_caller.call_api(request2)
# 执行响应验证阶段的规则
context2["api_response"] = response2
resp_results2 = executor.execute_rules_for_lifecycle(RuleLifecycle.RESPONSE_VALIDATION, context2)
# === 场景3: 错误响应场景 ===
logger.info("\n=== 场景3: 错误响应场景 ===")
# 创建API请求
request3 = APIRequest(
method="GET",
url="https://api.example.com/api/v1/users/error",
headers={"Content-Type": "application/json"},
params={"id": "999"}
)
# 执行请求准备阶段的规则
context3 = {"api_request": request3}
prep_results3 = executor.execute_rules_for_lifecycle(RuleLifecycle.REQUEST_PREPARATION, context3)
# 发送API请求
response3 = api_caller.call_api(request3)
# 执行响应验证阶段的规则
context3["api_response"] = response3
resp_results3 = executor.execute_rules_for_lifecycle(RuleLifecycle.RESPONSE_VALIDATION, context3)
# === 场景4: 非HTTPS场景 ===
logger.info("\n=== 场景4: 非HTTPS场景 ===")
# 创建API请求
request4 = APIRequest(
method="GET",
url="http://api.example.com/api/v1/users/123",
headers={"Content-Type": "application/json"}
)
# 执行请求准备阶段的规则
context4 = {"api_request": request4}
prep_results4 = executor.execute_rules_for_lifecycle(RuleLifecycle.REQUEST_PREPARATION, context4)
# 发送API请求
response4 = api_caller.call_api(request4)
# 执行响应验证阶段的规则
context4["api_response"] = response4
resp_results4 = executor.execute_rules_for_lifecycle(RuleLifecycle.RESPONSE_VALIDATION, context4)
# === 场景5: 不符合RESTful设计的URL ===
logger.info("\n=== 场景5: 不符合RESTful设计的URL ===")
# 创建API请求
request5 = APIRequest(
method="GET",
url="https://api.example.com/getUserInfo?id=123",
headers={"Content-Type": "application/json"}
)
# 执行请求准备阶段的规则
context5 = {"api_request": request5}
prep_results5 = executor.execute_rules_for_lifecycle(RuleLifecycle.REQUEST_PREPARATION, context5)
# 发送API请求
response5 = api_caller.call_api(request5)
# 执行响应验证阶段的规则
context5["api_response"] = response5
resp_results5 = executor.execute_rules_for_lifecycle(RuleLifecycle.RESPONSE_VALIDATION, context5)
logger.info("\n演示完成")
def main():
"""主函数"""
parser = argparse.ArgumentParser(description='规则执行演示脚本')
parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'], default='INFO',
help='日志级别')
args = parser.parse_args()
# 设置日志级别
logging.getLogger().setLevel(getattr(logging, args.log_level))
# 执行演示
demo_rule_execution()
if __name__ == "__main__":
main()