Python RPA项目安全测试自动化:基于pytest与aqua-security的实战指南
1. 项目概述:当RPA遇上安全测试自动化
如果你正在用RPA(机器人流程自动化)处理那些重复、枯燥但又至关重要的业务流程,比如自动登录系统、填报数据、下载报表,那你一定遇到过这样的困扰:这些自动化流程本身安全吗?它们会不会成为新的攻击入口?比如,RPA机器人使用的账号密码硬编码在脚本里,或者它访问的API接口缺乏鉴权,又或者它处理的数据在传输过程中是明文的。这些问题,单靠功能测试是发现不了的。传统的安全测试往往由专门的团队手动或半自动进行,与快速迭代的自动化开发流程脱节,导致安全漏洞发现晚、修复成本高。
这就是为什么我们需要将安全测试左移,并实现自动化。这个项目要做的,就是把强大的安全测试工具 aqua-security 无缝集成到你的 Python RPA 项目中,并利用 pytest 这个主流的测试框架来驱动整个安全测试流程自动化。简单来说,就是让你在每次RPA脚本开发或回归测试时,都能自动、快速地对代码、依赖、配置乃至运行时行为进行一次“安全体检”,发现问题立即告警,而不是等到上线后酿成事故。
aqua-security 是一个专注于云原生和应用安全的开源工具集,我们这里主要用到它的 trivy 组件。 trivy 是一款简单易用但功能全面的漏洞扫描器,它能扫描容器镜像、文件系统、Git仓库,以及像 Python 这样的编程语言依赖项(如 requirements.txt 或 Pipfile.lock ),找出其中已知的安全漏洞。而 pytest 则提供了灵活的测试组织、运行和报告能力。将它们结合起来,你就能创建可重复、可集成到CI/CD流水线中的安全测试用例。
本指南面向的是已经有一定 Python 和 RPA 开发基础的工程师或测试人员。你将学会如何搭建环境、编写安全测试用例、解析扫描结果,并最终构建一个可靠的安全测试自动化流程。整个过程,我们力求“说人话,做实事”,提供可以直接“抄作业”的代码和配置。
2. 环境准备与工具链搭建
2.1 核心工具安装与验证
工欲善其事,必先利其器。我们的工具链核心是 Python、pytest 和 aqua-security/trivy。
首先,确保你有一个可用的 Python 环境(3.7及以上)。建议使用虚拟环境来隔离项目依赖,避免包冲突。
# 创建并激活虚拟环境(以 venv 为例)
python -m venv venv_rpa_security
# Windows
venv_rpa_security\Scripts\activate
# Linux/macOS
source venv_rpa_security/bin/activate
接下来,安装 pytest。我们还会安装一些常用的插件,让测试更强大。
pip install pytest pytest-html pytest-xdist
pytest-html:用于生成美观的HTML测试报告。pytest-xdist:支持并行运行测试,加快扫描速度。
现在安装最关键的安全扫描工具: trivy 。虽然它有PyPI包( aqua-security-trivia ),但为了获得完整功能和最新漏洞数据库,我强烈建议直接安装其独立二进制文件,这样更稳定,扫描能力也更强。
对于 Linux/macOS:
# 使用官方安装脚本
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# 验证安装
trivy --version
对于 Windows:
- 访问 Trivy 的 GitHub Releases 页面:
https://github.com/aquasecurity/trivy/releases - 下载最新的
trivy_*_Windows-64bit.zip文件。 - 解压,将里面的
trivy.exe文件放到一个目录(例如C:\Tools\)。 - 将该目录添加到系统的 PATH 环境变量中。
- 打开新的命令行窗口,运行
trivy --version验证。
注意 :将 trivy 二进制文件路径加入系统 PATH 是必须的,这样 pytest 才能在任意位置调用它。在 Windows 上,有时需要重启终端或电脑才能使 PATH 更改生效。
验证安装成功后,可以运行一个快速测试,更新漏洞数据库并扫描一个示例:
trivy image --download-db-only # 首次使用建议先更新数据库
trivy fs . # 扫描当前目录文件系统
2.2 项目结构规划
一个清晰的项目结构有助于维护。假设你的 RPA 项目目录如下:
your_rpa_project/
├── rpa_scripts/ # 存放核心RPA业务流程脚本
│ ├── login_automation.py
│ ├── data_extraction.py
│ └── report_generation.py
├── requirements.txt # Python项目依赖声明文件
├── config/ # 配置文件(可能含敏感信息)
│ └── settings.yaml
├── tests/ # 测试目录
│ ├── functional/ # 功能测试
│ └── security/ # **安全测试专属目录** - 这是我们重点关注的
│ ├── conftest.py # pytest共享配置
│ ├── test_dependencies.py
│ ├── test_configs.py
│ └── test_images.py (如果有容器化部署)
└── .github/workflows/ # CI/CD流水线配置(可选)
└── security-scan.yml
我们将把所有的安全测试用例都放在 tests/security/ 目录下。 conftest.py 文件用于存放 pytest 的 fixture,这是实现测试逻辑复用的关键。
2.3 编写第一个安全测试 Fixture
在 tests/security/conftest.py 中,我们先创建一个用于执行 trivy 命令并解析结果的 fixture。这个 fixture 会被多个测试用例共用。
# tests/security/conftest.py
import subprocess
import json
import pytest
from pathlib import Path
PROJECT_ROOT = Path(__file__).parent.parent.parent
@pytest.fixture(scope="session")
def trivy_scan():
"""
一个session级别的fixture,用于执行trivy扫描并返回解析后的结果。
返回一个函数,该函数接受扫描目标(如文件路径)和扫描类型(如‘fs’, ‘config’)作为参数。
"""
def _scan(target: str, scan_type: str = "fs") -> dict:
"""
执行trivy扫描。
:param target: 扫描目标,如文件/目录路径或镜像名。
:param scan_type: 扫描类型,'fs'(文件系统),'config'(配置),'image'(镜像)等。
:return: 解析后的JSON结果字典。
"""
# 构建命令
cmd = ["trivy", scan_type, "--format", "json", "--quiet", target]
try:
# 执行命令,捕获输出
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=True, # 如果trivy命令执行失败(非零退出码),抛出异常
cwd=PROJECT_ROOT # 在项目根目录执行,确保路径正确
)
# 解析JSON输出
return json.loads(result.stdout)
except subprocess.CalledProcessError as e:
# trivy发现了漏洞,返回码非零,但输出中仍有JSON结果
# 我们需要捕获这个输出进行解析
if e.stdout:
try:
return json.loads(e.stdout)
except json.JSONDecodeError:
# 如果输出不是JSON,可能是其他错误
raise RuntimeError(f"Trivy scan failed with error: {e.stderr}")
else:
raise RuntimeError(f"Trivy command failed: {e.stderr}")
except FileNotFoundError:
raise RuntimeError("Trivy command not found. Please ensure trivy is installed and in your PATH.")
return _scan
这个 trivy_scan fixture 是核心。它封装了调用 trivy 命令的细节,并以 JSON 格式返回结构化的扫描结果。 scope="session" 表示这个 fixture 在整个测试会话中只创建一次,提高了测试效率。
3. 核心安全测试用例设计与实现
有了基础框架,我们现在可以针对 RPA 项目的不同风险点,设计具体的安全测试用例了。
3.1 测试用例一:依赖项漏洞扫描
RPA 脚本严重依赖第三方库。一个存在已知高危漏洞的库,可能就是攻击者入侵的跳板。我们将扫描 requirements.txt 文件。
在 tests/security/test_dependencies.py 中:
# tests/security/test_dependencies.py
import pytest
# 定义漏洞严重级别阈值(可根据项目要求调整)
SEVERITY_THRESHOLD = "HIGH" # 只关注HIGH及以上级别的漏洞
def test_no_critical_vulnerabilities_in_dependencies(trivy_scan):
"""测试项目依赖文件中是否存在超过阈值的漏洞。"""
target_file = "requirements.txt"
# 执行trivy文件系统扫描,指定目标为依赖文件
# trivy 会自动识别 requirements.txt 并扫描其中的包
scan_result = trivy_scan(target_file, scan_type="fs")
# 解析结果,提取漏洞
vulnerabilities = []
# Trivy对文件系统的扫描结果结构
for result in scan_result.get('Results', []):
if 'Vulnerabilities' in result:
vulnerabilities.extend(result['Vulnerabilities'])
# 过滤出超过严重级别阈值的漏洞
high_or_critical_vulns = [
vuln for vuln in vulnerabilities
if vuln.get('Severity') in ['CRITICAL', 'HIGH']
]
# 断言:不应该存在CRITICAL或HIGH级别的漏洞
# 如果存在,测试失败,并打印出详细信息
error_msg = f"发现 {len(high_or_critical_vulns)} 个高危及以上漏洞:\n"
for vuln in high_or_critical_vulns:
error_msg += (f"- [{vuln['Severity']}] {vuln.get('PkgName', 'N/A')}: "
f"{vuln.get('VulnerabilityID', 'N/A')} - {vuln.get('Title', 'No title')}\n"
f" 固定版本: {vuln.get('FixedVersion', 'N/A')}\n")
assert len(high_or_critical_vulns) == 0, error_msg
# 你可以添加更多细粒度的测试
def test_no_known_exploits(trivy_scan):
"""测试是否存在已知有公开利用代码的漏洞(如果trivy支持此元数据)。"""
target_file = "requirements.txt"
scan_result = trivy_scan(target_file, scan_type="fs")
vulnerabilities_with_exploit = []
for result in scan_result.get('Results', []):
for vuln in result.get('Vulnerabilities', []):
# 检查漏洞元数据中是否标记为有公开利用
# 注意:trivy的JSON输出结构可能随版本变化,这里是一个示例逻辑
if vuln.get('PrimaryURL', '').find('exploit') != -1:
vulnerabilities_with_exploit.append(vuln)
assert len(vulnerabilities_with_exploit) == 0, f"发现 {len(vulnerabilities_with_exploit)} 个有公开利用的漏洞"
实操心得 : trivy 对 requirements.txt 的扫描,本质上是检查文件中声明的每个包及其版本,在漏洞数据库(如NVD)中是否有匹配的CVE记录。这里的关键是 assert 语句的失败信息 ( error_msg )。我们精心构造了错误信息,当测试失败时,开发者能立刻看到是哪个库、哪个CVE编号、严重程度以及建议的修复版本,极大提升了排查效率。你可以根据团队的安全策略,灵活调整 SEVERITY_THRESHOLD ,比如在开发分支允许 MEDIUM ,但在主分支或发布前必须清零。
3.2 测试用例二:敏感配置检查
RPA 脚本经常需要处理账号、密码、API密钥等敏感信息。硬编码或在配置文件中明文存储是重大安全风险。我们可以用 trivy 的 config 扫描类型来检查配置文件。
在 tests/security/test_configs.py 中:
# tests/security/test_configs.py
import pytest
import yaml # 需要 pip install PyYAML
from pathlib import Path
PROJECT_ROOT = Path(__file__).parent.parent.parent
CONFIG_DIR = PROJECT_ROOT / "config"
def test_config_files_for_secrets(trivy_scan):
"""扫描配置文件目录,检测是否存在硬编码的密钥、密码等敏感信息。"""
if not CONFIG_DIR.exists():
pytest.skip(f"配置目录 {CONFIG_DIR} 不存在,跳过测试。")
# 扫描整个config目录
scan_result = trivy_scan(str(CONFIG_DIR), scan_type="config")
# trivy config扫描会检查已知的密钥模式、JWT令牌、密码等
misconfigurations = []
for result in scan_result.get('Results', []):
misconfigurations.extend(result.get('Misconfigurations', []))
# 我们只关心高严重性的错误配置
critical_misconfigs = [m for m in misconfigurations if m.get('Severity') in ['CRITICAL', 'HIGH']]
error_msg = f"在配置文件中发现 {len(critical_misconfigs)} 个高危错误配置或敏感信息泄露:\n"
for mis in critical_misconfigs:
error_msg += (f"- [{mis['Severity']}] {mis.get('Title', 'N/A')}\n"
f" 文件: {mis.get('CauseMetadata', {}).get('Resource', 'N/A')}\n"
f" 描述: {mis.get('Description', 'No description')}\n")
assert len(critical_misconfigs) == 0, error_msg
def test_no_hardcoded_credentials_in_python_scripts():
"""一个简单的正则匹配示例,用于在Python脚本中查找可能的硬编码凭证。"""
# 这是一个补充检查,因为trivy的config扫描可能不覆盖所有代码文件
import re
credential_patterns = [
r'password\s*=\s*[\'\"][^\'\"]+[\'\"]',
r'api[_-]?key\s*=\s*[\'\"][^\'\"]+[\'\"]',
r'secret\s*=\s*[\'\"][^\'\"]+[\'\"]',
r'token\s*=\s*[\'\"][^\'\"]+[\'\"]',
]
scripts_dir = PROJECT_ROOT / "rpa_scripts"
problematic_files = []
for py_file in scripts_dir.rglob("*.py"):
with open(py_file, 'r', encoding='utf-8') as f:
content = f.read()
for pattern in credential_patterns:
if re.search(pattern, content, re.IGNORECASE):
problematic_files.append((py_file, pattern))
break # 一个文件找到一个问题就够
error_msg = f"在Python脚本中发现疑似硬编码凭证:\n"
for file, pattern in problematic_files:
error_msg += f"- {file.relative_to(PROJECT_ROOT)} 匹配到模式: {pattern}\n"
assert len(problematic_files) == 0, error_msg + "\n建议:将敏感信息移至环境变量或安全的配置管理服务。"
重要提示 :
trivy config扫描非常有用,但它只是一个基于规则的检测。最根本的安全实践是 永远不要 将真实的密钥提交到代码仓库。应该使用环境变量、密钥管理服务(如AWS Secrets Manager, HashiCorp Vault)或至少在CI/CD流水线中注入。这个测试是一个重要的安全网,用于捕获开发过程中的疏忽。
3.3 测试用例三:自定义安全规则与扩展
除了使用 trivy 的内置规则,我们还可以根据项目特定的安全要求,编写自定义的检查。例如,检查 RPA 脚本是否使用了不安全的网络协议(如 http:// ),或者是否引入了被禁止的库。
在 tests/security/test_custom_rules.py 中:
# tests/security/test_custom_rules.py
import ast
import pytest
from pathlib import Path
PROJECT_ROOT = Path(__file__).parent.parent.parent
SCRIPTS_DIR = PROJECT_ROOT / "rpa_scripts"
class SecurityASTVisitor(ast.NodeVisitor):
"""使用AST(抽象语法树)遍历Python代码,进行自定义安全规则检查。"""
def __init__(self):
self.issues = []
def visit_Call(self, node):
"""检查函数调用,例如是否使用了不安全的pickle.loads"""
if isinstance(node.func, ast.Name):
if node.func.id == 'pickle' and isinstance(node.func.ctx, ast.Load):
# 检查是否调用了 pickle.loads
# 这里需要更精确的检查,简化示例
self.issues.append({
'line': node.lineno,
'issue': '使用不安全的pickle反序列化,可能导致代码执行。建议使用json或更安全的序列化库。'
})
self.generic_visit(node)
def visit_Assign(self, node):
"""检查变量赋值,例如是否直接使用了eval"""
if isinstance(node.value, ast.Call):
if isinstance(node.value.func, ast.Name) and node.value.func.id == 'eval':
self.issues.append({
'line': node.lineno,
'issue': '使用eval函数是危险的,可能导致任意代码执行。'
})
self.generic_visit(node)
def test_custom_ast_security_checks():
"""使用AST检查Python脚本中的不安全代码模式。"""
if not SCRIPTS_DIR.exists():
pytest.skip("RPA脚本目录不存在,跳过测试。")
all_issues = []
for py_file in SCRIPTS_DIR.rglob("*.py"):
try:
with open(py_file, 'r', encoding='utf-8') as f:
tree = ast.parse(f.read(), filename=str(py_file))
except SyntaxError:
continue # 跳过语法错误的文件
visitor = SecurityASTVisitor()
visitor.visit(tree)
if visitor.issues:
for issue in visitor.issues:
issue['file'] = py_file.relative_to(PROJECT_ROOT)
all_issues.extend(visitor.issues)
error_msg = "发现自定义安全规则违规:\n"
for issue in all_issues:
error_msg += f"- 文件: {issue['file']}, 行: {issue['line']}\n 问题: {issue['issue']}\n"
assert len(all_issues) == 0, error_msg
def test_banned_libraries():
"""检查是否引入了项目禁止使用的库。"""
banned_libs = {
'paramiko': '已知存在多个历史漏洞,建议使用更现代的SSH库如fabric或asyncssh。',
# 可以根据需要添加其他库
}
# 这里可以读取requirements.txt或解析import语句
# 简化示例:检查requirements.txt内容
req_file = PROJECT_ROOT / "requirements.txt"
if not req_file.exists():
pytest.skip("requirements.txt 不存在,跳过测试。")
found_banned = []
with open(req_file, 'r') as f:
for line in f:
line = line.strip().split('#')[0] # 移除注释
if not line:
continue
# 简单匹配包名(实际中应使用更精确的解析,如packaging库)
pkg_name = line.split('==')[0].split('>=')[0].split('<=')[0].strip().lower()
if pkg_name in banned_libs:
found_banned.append((pkg_name, banned_libs[pkg_name]))
error_msg = "发现禁止使用的库:\n"
for pkg, reason in found_banned:
error_msg += f"- {pkg}: {reason}\n"
assert len(found_banned) == 0, error_msg
使用 AST 进行代码分析是一种非常强大的静态应用安全测试(SAST)方法。虽然这里只是简单示例,但你可以扩展 SecurityASTVisitor 类来检查 SQL 注入风险(如字符串拼接的 SQL)、命令注入风险(如使用 os.system 拼接用户输入)等复杂模式。
4. 测试执行、报告与CI/CD集成
4.1 本地执行与报告生成
编写完测试用例后,你可以在项目根目录下运行它们。
运行所有安全测试:
pytest tests/security/ -v
-v 参数显示详细信息。
运行特定测试文件:
pytest tests/security/test_dependencies.py -v
并行运行以加快速度(利用 pytest-xdist):
pytest tests/security/ -n auto # 自动检测CPU核心数并行
生成HTML报告:
pytest tests/security/ --html=security_test_report.html --self-contained-html
这会在当前目录生成一个独立的 security_test_report.html 文件,用浏览器打开即可查看美观的测试结果汇总,包括通过/失败数量、错误详情等。这对于向团队展示安全状态非常有用。
4.2 集成到CI/CD流水线(以GitHub Actions为例)
自动化安全测试的价值在CI/CD流水线中才能最大化体现。每次代码推送或合并请求时,自动运行安全测试,阻止含有已知高危漏洞的代码进入主分支。
在 .github/workflows/security-scan.yml 中:
name: Security Scan
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
security-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install Trivy
run: |
sudo apt-get install -y wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install -y trivy
trivy --version
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install pytest pytest-html pytest-xdist
# 安装项目运行依赖(如果需要)
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Update Trivy vulnerability database
run: trivy --download-db-only
- name: Run Security Tests with pytest
run: |
pytest tests/security/ -v --html=security-report.html --self-contained-html
continue-on-error: true # 即使测试失败,也继续执行后续步骤,以便上传报告
- name: Upload Security Test Report
uses: actions/upload-artifact@v3
if: always() # 无论测试成功与否,都上传报告
with:
name: security-test-report
path: security-report.html
这个工作流做了以下几件事:
- 检出代码,安装 Python 和 trivy。
- 更新 trivy 漏洞数据库(确保扫描结果最新)。
- 运行我们编写的所有安全测试用例。
- 无论测试通过与否,都将生成的 HTML 报告保存为工件(Artifact),供后续下载查看。
continue-on-error: true 和 if: always() 的配置确保了即使测试失败(即发现了安全漏洞),流水线也会完成并上传报告,让你能清楚看到问题所在。你可以在仓库的 Settings -> Branches -> Branch protection rules 中,为 main 分支设置规则,要求 security-test 这个 job 必须通过才能合并 Pull Request,从而强制实施安全门禁。
4.3 进阶:失败阈值与基线管理
有时,项目中可能暂时存在一些无法立即修复的历史漏洞。为了不阻塞开发,我们可以设置一个“失败阈值”,允许一定数量和级别的漏洞存在,但超过阈值则失败。
我们可以修改 fixture 或测试逻辑来实现。例如,在 conftest.py 中增加一个解析函数,并在测试中设定允许的漏洞基线:
# 在 conftest.py 或测试文件中
def evaluate_vulnerabilities(vulnerabilities, baseline):
"""
根据基线评估漏洞扫描结果。
:param vulnerabilities: 漏洞列表
:param baseline: 字典,例如 {'CRITICAL': 0, 'HIGH': 2, 'MEDIUM': 10}
:return: (bool, str) 是否通过,错误信息
"""
from collections import defaultdict
counts = defaultdict(int)
for vuln in vulnerabilities:
sev = vuln.get('Severity')
if sev in baseline:
counts[sev] += 1
failed_severities = []
for sev, allowed in baseline.items():
if counts[sev] > allowed:
failed_severities.append(f"{sev}: 发现 {counts[sev]} 个 (允许 {allowed} 个)")
if failed_severities:
return False, "漏洞数量超过基线:\n" + "\n".join(failed_severities)
return True, ""
# 在测试用例中使用
def test_dependencies_against_baseline(trivy_scan):
target_file = "requirements.txt"
scan_result = trivy_scan(target_file, scan_type="fs")
all_vulns = []
for result in scan_result.get('Results', []):
all_vulns.extend(result.get('Vulnerabilities', []))
# 定义基线:允许0个CRITICAL,2个HIGH,10个MEDIUM,LOW不限
baseline = {'CRITICAL': 0, 'HIGH': 2, 'MEDIUM': 10}
passed, message = evaluate_vulnerabilities(all_vulns, baseline)
assert passed, message
这种方法为技术债务管理提供了灵活性,但基线应该随着时间推移而收紧,最终目标是将所有已知高危漏洞清零。
5. 常见问题、排查技巧与优化建议
在实际操作中,你肯定会遇到各种问题。下面是我踩过的一些坑和总结的技巧。
5.1 Trivy 扫描常见问题与解决
问题1: trivy 命令未找到或执行失败。
- 排查 :确保
trivy已正确安装并加入 PATH。在终端直接输入trivy --version测试。在 CI/CD 环境中,安装步骤必须正确无误(如上面 GitHub Actions 示例所示)。 - 技巧 :在
conftest.py的 fixture 中,我们通过捕获FileNotFoundError给出了明确的错误提示。
问题2:扫描速度慢,特别是第一次。
- 原因 :
trivy需要下载漏洞数据库,第一次运行或长时间未更新时会比较慢。 - 解决 :
- 预下载数据库 :在测试开始前,显式运行
trivy --download-db-only。在 CI/CD 中,可以将此步骤缓存,避免每次下载。 - 使用离线模式 :在内网环境,可以搭建一个内部的
trivy数据库镜像服务器,然后通过--db-repository参数指定。 - 并行扫描 :使用
pytest-xdist并行运行测试时,注意trivy命令本身可能不是线程安全的(如果同时写入同一个缓存目录)。可以为每个测试进程设置独立的缓存目录,或使用pytest的--dist=loadscope参数来合理分配测试。
- 预下载数据库 :在测试开始前,显式运行
问题3:误报或漏洞已修复但扫描结果仍显示。
- 原因 :
- 漏洞数据库有延迟,新修复的漏洞可能还未同步。
- 依赖关系解析有误。例如,你的
requirements.txt中写的是requests>=2.25.0,而当前安装的是2.28.0(已修复漏洞),但trivy扫描文件时可能只根据声明的约束条件判断,而不是实际安装的版本。
- 解决 :
- 扫描实际环境 :对于 Python 项目,除了扫描
requirements.txt,更准确的是扫描虚拟环境目录(如venv/)或生成的Pipfile.lock。使用trivy fs /path/to/venv/lib/python3.9/site-packages。 - 使用
--ignore-unfixed参数 :只报告有修复版本的漏洞,避免那些暂无补丁的漏洞干扰(虽然仍需关注)。 - 手动忽略 :对于确认的误报或已通过其他方式缓解的漏洞,可以在项目根目录创建
.trivyignore文件,按 CVE ID 忽略。但需谨慎使用,并定期复审。
- 扫描实际环境 :对于 Python 项目,除了扫描
5.2 Pytest 集成优化技巧
技巧1:合理使用 Fixture 作用域。 我们的 trivy_scan fixture 是 session 作用域,意味着所有测试共用同一个 fixture 实例。这很好,因为启动 trivy 进程有一定开销。但如果测试会修改扫描目标(例如临时创建有漏洞的文件),则需要使用 function 作用域或确保测试相互独立。
技巧2:动态生成测试用例。 如果你的 RPA 项目有很多独立的脚本或模块,可以为每个都生成一个测试用例,而不是写死。
import pytest
from pathlib import Path
SCRIPTS_DIR = Path("rpa_scripts")
# 收集所有.py文件
python_files = list(SCRIPTS_DIR.rglob("*.py"))
@pytest.mark.parametrize("script_file", python_files, ids=lambda p: p.name)
def test_each_script_for_secrets(script_file, trivy_scan):
"""为每个Python脚本单独运行一次敏感信息扫描。"""
result = trivy_scan(str(script_file), scan_type="config")
# ... 断言逻辑 ...
这样,pytest 会为每个 .py 文件生成一个独立的测试项,报告会更清晰。
技巧3:优雅处理测试失败。 安全测试的目标是发现问题,所以测试失败是常态。我们要让失败信息尽可能有用。除了前面构造详细的 error_msg ,还可以利用 pytest 的 pytest.fail() 或自定义异常来提供更丰富的上下文。
5.3 安全测试策略进阶思考
1. 左移与右移结合 :本文主要讲的是“左移”(Shift-Left),即在开发阶段进行静态扫描。完整的RPA安全还应包括“右移”:
- 动态测试(DAST) :对运行中的RPA机器人或它调用的API进行渗透测试。可以使用
ZAP、Burp Suite等工具,同样可以尝试用pytest驱动其API进行自动化扫描。 - 运行时保护 :监控RPA机器人的异常行为,如异常登录、高频失败、数据泄露等。
2. 秘密管理是重中之重 :再次强调,对于RPA,账号、令牌、密钥是最高风险点。除了扫描,必须建立制度:
- 使用环境变量或云服务商的密钥管理服务。
- 在CI/CD中通过安全的方式注入密钥。
- 定期轮换密钥。
3. 将安全测试作为质量门禁 :在团队中推行“安全即代码”的文化。让安全测试和单元测试、集成测试一样,成为提交代码前的必选项。在Git的 pre-commit 钩子中加入快速的安全检查(如依赖扫描、简单密钥检测),在合并请求时运行完整的测试套件。
4. 定期更新与维护 :安全威胁日新月异。你需要:
- 定期(如每天)在CI中更新
trivy的漏洞数据库。 - 定期(如每季度)复审自定义的安全规则和基线阈值。
- 关注所用RPA平台(如UiPath, Blue Prism, 影刀RPA)自身的安全公告。
将 pytest 与 aqua-security/trivy 集成,为Python RPA项目构建自动化的安全测试流程,就像为你的自动化流水线安装了一个“安全防火墙”。它不能保证100%安全,但能极大地降低因已知漏洞、配置错误和不良编码实践带来的风险。从今天开始,选一个现有的RPA项目,尝试加入一个最简单的依赖漏洞扫描测试,你会发现,安全自动化并没有想象中那么复杂,而它带来的安心感,是值得投入的。
更多推荐


所有评论(0)