Skip to main content

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​

  1. Shift Left: Integrate security testing early in development
  2. Automate Everything: Automate all security scans in CI/CD
  3. Fail Fast: Stop pipelines on critical vulnerabilities
  4. Continuous Monitoring: Monitor for new vulnerabilities continuously
  5. 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.