漏洞扫描 vulnerability-scanning

这个技能涉及使用自动化工具和手动评估来识别和修复软件中的安全漏洞,包括依赖项检查、代码审计、容器和基础设施的安全评估。

安全审计 0 次安装 0 次浏览 更新于 3/4/2026

name: 漏洞扫描 description: 使用OWASP工具、CVE数据库和安全扫描器自动检测应用程序、依赖项和基础设施中的安全漏洞。在执行安全审计、合规性检查或持续安全监控时使用。

漏洞扫描

概览

系统地使用自动化扫描工具和手动安全评估来识别应用程序、依赖项和基础设施中的安全漏洞。

何时使用

  • 部署前安全检查
  • 持续安全监控
  • 合规性审计(PCI-DSS,SOC 2)
  • 依赖项漏洞检测
  • 容器安全扫描
  • 基础设施安全评估

实施示例

1. Node.js 漏洞扫描器

// scanner.js - 综合漏洞扫描
const { exec } = require('child_process');
const util = require('util');
const fs = require('fs').promises;

const execPromise = util.promisify(exec);

class VulnerabilityScanner {
  constructor() {
    this.results = {
      dependencies: [],
      code: [],
      docker: [],
      secrets: []
    };
  }

  async scanDependencies() {
    console.log('使用npm audit扫描依赖项...');

    try {
      const { stdout } = await execPromise('npm audit --json');
      const auditResults = JSON.parse(stdout);

      for (const [name, advisory] of Object.entries(auditResults.vulnerabilities || {})) {
        this.results.dependencies.push({
          package: name,
          severity: advisory.severity,
          cve: advisory.via.filter(v => typeof v === 'object').map(v => v.cve),
          title: advisory.via.filter(v => typeof v === 'object').map(v => v.title),
          vulnerableVersions: advisory.range,
          recommendation: advisory.fixAvailable ? '更新可用' : '无可用的修复'
        });
      }
    } catch (error) {
      console.error('依赖项扫描失败:', error.message);
    }
  }

  async scanCode() {
    console.log('使用ESLint安全插件扫描代码...');

    try {
      const { stdout } = await execPromise('npx eslint . --format json --plugin security');
      const eslintResults = JSON.parse(stdout);

      for (const file of eslintResults) {
        for (const message of file.messages) {
          if (message.ruleId && message.ruleId.includes('security')) {
            this.results.code.push({
              file: file.filePath,
              line: message.line,
              column: message.column,
              rule: message.ruleId,
              severity: message.severity === 2 ? '高' : '中',
              message: message.message
            });
          }
        }
      }
    } catch (error) {
      console.log('代码扫描完成,发现问题');
    }
  }

  async scanDockerfile() {
    console.log('使用hadolint扫描Dockerfile...');

    try {
      const { stdout } = await execPromise('hadolint Dockerfile --format json');
      const hadolintResults = JSON.parse(stdout);

      for (const issue of hadolintResults) {
        this.results.docker.push({
          line: issue.line,
          level: issue.level,
          code: issue.code,
          message: issue.message
        });
      }
    } catch (error) {
      console.log('Dockerfile扫描完成');
    }
  }

