Security Testing Integration
Integrate comprehensive security testing into CI/CD pipelines to identify and fix vulnerabilities early in the development lifecycle.
Security Testing Overview
Security testing should be integrated throughout the CI/CD pipeline, implementing a "shift-left" approach to catch vulnerabilities early.
Security Testing Pyramid
DevSecOps Pipeline
Static Application Security Testing (SAST)
SAST analyzes source code to identify security vulnerabilities without executing the application.
SonarQube Security Rules
1. SonarQube Security Setup
# sonar-project.properties
sonar.projectKey=myapp
sonar.projectName=My Application
sonar.projectVersion=1.0
# Source code paths
sonar.sources=src
sonar.tests=test
# Security hotspots
sonar.security.hotspots.enabled=true
# Security categories to track
sonar.security.rules=squid:S2068,squid:S2076,squid:S2078,squid:S2089
# Coverage and quality gates
sonar.qualitygate.wait=true
sonar.qualitygate.timeout=300
# Security standards
sonar.security.standards=owasp-top10-2021,cwe,sans-top25
2. Security-Focused Pipeline
pipeline {
agent any
environment {
SONAR_HOST_URL = 'http://sonarqube:9000'
SONAR_TOKEN = credentials('sonarqube-token')
}
stages {
stage('Secret Scanning') {
steps {
script {
// Scan for secrets in code
sh '''
# Install gitleaks
docker pull zricethezav/gitleaks:latest
# Run gitleaks scan
docker run -v $(pwd):/code zricethezav/gitleaks:latest \
detect --source /code --verbose --report-path /code/gitleaks-report.json
'''
// Check for secrets found
def report = readJSON file: 'gitleaks-report.json'
if (report.size() > 0) {
error "Secrets found in code! Please remove them before proceeding."
}
}
}
}
stage('SAST with SonarQube') {
steps {
script {
def scannerHome = tool 'SonarQubeScanner'
withSonarQubeEnv('SonarQube') {
sh """
${scannerHome}/bin/sonar-scanner \
-Dsonar.projectKey=myapp \
-Dsonar.sources=src \
-Dsonar.security.hotspots.enabled=true \
-Dsonar.qualitygate.wait=true
"""
}
}
}
}
stage('Security Quality Gate') {
steps {
timeout(time: 5, unit: 'MINUTES') {
script {
def qg = waitForQualityGate()
if (qg.status != 'OK') {
// Get security issues
def securityIssues = sh(
script: """
curl -u ${SONAR_TOKEN}: \
"${SONAR_HOST_URL}/api/issues/search?componentKeys=myapp&types=VULNERABILITY,SECURITY_HOTSPOT"
""",
returnStdout: true
)
error "Security quality gate failed: ${qg.status}\n${securityIssues}"
}
}
}
}
}
}
}
Semgrep Security Scanning
1. Semgrep Configuration
# .semgrep.yml
rules:
# SQL Injection
- id: sql-injection
patterns:
- pattern: |
$QUERY = "SELECT * FROM users WHERE id = " + $INPUT
message: "Potential SQL injection vulnerability"
severity: ERROR
languages: [javascript, python, java]
# XSS Prevention
- id: xss-vulnerability
patterns:
- pattern: |
element.innerHTML = $INPUT
message: "Potential XSS vulnerability - use textContent instead"
severity: ERROR
languages: [javascript]
# Hardcoded Credentials
- id: hardcoded-password
patterns:
- pattern: |
password = "$PASSWORD"
- metavariable-regex:
metavariable: $PASSWORD
regex: .+
message: "Hardcoded password detected"
severity: ERROR
languages: [javascript, python, java]
# Insecure Random
- id: insecure-random
patterns:
- pattern: Math.random()
message: "Using Math.random() for security purposes is insecure"
severity: WARNING
languages: [javascript]
fix: crypto.randomBytes()
# Path Traversal
- id: path-traversal
patterns:
- pattern: |
fs.readFile($PATH)
- pattern-not: |
fs.readFile(path.join(__dirname, $PATH))
message: "Potential path traversal vulnerability"
severity: ERROR
languages: [javascript]
2. Semgrep CI/CD Integration
# .github/workflows/security-scan.yml
name: Security Scan
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
semgrep:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/cwe-top-25
generateSarif: true
- name: Upload SARIF results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: semgrep.sarif
- name: Semgrep Results
if: failure()
run: |
echo "Security vulnerabilities found!"
exit 1
Dependency Vulnerability Scanning
Scan dependencies for known vulnerabilities to prevent using insecure libraries.
Snyk Integration
1. Snyk Configuration
# .snyk
version: v1.19.0
# Ignore specific vulnerabilities
ignore:
SNYK-JS-LODASH-567746:
- '*':
reason: 'Low severity, fix planned for next sprint'
expires: '2025-12-31'
# Fail on severity levels
failOn: high
# Monitor settings
monitor: true
# Language settings
language-settings:
javascript:
packageManager: npm
targetFile: package.json
2. Snyk Pipeline Integration
pipeline {
agent any
environment {
SNYK_TOKEN = credentials('snyk-token')
}
stages {
stage('Dependency Scanning') {
steps {
script {
sh '''
# Install Snyk
npm install -g snyk
# Authenticate
snyk auth ${SNYK_TOKEN}
# Test for vulnerabilities
snyk test --severity-threshold=high --json > snyk-report.json || true
# Monitor project
snyk monitor --project-name=${JOB_NAME}
'''
// Parse results
def snykReport = readJSON file: 'snyk-report.json'
if (snykReport.vulnerabilities) {
def highVulns = snykReport.vulnerabilities.findAll { it.severity == 'high' }
def criticalVulns = snykReport.vulnerabilities.findAll { it.severity == 'critical' }
if (criticalVulns.size() > 0) {
error "Found ${criticalVulns.size()} critical vulnerabilities!"
}
if (highVulns.size() > 0) {
unstable "Found ${highVulns.size()} high severity vulnerabilities"
}
}
}
}
}
}
post {
always {
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.',
reportFiles: 'snyk-report.html',
reportName: 'Snyk Vulnerability Report'
])
}
}
}
OWASP Dependency-Check
1. Dependency-Check Configuration
<!-- pom.xml for Maven -->
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.4.0</version>
<configuration>
<format>ALL</format>
<failBuildOnCVSS>7</failBuildOnCVSS>
<suppressionFiles>
<suppressionFile>dependency-check-suppressions.xml</suppressionFile>
</suppressionFiles>
<nvdApiKey>${env.NVD_API_KEY}</nvdApiKey>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
2. Dependency-Check Pipeline
stage('OWASP Dependency Check') {
steps {
script {
sh '''
# Run OWASP Dependency Check
mvn dependency-check:check \
-DfailBuildOnCVSS=7 \
-DsuppressionFile=dependency-check-suppressions.xml
'''
}
}
post {
always {
dependencyCheckPublisher pattern: 'target/dependency-check-report.xml'
}
}
}
Container Security Scanning
Scan container images for vulnerabilities before deployment.
Trivy Integration
1. Trivy Scanning
#!/bin/bash
# trivy-scan.sh
IMAGE_NAME="$1"
SEVERITY_THRESHOLD="${2:-HIGH,CRITICAL}"
echo "Scanning image: ${IMAGE_NAME}"
# Run Trivy scan
trivy image \
--severity ${SEVERITY_THRESHOLD} \
--format json \
--output trivy-report.json \
${IMAGE_NAME}
# Check for vulnerabilities
VULN_COUNT=$(jq '.Results[].Vulnerabilities | length' trivy-report.json | awk '{s+=$1} END {print s}')
if [ "$VULN_COUNT" -gt 0 ]; then
echo "Found ${VULN_COUNT} vulnerabilities with severity ${SEVERITY_THRESHOLD}"
# Generate HTML report
trivy image \
--severity ${SEVERITY_THRESHOLD} \
--format template \
--template "@contrib/html.tpl" \
--output trivy-report.html \
${IMAGE_NAME}
exit 1
else
echo "No vulnerabilities found!"
exit 0
fi
2. Container Scan Pipeline
pipeline {
agent any
environment {
IMAGE_NAME = "myapp:${BUILD_NUMBER}"
REGISTRY = "ghcr.io"
}
stages {
stage('Build Image') {
steps {
sh "docker build -t ${REGISTRY}/${IMAGE_NAME} ."
}
}
stage('Scan with Trivy') {
steps {
script {
sh """
# Install Trivy
docker pull aquasec/trivy:latest
# Scan image
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
-v \$(pwd):/output \
aquasec/trivy:latest image \
--severity HIGH,CRITICAL \
--format json \
--output /output/trivy-report.json \
${REGISTRY}/${IMAGE_NAME}
"""
// Check results
def report = readJSON file: 'trivy-report.json'
def vulnerabilities = report.Results.collectMany { it.Vulnerabilities ?: [] }
if (vulnerabilities.size() > 0) {
def critical = vulnerabilities.count { it.Severity == 'CRITICAL' }
def high = vulnerabilities.count { it.Severity == 'HIGH' }
if (critical > 0) {
error "Found ${critical} CRITICAL vulnerabilities!"
} else if (high > 0) {
unstable "Found ${high} HIGH vulnerabilities"
}
}
}
}
}
stage('Push Image') {
when {
expression { currentBuild.result != 'FAILURE' }
}
steps {
sh "docker push ${REGISTRY}/${IMAGE_NAME}"
}
}
}
post {
always {
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.',
reportFiles: 'trivy-report.html',
reportName: 'Trivy Scan Report'
])
}
}
}
Anchore Engine
1. Anchore Configuration
# anchore-policy.json
{
"id": "security-policy",
"version": "1.0.0",
"name": "Security Policy",
"rules": [
{
"id": "critical-vulnerabilities",
"gate": "vulnerabilities",
"trigger": "package",
"action": "stop",
"params": [
{
"name": "severity_comparison",
"value": ">="
},
{
"name": "severity",
"value": "critical"
}
]
},
{
"id": "high-vulnerabilities",
"gate": "vulnerabilities",
"trigger": "package",
"action": "warn",
"params": [
{
"name": "severity_comparison",
"value": "="
},
{
"name": "severity",
"value": "high"
}
]
},
{
"id": "dockerfile-checks",
"gate": "dockerfile",
"trigger": "instruction",
"action": "warn",
"params": [
{
"name": "instruction",
"value": "FROM"
},
{
"name": "check",
"value": "not_in"
},
{
"name": "value",
"value": "scratch"
}
]
}
]
}
2. Anchore Pipeline
stage('Anchore Scan') {
steps {
script {
sh """
# Add image to Anchore
anchore-cli image add ${REGISTRY}/${IMAGE_NAME}
# Wait for analysis
anchore-cli image wait ${REGISTRY}/${IMAGE_NAME}
# Get vulnerability report
anchore-cli image vuln ${REGISTRY}/${IMAGE_NAME} all
# Check against policy
anchore-cli evaluate check ${REGISTRY}/${IMAGE_NAME} --policy security-policy
"""
}
}
}
Dynamic Application Security Testing (DAST)
DAST tests running applications to identify runtime vulnerabilities.
OWASP ZAP Integration
1. ZAP Baseline Scan
# .github/workflows/dast-scan.yml
name: DAST Scan
on:
push:
branches: [ main ]
schedule:
- cron: '0 2 * * *' # Daily at 2 AM
jobs:
zap-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Start application
run: |
docker-compose up -d
sleep 30 # Wait for app to be ready
- name: ZAP Baseline Scan
uses: zaproxy/action-[email protected]
with:
target: 'http://localhost:3000'
rules_file_name: '.zap/rules.tsv'
cmd_options: '-a -j'
- name: Upload ZAP Report
uses: actions/upload-artifact@v3
if: always()
with:
name: zap-report
path: |
report_html.html
report_json.json
2. ZAP Full Scan Pipeline
pipeline {
agent any
environment {
APP_URL = 'http://staging.example.com'
ZAP_PORT = '8090'
}
stages {
stage('Deploy to Staging') {
steps {
sh 'kubectl apply -f k8s/staging/'
sh 'kubectl wait --for=condition=ready pod -l app=myapp -n staging --timeout=300s'
}
}
stage('DAST with ZAP') {
steps {
script {
sh """
# Start ZAP in daemon mode
docker run -d --name zap \
-p ${ZAP_PORT}:${ZAP_PORT} \
-v \$(pwd):/zap/wrk:rw \
owasp/zap2docker-stable \
zap.sh -daemon -port ${ZAP_PORT} -config api.disablekey=true
# Wait for ZAP to start
sleep 30
# Run spider scan
docker exec zap zap-cli quick-scan -s xss,sqli ${APP_URL}
# Generate report
docker exec zap zap-cli report -o /zap/wrk/zap-report.html -f html
# Stop ZAP
docker stop zap
docker rm zap
"""
// Parse alerts
def report = readFile 'zap-report.html'
if (report.contains('High Risk Alert') || report.contains('Critical Risk Alert')) {
unstable 'High or Critical security issues found'
}
}
}
}
}
post {
always {
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.',
reportFiles: 'zap-report.html',
reportName: 'ZAP Security Report'
])
}
}
}
Nuclei Security Scanner
1. Nuclei Templates
# custom-security-checks.yaml
id: custom-api-security
info:
name: Custom API Security Checks
author: security-team
severity: high
description: Custom security checks for API endpoints
requests:
- method: GET
path:
- "{{BaseURL}}/api/users"
- "{{BaseURL}}/api/admin"
matchers-condition: and
matchers:
- type: status
status:
- 200
- type: word
words:
- "password"
- "secret"
- "token"
condition: or
extractors:
- type: regex
regex:
- '"password":"([^"]+)"'
- '"token":"([^"]+)"'
2. Nuclei Pipeline
#!/bin/bash
# nuclei-scan.sh
TARGET="$1"
TEMPLATES_DIR="nuclei-templates"
echo "Starting Nuclei scan for ${TARGET}"
# Run Nuclei scan
nuclei \
-u ${TARGET} \
-t ${TEMPLATES_DIR}/ \
-severity high,critical \
-json -o nuclei-report.json
# Check for findings
FINDINGS=$(jq '. | length' nuclei-report.json)
if [ "$FINDINGS" -gt 0 ]; then
echo "Found ${FINDINGS} security issues"
# Generate HTML report
nuclei \
-u ${TARGET} \
-t ${TEMPLATES_DIR}/ \
-severity high,critical \
-markdown-export nuclei-report.md
exit 1
else
echo "No security issues found"
exit 0
fi
Security Compliance Automation
Automate compliance checks for industry standards and regulations.
CIS Benchmarks
1. Docker Bench Security
#!/bin/bash
# docker-bench-security.sh
echo "Running Docker Bench Security..."
# Run Docker Bench
docker run --rm --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /etc:/etc:ro \
-v /usr/bin/containerd:/usr/bin/containerd:ro \
-v /usr/bin/runc:/usr/bin/runc:ro \
-v /usr/lib/systemd:/usr/lib/systemd:ro \
-v /var/lib:/var/lib:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--label docker_bench_security \
docker/docker-bench-security > docker-bench-report.txt
# Check for failures
WARNINGS=$(grep -c "\[WARN\]" docker-bench-report.txt)
FAILURES=$(grep -c "\[FAIL\]" docker-bench-report.txt)
echo "Docker Bench Results:"
echo "Warnings: ${WARNINGS}"
echo "Failures: ${FAILURES}"
if [ "$FAILURES" -gt 0 ]; then
echo "Critical security issues found!"
exit 1
fi
2. Kubernetes CIS Benchmark
#!/bin/bash
# k8s-cis-benchmark.sh
echo "Running Kubernetes CIS Benchmark..."
# Run kube-bench
kubectl apply -f - <<EOF
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench
spec:
template:
spec:
hostPID: true
containers:
- name: kube-bench
image: aquasec/kube-bench:latest
command: ["kube-bench"]
volumeMounts:
- name: var-lib-etcd
mountPath: /var/lib/etcd
readOnly: true
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
readOnly: true
- name: etc-systemd
mountPath: /etc/systemd
readOnly: true
- name: etc-kubernetes
mountPath: /etc/kubernetes
readOnly: true
restartPolicy: Never
volumes:
- name: var-lib-etcd
hostPath:
path: "/var/lib/etcd"
- name: var-lib-kubelet
hostPath:
path: "/var/lib/kubelet"
- name: etc-systemd
hostPath:
path: "/etc/systemd"
- name: etc-kubernetes
hostPath:
path: "/etc/kubernetes"
EOF
# Wait for job to complete
kubectl wait --for=condition=complete job/kube-bench --timeout=300s
# Get results
kubectl logs job/kube-bench > k8s-cis-report.txt
# Cleanup
kubectl delete job kube-bench
Security Reporting Dashboard
1. Security Metrics Collection
// security-metrics.js
class SecurityMetrics {
constructor() {
this.metrics = {
sast: {},
dast: {},
dependencies: {},
containers: {},
compliance: {}
};
}
async collectSASTMetrics() {
// Collect from SonarQube
const sonarMetrics = await this.fetchSonarQubeMetrics();
this.metrics.sast = {
vulnerabilities: sonarMetrics.vulnerabilities || 0,
securityHotspots: sonarMetrics.security_hotspots || 0,
securityRating: sonarMetrics.security_rating || 'A',
lastScan: new Date().toISOString()
};
}
async collectDependencyMetrics() {
// Collect from Snyk
const snykData = await this.fetchSnykData();
this.metrics.dependencies = {
critical: snykData.critical || 0,
high: snykData.high || 0,
medium: snykData.medium || 0,
low: snykData.low || 0,
totalVulnerabilities: snykData.total || 0,
lastScan: new Date().toISOString()
};
}
async collectContainerMetrics() {
// Collect from Trivy
const trivyData = await this.fetchTrivyData();
this.metrics.containers = {
critical: trivyData.critical || 0,
high: trivyData.high || 0,
scannedImages: trivyData.images || 0,
lastScan: new Date().toISOString()
};
}
generateSecurityScore() {
const weights = {
sast: 0.25,
dependencies: 0.30,
containers: 0.25,
dast: 0.20
};
let score = 100;
// Deduct points for vulnerabilities
score -= (this.metrics.sast.vulnerabilities * 2);
score -= (this.metrics.dependencies.critical * 10);
score -= (this.metrics.dependencies.high * 5);
score -= (this.metrics.containers.critical * 8);
score -= (this.metrics.containers.high * 4);
return Math.max(0, Math.min(100, score));
}
async generateReport() {
await Promise.all([
this.collectSASTMetrics(),
this.collectDependencyMetrics(),
this.collectContainerMetrics()
]);
const securityScore = this.generateSecurityScore();
return {
timestamp: new Date().toISOString(),
securityScore: securityScore,
grade: this.getGrade(securityScore),
metrics: this.metrics,
recommendations: this.generateRecommendations()
};
}
getGrade(score) {
if (score >= 90) return 'A';
if (score >= 80) return 'B';
if (score >= 70) return 'C';
if (score >= 60) return 'D';
return 'F';
}
generateRecommendations() {
const recommendations = [];
if (this.metrics.dependencies.critical > 0) {
recommendations.push({
priority: 'critical',
message: `Update ${this.metrics.dependencies.critical} critical dependency vulnerabilities immediately`,
action: 'Run: npm audit fix --force'
});
}
if (this.metrics.containers.critical > 0) {
recommendations.push({
priority: 'critical',
message: `Fix ${this.metrics.containers.critical} critical container vulnerabilities`,
action: 'Rebuild images with updated base images'
});
}
if (this.metrics.sast.securityHotspots > 10) {
recommendations.push({
priority: 'high',
message: `Review ${this.metrics.sast.securityHotspots} security hotspots in code`,
action: 'Review and fix in SonarQube'
});
}
return recommendations;
}
}
Key Takeaways
Security Testing Best Practices
- Shift Left: Integrate security testing early in development
- Automate Everything: Automate all security scans in CI/CD
- Fail Fast: Stop pipelines on critical vulnerabilities
- Continuous Monitoring: Monitor for new vulnerabilities continuously
- Remediation First: Prioritize fixing over finding
Implementation Strategy
- Start with Dependencies: Easiest to implement and high impact
- Add SAST: Static analysis for code-level issues
- Container Scanning: Scan all container images
- DAST for Runtime: Test deployed applications
- Compliance Automation: Automate compliance checks
Common Patterns
- Security Gates: Block deployments on critical issues
- Automated Remediation: Auto-fix known vulnerabilities
- Continuous Scanning: Regular security scans
- Security Metrics: Track security posture over time
- Incident Response: Automated alerting and response
Next Steps: Ready to implement quality gates? Continue to Section 4.4: Quality Gates Project to build a comprehensive quality assurance system.
Security testing integration is essential for delivering secure software. In the next section, we'll combine all quality and security measures into a complete quality gate system.