
DevSecOps:左移安全
CI/CD 流水线中的安全
代码提交 → SAST → SCA → 构建 → 容器扫描 → 部署 → DAST → 监控

SAST(静态应用安全测试)
# GitHub Actions - SAST 流水线
name: Security Pipeline
on: [push, pull_request]
jobs:
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# Semgrep SAST
- name: Semgrep SAST
uses: returntocorp/semgrep-action@v1
with:
config: "p/owasp-top-ten p/nodejs"
auditOn: push
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
# ESLint 安全规则
- name: ESLint Security
run: |
npm install eslint-plugin-security eslint-plugin-no-secrets
npx eslint --plugin security --rule 'security/detect-eval-with-expression: error' src/
# Bandit for Python
- name: Bandit Python SAST
if: hashFiles('**/*.py') != ''
run: |
pip install bandit
bandit -r . -f json -o bandit-report.json -ll
cat bandit-report.json
# 上传 SARIF 到 GitHub Security
- name: Upload SAST results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: sast-results.sarif
SCA(软件组成分析)
sca:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Snyk 漏洞扫描
- name: Snyk SCA
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
command: test
args: --severity-threshold=high --json > snyk-results.json
# 许可证合规性
- name: License Check
run: |
npx license-checker --onlyAllow "MIT;ISC;BSD-2-Clause;BSD-3-Clause;Apache-2.0" \
--excludePrivatePackages \
--failOn "GPL-2.0;GPL-3.0;AGPL-3.0"
# OWASP Dependency Check
- name: OWASP Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'myapp'
path: '.'
format: 'SARIF'
args: --failOnCVSS 8

基础设施即代码安全
iac-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Checkov - Terraform/K8s/Dockerfile 扫描
- name: Checkov IaC Scan
uses: bridgecrewio/checkov-action@v12
with:
directory: infrastructure/
framework: terraform,kubernetes,dockerfile
soft_fail: false
output_format: sarif
output_file_path: checkov-results.sarif
# Trivy for Terraform
- name: Trivy IaC Scan
run: |
trivy config infrastructure/ \
--exit-code 1 \
--severity HIGH,CRITICAL \
--format sarif -o trivy-iac.sarif
# tfsec for Terraform
- name: tfsec
uses: aquasecurity/tfsec-action@v1.0.0
with:
working_directory: infrastructure/terraform/
容器安全扫描
container-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy container scan
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: sarif
output: trivy-container.sarif
severity: CRITICAL,HIGH
exit-code: 1
- name: Hadolint Dockerfile lint
uses: hadolint/hadolint-action@v3.1.0
with:
dockerfile: Dockerfile
failure-threshold: error

DAST(动态应用安全测试)
dast:
runs-on: ubuntu-latest
needs: [deploy-staging]
steps:
- name: OWASP ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.10.0
with:
target: https://staging.myapp.com
rules_file_name: .zap/rules.tsv
fail_action: true
- name: Nuclei DAST
uses: projectdiscovery/nuclei-action@main
with:
target: https://staging.myapp.com
flags: "-severity critical,high -t cves/ -t exposures/"
安全质量门控
// security-gate.js - 如果发现安全问题则阻止部署
const fs = require('fs')
function checkSecurityGates() {
const gates = []
// 检查 SAST 结果
const sast = JSON.parse(fs.readFileSync('semgrep-results.json'))
const criticalFindings = sast.results.filter(r => r.extra.severity === 'ERROR')
gates.push({
name: 'SAST',
passed: criticalFindings.length === 0,
details: `${criticalFindings.length} 个严重发现`,
})
// 检查 SCA
const snyk = JSON.parse(fs.readFileSync('snyk-results.json'))
const criticalVulns = snyk.vulnerabilities?.filter(v => v.severity === 'critical') || []
gates.push({
name: 'SCA',
passed: criticalVulns.length === 0,
details: `${criticalVulns.length} 个严重漏洞`,
})
// 报告
const failed = gates.filter(g => !g.passed)
if (failed.length > 0) {
console.error('安全门控失败:')
failed.forEach(g => console.error(` - ${g.name}: ${g.details}`))
process.exit(1)
}
console.log('所有安全门控通过!')
}
checkSecurityGates()
DevSecOps 成熟度模型
| 级别 |
实践 |
| 1 - 基础 |
依赖扫描,SAST |
| 2 - 发展中 |
容器扫描,IaC 检查 |
| 3 - 已定义 |
DAST,安全门控,策略即代码 |
| 4 - 已管理 |
威胁建模,安全混沌 |
| 5 - 已优化 |
持续安全,紫队协作 |