  async scanSecrets() {
    console.log('使用truffleHog扫描秘密...');

    try {
      const { stdout } = await execPromise('trufflehog filesystem . --json');
      const lines = stdout.trim().split('
');

      for (const line of lines) {
        if (line) {
          const finding = JSON.parse(line);
          this.results.secrets.push({
            file: finding.SourceMetadata?.Data?.Filesystem?.file,
            line: finding.SourceMetadata?.Data?.Filesystem?.line,
            type: finding.DetectorName,
            verified: finding.Verified
          });
        }
      }
    } catch (error) {
      console.log('秘密扫描完成');
    }
  }

  async runFullScan() {
    await this.scanDependencies();
    await this.scanCode();
    await this.scanDockerfile();
    await this.scanSecrets();

    return this.generateReport();
  }

  generateReport() {
    const report = {
      timestamp: new Date().toISOString(),
      summary: {
        critical: 0,
        high: 0,
        medium: 0,
        low: 0
      },
      findings: this.results,
      totalIssues: 0
    };

    // 按严重程度计数
    for (const dep of this.results.dependencies) {
      report.summary[dep.severity]++;
      report.totalIssues++;
    }

    for (const code of this.results.code) {
      report.summary[code.severity]++;
      report.totalIssues++;
    }

    // 高严重程度的秘密
    report.summary.high += this.results.secrets.filter(s => s.verified).length;
    report.totalIssues += this.results.secrets.length;

    return report;
  }
}

// 使用方法
async function main() {
  const scanner = new VulnerabilityScanner();
  const report = await scanner.runFullScan();

  await fs.writeFile('security-report.json', JSON.stringify(report, null, 2));

  console.log('
=== 安全扫描摘要 ===');
  console.log(`总问题数: ${report.totalIssues}`);
  console.log(`严重: ${report.summary.critical}`);
  console.log(`高: ${report.summary.high}`);
  console.log(`中: ${report.summary.medium}`);
  console.log(`低: ${report.summary.low}`);
}

main().catch(console.error);

2. Python OWASP 扫描器

# owasp_scanner.py
import subprocess
import json
import os
from typing import Dict, List
from dataclasses import dataclass, asdict
from datetime import datetime

@dataclass
class Vulnerability:
    severity: str
    cve: str
    package: str
    version: str
    description: str
    fix: str

class OWASPScanner:
    def __init__(self, project_path: str):
        self.project_path = project_path
        self.vulnerabilities: List[Vulnerability] = []

    def scan_dependencies(self) -> None:
        """使用Safety扫描Python依赖项"""
        print("使用Safety扫描依赖项...")

        try:
            result = subprocess.run(
                ['safety', 'check', '--json', '--file', 'requirements.txt'],
                cwd=self.project_path,
                capture_output=True,
                text=True
            )

            if result.stdout:
                findings = json.loads(result.stdout)

                for vuln in findings:
                    self.vulnerabilities.append(Vulnerability(
                        severity=self._map_severity(vuln.get('severity', 'unknown')),
                        cve=vuln.get('cve', 'N/A'),
                        package=vuln.get('package_name', ''),
                        version=vuln.get('analyzed_version', ''),
                        description=vuln.get('advisory', ''),
                        fix=f"升级到 {vuln.get('fixed_versions', 'latest')}"
                    ))

        except Exception as e:
            print(f"依赖项扫描错误: {e}")

    def scan_with_bandit(self) -> None:
        """使用Bandit扫描Python代码"""
        print("使用Bandit扫描代码...")

        try:
            result = subprocess.run(
                ['bandit', '-r', '.', '-f', 'json'],
                cwd=self.project_path,
                capture_output=True,
                text=True
            )

            if result.stdout:
                findings = json.loads(result.stdout)

                for issue in findings.get('results', []):
                    self.vulnerabilities.append(Vulnerability(
                        severity=issue['issue_severity'].lower(),
                        cve='BANDIT-' + issue['test_id'],
                        package=os.path.basename(issue['filename']),
                        version=str(issue['line_number']),
                        description=f"{issue['issue_text']} - {issue['test_name']}",
                        fix=issue.get('more_info', 'Review code')
                    ))

        except Exception as e:
            print(f"Bandit扫描错误: {e}")

    def scan_with_trivy(self) -> None:
        """使用Trivy扫描容器镜像"""
        print("使用Trivy扫描容器...")

        try:
            result = subprocess.run(
                ['trivy', 'image', '--format', 'json', 'myapp:latest'],
                capture_output=True,
                text=True
            )

            if result.stdout:
                findings = json.loads(result.stdout)

                for result_item in findings.get('Results', []):
                    for vuln in result_item.get('Vulnerabilities', []):
                        self.vulnerabilities.append(Vulnerability(
                            severity=vuln['Severity'].lower(),
                            cve=vuln.get('VulnerabilityID', 'N/A'),
                            package=vuln['PkgName'],
                            version=vuln['InstalledVersion'],
                            description=vuln.get('Title', vuln.get('Description', '')),
                            fix=vuln.get('FixedVersion', '无可用的修复')
                        ))

        except Exception as e:
            print(f"Trivy扫描错误: {e}")

    def _map_severity(self, severity: str) -> str:
        mapping = {
            'critical': 'critical',
            'high': 'high',
            'medium': 'medium',
            'low': 'low'
        }
        return mapping.get(severity.lower(), 'medium')

    def generate_report(self) -> Dict:
        summary = {
            'critical': 0,
            'high': 0,
            'medium': 0,
            'low': 0
        }

        for vuln in self.vulnerabilities:
            if vuln.severity in summary:
                summary[vuln.severity] += 1

        return {
            'timestamp': datetime.now().isoformat(),
            'total_vulnerabilities': len(self.vulnerabilities),
            'summary': summary,
            'vulnerabilities': [asdict(v) for v in self.vulnerabilities],
            'compliance_status': 'FAIL' if summary['critical'] > 0 or summary['high'] > 0 else 'PASS'
        }

    def run_full_scan(self) -> Dict:
        self.scan_dependencies()
        self.scan_with_bandit()
        self.scan_with_trivy()

        report = self.generate_report()

        with open('owasp-scan-report.json', 'w') as f:
            json.dump(report, f, indent=2)

        return report

# 使用方法
if __name__ == '__main__':
    scanner = OWASPScanner('.')
    report = scanner.run_full_scan()

    print(f"
=== OWASP扫描完成 ===")
    print(f"总漏洞数: {report['total_vulnerabilities']}")
    print(f"严重: {report['summary']['critical']}")
    print(f"高: {report['summary']['high']}")
    print(f"合规性状态: {report['compliance_status']}")

3. CI/CD集成 - GitHub Actions

# .github/workflows/security-scan.yml
name: 安全漏洞扫描

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
  schedule:
    - cron: '0 2 * * *' # 每天凌晨2点

jobs:
  vulnerability-scan:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: 运行Trivy漏洞扫描器
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH,MEDIUM'

      - name: 将Trivy结果上传到GitHub安全
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'

      - name: 运行Snyk安全扫描
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high

      - name: OWASP依赖项检查
        uses: dependency-check/Dependency-Check_Action@main
        with:
          project: 'myapp'
          path: '.'
          format: 'JSON'

      - name: 严重漏洞失败
        run: |
          if [ -f trivy-results.sarif ]; then
            HIGH_COUNT=$(jq '.runs[].results | length' trivy-results.sarif)
            if [ $HIGH_COUNT -gt 0 ]; then
              echo "发现$HIGH_COUNT个高严重漏洞"
              exit 1
            fi
          fi

最佳实践

✅ 要做

  • 在CI/CD中自动化扫描
  • 定期扫描依赖项
  • 使用多个扫描工具
  • 设置严重性阈值
  • 跟踪漏洞趋势
  • 扫描容器和镜像
  • 监控CVE数据库
  • 文档化误报

❌ 不要做

  • 跳过漏洞扫描
  • 忽视低严重性问题
  • 信任单一扫描工具
  • 绕过安全门
  • 提交秘密到仓库

常见漏洞类型

  • CVE: 库中已知漏洞
  • CWE: 常见弱点模式
  • OWASP Top 10: Web应用风险
  • 容器漏洞: 基础镜像问题
  • 依赖项混淆: 供应链攻击
  • 秘密暴露: 硬编码凭证

扫描工具

  • Trivy: 容器和文件系统扫描器
  • Snyk: 依赖项和代码扫描
  • OWASP Dependency Check: Java, .NET, Node.js
  • Safety: Python依赖项检查器
  • npm audit / yarn audit: Node.js包
  • Bandit: Python安全linter
  • Semgrep: 静态分析工具

资源