#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
安全验证模块
提供路径验证、敏感信息检测、文件类型验证等安全功能
"""
import os
import re
from typing import List, Dict, Any, Optional, Set
from pathlib import Path
import logging
# 敏感信息模式
SENSITIVE_PATTERNS = {
'password': re.compile(r'password\s*[:=]\s*["\']?([^"\']+)["\']?', re.IGNORECASE),
'api_key': re.compile(r'api[_-]?key\s*[:=]\s*["\']?([^"\']+)["\']?', re.IGNORECASE),
'token': re.compile(r'token\s*[:=]\s*["\']?([^"\']+)["\']?', re.IGNORECASE),
'secret': re.compile(r'secret\s*[:=]\s*["\']?([^"\']+)["\']?', re.IGNORECASE),
}
# 危险文件扩展名
DANGEROUS_EXTENSIONS = {
'.exe', '.bat', '.cmd', '.com', '.scr', '.pif', '.vbs', '.js', '.jar',
'.app', '.deb', '.rpm', '.dmg', '.pkg', '.msi', '.dll', '.so', '.dylib'
}
class SecurityValidator:
"""安全验证器"""
def __init__(self, config: Optional[Dict[str, Any]] = None):
self.config = config or {}
self.logger = logging.getLogger(__name__)
self.allowed_paths = set(self.config.get('allowed_paths', []))
self.exclude_dirs = set(self.config.get('exclude_dirs', ['.git', '__pycache__', 'node_modules']))
def validate_path(self, path: str) -> Dict[str, Any]:
"""
验证路径安全性
Args:
path: 要验证的路径
Returns:
包含验证结果的字典
"""
try:
path_obj = Path(path).resolve()
# 检查路径是否存在
if not path_obj.exists():
return {
'valid': False,
'error': f'路径不存在: {path}',
'risk_level': 'high'
}
# 检查路径是否在允许范围内
if self.allowed_paths and not any(
str(path_obj).startswith(allowed) for allowed in self.allowed_paths
):
return {
'valid': False,
'error': f'路径不在允许范围内: {path}',
'risk_level': 'high'
}
# 检查是否为符号链接攻击
if path_obj.is_symlink():
return {
'valid': False,
'error': f'检测到符号链接,存在安全风险: {path}',
'risk_level': 'medium'
}
# 检查权限
if not os.access(path_obj, os.R_OK):
return {
'valid': False,
'error': f'路径无读取权限: {path}',
'risk_level': 'low'
}
return {
'valid': True,
'path': str(path_obj),
'risk_level': 'low'
}
except Exception as e:
return {
'valid': False,
'error': f'路径验证异常: {str(e)}',
'risk_level': 'high'
}
def scan_file_content(self, content: str, file_path: str) -> List[Dict[str, Any]]:
"""
扫描文件内容中的敏感信息
Args:
content: 文件内容
file_path: 文件路径
Returns:
发现的敏感信息列表
"""
findings = []
for pattern_name, pattern in SENSITIVE_PATTERNS.items():
matches = pattern.finditer(content)
for match in matches:
# 检查是否为测试数据或示例
matched_value = match.group(1)
if self._is_test_data(matched_value):
continue
findings.append({
'type': pattern_name,
'line': content[:match.start()].count('\n') + 1,
'match': match.group(0),
'file': file_path,
'severity': 'high' if pattern_name in ['password', 'api_key'] else 'medium'
})
return findings
def _is_test_data(self, value: str) -> bool:
"""检查是否为测试数据"""
test_indicators = ['test', 'demo', 'example', 'sample', 'fake', 'mock', 'placeholder']
value_lower = value.lower()
return any(indicator in value_lower for indicator in test_indicators)
def validate_file_type(self, file_path: str) -> Dict[str, Any]:
"""
验证文件类型安全性
Args:
file_path: 文件路径
Returns:
验证结果
"""
path_obj = Path(file_path)
extension = path_obj.suffix.lower()
if extension in DANGEROUS_EXTENSIONS:
return {
'safe': False,
'extension': extension,
'risk_level': 'high',
'message': f'危险文件类型: {extension}'
}
return {
'safe': True,
'extension': extension,
'risk_level': 'low'
}
def validate_directory_structure(self, directory: str) -> Dict[str, Any]:
"""
验证目录结构安全性
Args:
directory: 目录路径
Returns:
验证结果
"""
try:
path_obj = Path(directory)
# 检查目录是否在排除列表中
dir_name = path_obj.name
if dir_name in self.exclude_dirs:
return {
'safe': False,
'reason': f'目录在排除列表中: {dir_name}',
'risk_level': 'low'
}
# 检查目录深度(防止路径遍历攻击)
relative_parts = path_obj.relative_to(Path.cwd()).parts
if len(relative_parts) > 20: # 设置最大深度
return {
'safe': False,
'reason': f'目录过深,可能存在路径遍历风险: {len(relative_parts)}层',
'risk_level': 'medium'
}
return {
'safe': True,
'depth': len(relative_parts),
'risk_level': 'low'
}
except Exception as e:
return {
'safe': False,
'reason': f'目录结构验证异常: {str(e)}',
'risk_level': 'high'
}
def get_security_validator(config: Optional[Dict[str, Any]] = None) -> SecurityValidator:
"""获取安全验证器实例"""
return SecurityValidator(config)
# 便捷函数
def validate_path_safety(path: str, config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""验证路径安全性的便捷函数"""
validator = get_security_validator(config)
return validator.validate_path(path)
def scan_content_security(content: str, file_path: str, config: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]:
"""扫描内容安全性的便捷函数"""
validator = get_security_validator(config)
return validator.scan_file_content(content, file_path)