修复37项安全漏洞和Bug
高危修复: - app.py: 添加ip_rate_limit_lock线程锁保护IP限流字典 - app.py: 添加validate_ip_port()验证代理IP/端口范围 - database.py: SQL字段名白名单验证防止注入 - playwright_automation.py: 改进浏览器进程强制清理逻辑 中危修复: - database.py: 统一时区处理函数get_cst_now() - database.py: 消除循环导入,移动app_security导入到顶部 - playwright_automation.py: 所有bare except改为except Exception - app_config.py: dotenv导入失败警告+安全配置检查 - db_pool.py: 添加详细异常堆栈日志 - app_security.py: 用户名过滤零宽字符 - database.py: delete_old_task_logs分批删除避免锁表 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -227,7 +227,8 @@ class PlaywrightAutomation:
|
||||
if 'index.aspx' in current_url:
|
||||
return True
|
||||
return False
|
||||
except:
|
||||
except Exception:
|
||||
# Bug fix: 明确捕获Exception而非所有异常
|
||||
return False
|
||||
|
||||
def quick_login(self, username: str, password: str, remember: bool = True):
|
||||
@@ -258,8 +259,8 @@ class PlaywrightAutomation:
|
||||
self.browser.close()
|
||||
if self.playwright:
|
||||
self.playwright.stop()
|
||||
except:
|
||||
pass
|
||||
except Exception:
|
||||
pass # 清理时忽略错误
|
||||
|
||||
# 正常登录
|
||||
result = self.login(username, password, remember)
|
||||
@@ -359,8 +360,8 @@ class PlaywrightAutomation:
|
||||
error_type = "password_error"
|
||||
else:
|
||||
error_type = "login_error"
|
||||
except:
|
||||
pass
|
||||
except Exception:
|
||||
pass # 获取错误提示失败时忽略
|
||||
|
||||
# 如果没有明确的错误提示,可能是网络问题,不认为是密码错误
|
||||
if error_type == "unknown":
|
||||
@@ -476,11 +477,11 @@ class PlaywrightAutomation:
|
||||
time.sleep(1.5)
|
||||
try:
|
||||
self.main_page.wait_for_load_state('domcontentloaded', timeout=5000)
|
||||
except:
|
||||
except Exception: # Bug fix: 明确捕获Exception
|
||||
pass
|
||||
try:
|
||||
self.main_page.wait_for_load_state('networkidle', timeout=10000)
|
||||
except:
|
||||
except Exception: # Bug fix: 明确捕获Exception
|
||||
pass
|
||||
|
||||
self.page = self.get_iframe_safe(retry=True, max_retries=3)
|
||||
@@ -529,10 +530,10 @@ class PlaywrightAutomation:
|
||||
# 等待表格加载
|
||||
try:
|
||||
self.page.locator("//table[@class='ltable']").wait_for(timeout=10000)
|
||||
except:
|
||||
except Exception: # Bug fix: 明确捕获Exception
|
||||
pass
|
||||
self.log(f"✓ iframe恢复成功(刷新后重新点击'{browse_type}')")
|
||||
except:
|
||||
except Exception: # Bug fix: 明确捕获Exception
|
||||
# 尝试点击label
|
||||
try:
|
||||
label_selector = f"//label[contains(text(), '{browse_type}')]"
|
||||
@@ -777,7 +778,7 @@ class PlaywrightAutomation:
|
||||
# 等待表格加载完成(最多等待10秒)
|
||||
try:
|
||||
self.page.locator("//table[@class='ltable']").wait_for(timeout=10000)
|
||||
except:
|
||||
except Exception: # Bug fix: 明确捕获Exception
|
||||
self.log("等待表格超时,继续尝试...")
|
||||
|
||||
# 额外等待,确保AJAX内容加载完成
|
||||
@@ -912,7 +913,7 @@ class PlaywrightAutomation:
|
||||
if match:
|
||||
expected_total = int(match.group(1))
|
||||
self.log(f"[总数] 预期浏览 {expected_total} 条内容")
|
||||
except:
|
||||
except Exception: # Bug fix: 明确捕获Exception
|
||||
pass
|
||||
|
||||
# 处理每一行 (每次从头重新获取所有行)
|
||||
@@ -1026,7 +1027,7 @@ class PlaywrightAutomation:
|
||||
try:
|
||||
current_rows_locator = self.page.locator("//table[@class='ltable']/tbody/tr[position()>1 and count(td)>=5]")
|
||||
row = current_rows_locator.nth(i)
|
||||
except:
|
||||
except Exception: # Bug fix: 明确捕获Exception
|
||||
break
|
||||
else:
|
||||
break
|
||||
@@ -1085,7 +1086,7 @@ class PlaywrightAutomation:
|
||||
# 关闭新窗口
|
||||
try:
|
||||
new_page.close()
|
||||
except:
|
||||
except Exception: # Bug fix: 明确捕获Exception
|
||||
pass
|
||||
self.log(f" - 新窗口已关闭")
|
||||
else:
|
||||
@@ -1134,7 +1135,7 @@ class PlaywrightAutomation:
|
||||
self.page = self.get_iframe_safe()
|
||||
if not self.page:
|
||||
self.recover_iframe(browse_type)
|
||||
except:
|
||||
except Exception: # Bug fix: 明确捕获Exception
|
||||
pass
|
||||
|
||||
# 处理完当前页后,检查是否需要翻页
|
||||
@@ -1212,7 +1213,7 @@ class PlaywrightAutomation:
|
||||
try:
|
||||
self.page.locator("//table[@class='ltable']").wait_for(timeout=30000)
|
||||
self.log("内容表格已加载")
|
||||
except:
|
||||
except Exception: # Bug fix: 明确捕获Exception
|
||||
self.log("等待表格加载超时,继续...")
|
||||
except Exception as e:
|
||||
if self.is_context_error(str(e)):
|
||||
@@ -1388,26 +1389,68 @@ class PlaywrightAutomation:
|
||||
# else部分日志已精简
|
||||
|
||||
def _cleanup_on_exit(self):
|
||||
"""进程退出时的清理函数(由atexit调用)"""
|
||||
# Bug #13 fix: 尝试获取锁,但不阻塞(避免退出时死锁)
|
||||
"""进程退出时的清理函数(由atexit调用)
|
||||
|
||||
Bug fix: 改进清理逻辑,即使锁获取失败也尝试清理资源
|
||||
"""
|
||||
# 尝试获取锁,但不阻塞(避免退出时死锁)
|
||||
acquired = self._lock.acquire(blocking=False)
|
||||
try:
|
||||
if not self._closed:
|
||||
try:
|
||||
# 静默关闭,避免在退出时产生过多日志
|
||||
if self.context:
|
||||
self.context.close()
|
||||
if self.browser:
|
||||
self.browser.close()
|
||||
if self.playwright:
|
||||
self.playwright.stop()
|
||||
self._closed = True
|
||||
except:
|
||||
pass # 退出时忽略所有错误
|
||||
self._force_cleanup()
|
||||
finally:
|
||||
if acquired:
|
||||
self._lock.release()
|
||||
|
||||
def _force_cleanup(self):
|
||||
"""强制清理资源(不依赖锁状态)
|
||||
|
||||
Bug fix: 添加进程级清理,确保浏览器进程被终止
|
||||
"""
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# 记录浏览器进程ID用于强制清理
|
||||
browser_pid = None
|
||||
try:
|
||||
if self.browser and hasattr(self.browser, '_impl_obj'):
|
||||
# 尝试获取浏览器进程ID
|
||||
impl = self.browser._impl_obj
|
||||
if hasattr(impl, '_browser_process') and impl._browser_process:
|
||||
browser_pid = impl._browser_process.pid
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 尝试正常关闭
|
||||
try:
|
||||
if self.context:
|
||||
self.context.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
if self.browser:
|
||||
self.browser.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
if self.playwright:
|
||||
self.playwright.stop()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 如果有浏览器进程ID且在Linux/Mac上,强制杀死进程
|
||||
if browser_pid and sys.platform != 'win32':
|
||||
try:
|
||||
import os
|
||||
import signal
|
||||
os.kill(browser_pid, signal.SIGKILL)
|
||||
except (ProcessLookupError, PermissionError, OSError):
|
||||
pass # 进程可能已经退出
|
||||
|
||||
self._closed = True
|
||||
|
||||
def __enter__(self):
|
||||
"""Context manager支持 - 进入"""
|
||||
return self
|
||||
|
||||
Reference in New Issue
Block a user