Files
wecom_it_smart_desk/scripts/security-audit.sh
T
Simon 93ba41ed79 feat: 审批流程模块 (T审批A审批)
- 新增 backend/app/api/approval.py 审批API
- 前端H5支持发起审批、审批操作
- 添加审批卡片弹窗组件
- 路由注册审批模块
2026-06-15 09:32:41 +08:00

343 lines
11 KiB
Bash

#!/bin/bash
# =============================================================================
# 安全审计脚本
# =============================================================================
# 用途: 跑 5 大安全工具,生成审计报告
# 1. bandit - Python 代码静态分析
# 2. safety - Python 依赖漏洞
# 3. pip-audit - Python 依赖漏洞(更准)
# 4. npm audit - JS 依赖漏洞
# 5. gitleaks - 仓库 secret 扫描
#
# 用法:
# bash scripts/security-audit.sh # 跑全部
# bash scripts/security-audit.sh --python # 只跑 Python 套件
# bash scripts/security-audit.sh --js # 只跑 JS 套件
# bash scripts/security-audit.sh --secrets # 只跑 secret 扫描
# bash scripts/security-audit.sh --output FILE # 自定义报告路径
#
# 退出码:
# 0 = 全过 / 仅 INFO
# 1 = 有 LOW
# 2 = 有 MEDIUM
# 3 = 有 HIGH/CRITICAL
# =============================================================================
set -e
# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
# 路径
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$PROJECT_ROOT"
REPORT="docs/审计报告/security_audit_$(date +%Y%m%d).md"
LOG_DIR="/tmp/security-audit-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$LOG_DIR" "$(dirname "$REPORT")"
# 参数
RUN_PYTHON=true
RUN_JS=true
RUN_SECRETS=true
for arg in "$@"; do
case $arg in
--python) RUN_PYTHON=true; RUN_JS=false; RUN_SECRETS=false ;;
--js) RUN_PYTHON=false; RUN_JS=true; RUN_SECRETS=false ;;
--secrets) RUN_PYTHON=false; RUN_JS=false; RUN_SECRETS=true ;;
--output) REPORT="$2" ;;
esac
done
# 计数器
PASS=0
WARN=0
FAIL=0
CRITICAL=0
# 报告头
cat > "$REPORT" <<EOF
# 安全审计报告
**审计日期**: $(date +%Y-%m-%d)
**审计人**: Claude(自动化跑批)
**工具**: bandit / safety / pip-audit / npm audit / gitleaks
**关联**: [[风险跟踪表]]
---
## 1. 跑批概览
| 工具 | 范围 | 结果 |
|---|---|---|
EOF
# =============================================================================
# 1. bandit (Python 静态分析)
# =============================================================================
if [ "$RUN_PYTHON" = true ]; then
info "── 1/5 bandit: Python 静态分析"
if ! command -v bandit &> /dev/null; then
warn "bandit 未安装,跑: pip install bandit"
echo "| bandit | Python 静态 | ⚠️ 工具未安装 |" >> "$REPORT"
else
if bandit -r backend/ -f json -o "$LOG_DIR/bandit.json" 2> "$LOG_DIR/bandit.err" ; then
ok "bandit: 无问题"
PASS=$((PASS+1))
echo "| bandit | Python 静态 | ✅ 无问题 |" >> "$REPORT"
else
# 解析 bandit JSON 报告
if command -v jq &> /dev/null; then
HIGH=$(jq '[.results[] | select(.issue_severity=="HIGH")] | length' "$LOG_DIR/bandit.json" 2>/dev/null || echo 0)
MED=$(jq '[.results[] | select(.issue_severity=="MEDIUM")] | length' "$LOG_DIR/bandit.json" 2>/dev/null || echo 0)
LOW=$(jq '[.results[] | select(.issue_severity=="LOW")] | length' "$LOG_DIR/bandit.json" 2>/dev/null || echo 0)
else
HIGH=0; MED=0; LOW=0
fi
warn "bandit: HIGH=$HIGH MED=$MED LOW=$LOW"
[ $HIGH -gt 0 ] && FAIL=$((FAIL+HIGH)) || true
[ $MED -gt 0 ] && WARN=$((WARN+MED)) || true
[ $LOW -gt 0 ] && WARN=$((WARN+LOW)) || true
echo "| bandit | Python 静态 | ⚠️ HIGH=$HIGH MED=$MED LOW=$LOW |" >> "$REPORT"
# 列出问题
if [ $HIGH -gt 0 ] || [ $MED -gt 0 ]; then
cat >> "$REPORT" <<EOR
### bandit 详情
| 文件 | 行 | 严重度 | 问题 |
|---|---|---|---|
EOR
jq -r '.results[] | "| \(.filename) | \(.line_number) | \(.issue_severity) | \(.issue_text | gsub("\n"; " ")) |"' "$LOG_DIR/bandit.json" >> "$REPORT" 2>/dev/null || true
fi
fi
fi
fi
# =============================================================================
# 2. safety (Python 依赖漏洞)
# =============================================================================
if [ "$RUN_PYTHON" = true ]; then
info "── 2/5 safety: Python 依赖漏洞"
if ! command -v safety &> /dev/null; then
warn "safety 未安装,跑: pip install safety"
echo "| safety | Python 依赖 | ⚠️ 工具未安装 |" >> "$REPORT"
else
if safety check --file=backend/requirements.txt --output=text > "$LOG_DIR/safety.txt" 2>&1; then
ok "safety: 无漏洞"
PASS=$((PASS+1))
echo "| safety | Python 依赖 | ✅ 无漏洞 |" >> "$REPORT"
else
VULN_COUNT=$(grep -c "VULNERABLE" "$LOG_DIR/safety.txt" 2>/dev/null || echo 0)
warn "safety: $VULN_COUNT 个漏洞"
[ $VULN_COUNT -gt 0 ] && FAIL=$((FAIL+VULN_COUNT)) || true
echo "| safety | Python 依赖 | 🔴 $VULN_COUNT 个漏洞 |" >> "$REPORT"
cat >> "$REPORT" <<EOR
### safety 详情
\`\`\`
$(cat "$LOG_DIR/safety.txt" | head -30)
\`\`\`
EOR
fi
fi
fi
# =============================================================================
# 3. pip-audit (Python 依赖漏洞,更准)
# =============================================================================
if [ "$RUN_PYTHON" = true ]; then
info "── 3/5 pip-audit: Python 依赖漏洞(精确)"
if ! command -v pip-audit &> /dev/null; then
warn "pip-audit 未安装,跑: pip install pip-audit"
echo "| pip-audit | Python 依赖 | ⚠️ 工具未安装 |" >> "$REPORT"
else
if pip-audit -r backend/requirements.txt --format=json > "$LOG_DIR/pip-audit.json" 2>&1; then
ok "pip-audit: 无漏洞"
PASS=$((PASS+1))
echo "| pip-audit | Python 依赖 | ✅ 无漏洞 |" >> "$REPORT"
else
VULN_COUNT=$(python3 -c "import json; d=json.load(open('$LOG_DIR/pip-audit.json')); print(len(d.get('vulnerabilities', [])))" 2>/dev/null || echo 0)
warn "pip-audit: $VULN_COUNT 个漏洞"
[ $VULN_COUNT -gt 0 ] && FAIL=$((FAIL+VULN_COUNT)) || true
echo "| pip-audit | Python 依赖 | 🔴 $VULN_COUNT 个漏洞 |" >> "$REPORT"
fi
fi
fi
# =============================================================================
# 4. npm audit (JS 依赖漏洞)
# =============================================================================
if [ "$RUN_JS" = true ]; then
info "── 4/5 npm audit: JS 依赖漏洞"
for d in frontend-admin frontend-agent frontend-h5 frontend-portal; do
if [ -d "$d" ]; then
info "$d"
if [ -f "$d/package-lock.json" ]; then
cd "$d"
if npm audit --json > "$LOG_DIR/npm-$d.json" 2>&1; then
ok " $d: 无漏洞"
PASS=$((PASS+1))
else
VULN=$(python3 -c "import json; d=json.load(open('$LOG_DIR/npm-$d.json')); m=d.get('metadata',{}).get('vulnerabilities',{}); print(m.get('total', 0))" 2>/dev/null || echo 0)
CRIT=$(python3 -c "import json; d=json.load(open('$LOG_DIR/npm-$d.json')); m=d.get('metadata',{}).get('vulnerabilities',{}); print(m.get('critical', 0))" 2>/dev/null || echo 0)
HIGH=$(python3 -c "import json; d=json.load(open('$LOG_DIR/npm-$d.json')); m=d.get('metadata',{}).get('vulnerabilities',{}); print(m.get('high', 0))" 2>/dev/null || echo 0)
warn " $d: total=$VULN critical=$CRIT high=$HIGH"
[ $CRIT -gt 0 ] && CRITICAL=$((CRITICAL+CRIT)) || true
[ $HIGH -gt 0 ] && FAIL=$((FAIL+HIGH)) || true
[ $VULN -gt 0 ] && WARN=$((WARN+VULN)) || true
echo "| npm-audit-$d | JS 依赖 | ⚠️ total=$VULN crit=$CRIT high=$HIGH |" >> "$REPORT"
fi
cd "$PROJECT_ROOT"
fi
fi
done
fi
# =============================================================================
# 5. gitleaks (Secret 扫描)
# =============================================================================
if [ "$RUN_SECRETS" = true ]; then
info "── 5/5 gitleaks: Secret 扫描"
if ! command -v gitleaks &> /dev/null; then
warn "gitleaks 未安装,跑(可选):"
echo " brew install gitleaks # Mac"
echo " scoop install gitleaks # Windows"
echo " docker run -v \$(pwd):/repo zricethezav/gitleaks:latest detect --source /repo --no-git -v"
echo "| gitleaks | Secret 扫描 | ⚠️ 工具未安装 |" >> "$REPORT"
else
if gitleaks detect --source . --no-git -v > "$LOG_DIR/gitleaks.txt" 2>&1; then
ok "gitleaks: 无 secret 泄露"
PASS=$((PASS+1))
echo "| gitleaks | Secret 扫描 | ✅ 无泄露 |" >> "$REPORT"
else
LEAK_COUNT=$(grep -c "Finding:" "$LOG_DIR/gitleaks.txt" 2>/dev/null || echo 0)
if [ "$LEAK_COUNT" -gt 0 ]; then
warn "gitleaks: 发现 $LEAK_COUNT 个 secret"
CRITICAL=$((CRITICAL+LEAK_COUNT))
echo "| gitleaks | Secret 扫描 | 🔴 $LEAK_COUNT 个 secret |" >> "$REPORT"
cat >> "$REPORT" <<EOR
### gitleaks 详情
\`\`\`
$(head -50 "$LOG_DIR/gitleaks.txt")
\`\`\`
> 🚨 **CRITICAL**:发现 secret 泄露,立即:
> 1. 撤销泄露的 token / 密钥
> 2. 创新凭据
> 3. 加进 .gitignore(防二次泄露)
> 4. 改所有引用
EOR
fi
fi
fi
fi
# =============================================================================
# 总结
# =============================================================================
cat >> "$REPORT" <<EOF
---
## 2. 总结
| 等级 | 数量 |
|---|---|
| ✅ PASS | $PASS |
| ⚠️ WARN | $WARN |
| 🔴 FAIL | $FAIL |
| 🚨 CRITICAL | $CRITICAL |
EOF
if [ $CRITICAL -gt 0 ]; then
cat >> "$REPORT" <<EOF
## 🚨 阻断
发现 **$CRITICAL** 个 CRITICAL 问题,**必须**:
1. 撤销所有泄露的 secret(token / 密钥 / 凭据)
2. 创新凭据 + 配新引用
3. 加进 .gitignore
4. 评审所有引用 + 改
## 下一步
1. 修所有 CRITICAL(立即)
2. 修所有 FAIL(本周末)
3. 评估 WARN(下迭代)
4. 加 CI 自动化跑(本季度)
EOF
echo ""
error "🚨 CRITICAL: $CRITICAL 个 secret 泄露或 CRITICAL 漏洞,必须立即处理"
exit 3
fi
if [ $FAIL -gt 0 ]; then
cat >> "$REPORT" <<EOF
## 🛑 FAIL
发现 **$FAIL** 个 FAIL(HIGH)级问题,本周末修。
## 下一步
1. 修 FAIL(本周末)
2. 评估 WARN(下迭代)
3. 加 CI 自动化跑
EOF
echo ""
warn "🛑 FAIL: $FAIL 个 HIGH 级问题,本周末修"
exit 2
fi
if [ $WARN -gt 0 ]; then
cat >> "$REPORT" <<EOF
## ⚠️ WARN
发现 **$WARN** 个 MEDIUM/LOW 级问题,下迭代评估。
## 下一步
1. 评估 WARN(下迭代)
2. 加 CI 自动化跑
3. 跑批频率:每周一次
EOF
echo ""
warn "⚠️ WARN: $WARN 个问题,下迭代评估"
exit 1
fi
cat >> "$REPORT" <<EOF
## ✅ 全部通过
无 CRITICAL / FAIL / WARN,健康度 100%。
## 后续
1. 加 CI 自动化跑(每周 + 推送触发)
2. 跑批频率:每周一次 + 重大变更后
EOF
echo ""
ok "✅ 全部通过,健康度 100%"
ok "报告: $REPORT"
exit 0