
本工具集专为应对 CVE-2025-30066 漏洞设计,旨在帮助开发者和安全团队主动扫描 GitHub Actions 工作流中的潜在恶意行为、风险组件以及可能通过日志泄露的密钥。通过自动化扫描,您可以在攻击者利用之前发现并修复安全隐患,保障 CI/CD 流水线的安全。
--org)、按单个仓库(--repo)或按特定用户(--user)进行扫描,灵活适配不同管理场景。reviewdog/* 和 tj-actions/* 系列。.github/workflows 目录及其子目录,全面审查所有 YAML 配置文件。concurrent.futures 实现多线程扫描,大幅提升大规模日志文件的处理效率。CxGithub2msScan 需要):2ms 可执行文件。CxGithub2msScan.py 相同的目录下。repo(完全控制私有仓库)和 read:org(读取组织数据)。public_repo(访问公共仓库)或 repo(访问私有仓库)。--token 参数提供。python CxGithubActionsScan.py --org your-organization-name --token YOUR_GITHUB_PATpython CxGithubActionsScan.py --repo owner/repo-name --token YOUR_GITHUB_PAT或使用完整 GitHub URL:
python CxGithubActionsScan.py --repo https://github.com/owner/repo-name --token YOUR_GITHUB_PATpython CxGithubActionsScan.py --user github-username --token YOUR_GITHUB_PATpython CxGithub2msScan.py --owner your-org-or-username --repo your-repo-name --days 7 --token YOUR_GITHUB_TOKEN --output ./logs--owner:仓库所属的组织名或用户名。--repo:仓库名称。--days:要扫描的日志天数范围(从当前时间向前推)。--token:GitHub 个人访问令牌。--output:下载的日志文件存储目录。CxGithubActionsScan - 核心扫描逻辑 (片段)
# 递归扫描仓库中的指定路径,检测风险 Actions 和恶意代码
def scan_path(owner, repo, path, search_terms):
url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}"
response = requests.get(url, headers=HEADERS)
if response.status_code == 404:
return {}
if response.status_code != 200:
print(f"Error retrieving {path} in {owner}/{repo}: {response.status_code}")
return {}
data = response.json()
results = {}
if isinstance(data, dict):
# 处理单个文件(Base64 编码)
if "content" in data and data.get("encoding") == "base64":
try:
content = base64.b64decode(data["content"]).decode("utf-8")
except Exception as e:
print(f"Error decoding content in {owner}/{repo}/{path}: {e}")
return results
# 检查文件中是否包含任何风险特征词
matches = [term for term in search_terms if term in content]
if matches:
results[path] = matches
return results
# ... 处理目录的逻辑CxGithub2msScan - 日志扫描核心逻辑 (片段)
# 获取指定时间范围内的所有工作流运行记录
def get_workflow_runs(owner, repo, days, token):
headers = {"Accept": "application/vnd.github.v3+json", "Authorization": f"Bearer {token}"}
runs = []
page = 1
per_page = 100
cutoff = datetime.datetime.utcnow() - datetime.timedelta(days=days)
while True:
url = f"https://api.github.com/repos/{owner}/{repo}/actions/runs"
params = {"per_page": per_page, "page": page}
resp = requests.get(url, headers=headers, params=params)
if resp.status_code != 200:
break
data = resp.json()
if "workflow_runs" not in data or not data["workflow_runs"]:
break
for run in data["workflow_runs"]:
created_at = datetime.datetime.strptime(run["created_at"], "%Y-%m-%dT%H:%M:%SZ")
if created_at < cutoff:
return runs # 返回截止时间之前的 runs
runs.append(run)
page += 1
return runs
# 使用 2ms 引擎扫描单个文件
def scan_file(file_path):
print(f"Running Checkmarx 2ms on {file_path}...")
try:
result = subprocess.run(["2ms.exe", "filesystem", "--path", file_path],
capture_output=True, text=True)
if result.returncode != 0:
return {"file": file_path, "error": result.stderr.strip()}
# 从输出中提取找到的密钥数量
match = re.search(r"totalsecretsfound:\s*(\d+)", result.stdout, re.IGNORECASE)
secret_count = int(match.group(1)) if match else 0
if secret_count > 0:
return {"file": file_path, "secrets_found": secret_count, "output": result.stdout.strip()}
else:
return {"file": file_path, "secrets_found": 0}
except Exception as e:
return {"file": file_path, "error": str(e)}
```FINISHED6HFtX5dABrKlqXeO5PUv/7b4YVDDMwcrE+FmfPoZGK2bxM2ff+ga26S+bYmqNFP+wkH49J3l6n8JiqJi3LVqOw==
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。