Security Automation in CI/CD Pipelines

Security Automation in CI/CD Pipelines

Automation enables consistent security enforcement without slowing development velocity. Security automation should be transparent when possible, providing clear feedback when intervention is required. Progressive automation allows teams to adopt security practices gradually, building confidence before enforcing strict policies.

Pipeline security gates must balance security with development velocity. Early pipeline stages should run fast security checks providing immediate feedback. Later stages can run comprehensive scans. Parallel execution of security checks minimizes pipeline duration. Clear error messages help developers understand and fix security issues quickly.

// Example: Jenkins pipeline with progressive security checks
pipeline {
    agent any
    
    environment {
        DOCKER_REGISTRY = 'registry.company.com'
        IMAGE_NAME = "${DOCKER_REGISTRY}/${env.JOB_NAME}:${env.BUILD_ID}"
        SECURITY_THRESHOLD = credentials('security-thresholds')
    }
    
    stages {
        stage('Quick Security Checks') {
            parallel {
                stage('Dockerfile Lint') {
                    steps {
                        script {
                            sh '''
                                # Hadolint for Dockerfile best practices
                                docker run --rm -i hadolint/hadolint < Dockerfile
                            '''
                        }
                    }
                }
                
                stage('Secrets Scan') {
                    steps {
                        script {
                            sh '''
                                # Scan for hardcoded secrets
                                docker run --rm -v $(pwd):/src \
                                    trufflesecurity/trufflehog:latest \
                                    filesystem /src --json | \
                                    jq -e '.[] | select(.verified == true)' && \
                                    echo "SECRETS FOUND!" && exit 1 || \
                                    echo "No secrets detected"
                            '''
                        }
                    }
                }
                
                stage('SAST Quick Scan') {
                    steps {
                        script {
                            sh '''
                                # Quick static analysis
                                docker run --rm -v $(pwd):/src \
                                    semgrep/semgrep-agent:latest \
                                    --config=auto --error
                            '''
                        }
                    }
                }
            }
        }
        
        stage('Build Image') {
            steps {
                script {
                    // Build with security labels
                    sh """
                        docker build \
                            --label security.scan.required=true \
                            --label build.id=${env.BUILD_ID} \
                            --label vcs.ref=${env.GIT_COMMIT} \
                            --label build.date=\$(date -u +%Y-%m-%dT%H:%M:%SZ) \
                            -t ${IMAGE_NAME} .
                    """
                }
            }
        }
        
        stage('Comprehensive Security Analysis') {
            parallel {
                stage('Vulnerability Scan') {
                    steps {
                        script {
                            sh """
                                # Trivy scan with cache
                                docker run --rm \
                                    -v /var/run/docker.sock:/var/run/docker.sock \
                                    -v ${WORKSPACE}/trivy-cache:/tmp/trivy-cache \
                                    aquasec/trivy:latest image \
                                    --cache-dir /tmp/trivy-cache \
                                    --format json \
                                    --output vulnerability-report.json \
                                    ${IMAGE_NAME}
                                
                                # Process results
                                python3 process_vulnerabilities.py \
                                    --report vulnerability-report.json \
                                    --thresholds ${SECURITY_THRESHOLD}
                            """
                        }
                    }
                    post {
                        always {
                            publishHTML([
                                allowMissing: false,
                                alwaysLinkToLastBuild: true,
                                keepAll: true,
                                reportDir: '.',
                                reportFiles: 'vulnerability-report.html',
                                reportName: 'Vulnerability Scan Report'
                            ])
                        }
                    }
                }
                
                stage('License Compliance') {
                    steps {
                        script {
                            sh """
                                # License scanning
                                docker run --rm \
                                    -v /var/run/docker.sock:/var/run/docker.sock \
                                    licensefinder/license_finder \
                                    --docker-image ${IMAGE_NAME}
                            """
                        }
                    }
                }
                
                stage('Security Benchmarks') {
                    steps {
                        script {
                            sh """
                                # CIS Docker Benchmark
                                docker run --rm \
                                    -v /var/run/docker.sock:/var/run/docker.sock \
                                    docker/docker-bench-security \
                                    -i ${IMAGE_NAME}
                            """
                        }
                    }
                }
            }
        }
        
        stage('Security Approval') {
            when {
                expression { 
                    return env.BRANCH_NAME == 'main' || 
                           env.BRANCH_NAME == 'develop' 
                }
            }
            steps {
                script {
                    // Automated approval based on scan results
                    def approved = sh(
                        script: '''
                            python3 security_approval.py \
                                --vulnerability-report vulnerability-report.json \
                                --policy-file security-policy.yaml
                        ''',
                        returnStatus: true
                    ) == 0
                    
                    if (!approved) {
                        // Request manual approval for policy violations
                        input message: 'Security policy violations detected. Approve deployment?',
                              parameters: [
                                  string(name: 'JUSTIFICATION',
                                         description: 'Provide justification for override'),
                                  string(name: 'APPROVER_EMAIL',
                                         description: 'Your email for audit trail')
                              ]
                        
                        // Log override
                        sh """
                            echo '${env.BUILD_ID},${params.APPROVER_EMAIL},${params.JUSTIFICATION}' \
                                >> security-overrides.log
                        """
                    }
                }
            }
        }
        
        stage('Sign and Push') {
            steps {
                script {
                    // Sign image with Cosign
                    sh """
                        # Generate ephemeral keys
                        cosign generate-key-pair
                        
                        # Sign image
                        cosign sign --key cosign.key ${IMAGE_NAME}
                        
                        # Attach SBOM
                        syft ${IMAGE_NAME} -o spdx-json > sbom.json
                        cosign attach sbom --sbom sbom.json ${IMAGE_NAME}
                        
                        # Push to registry
                        docker push ${IMAGE_NAME}
                    """
                }
            }
        }
    }
    
    post {
        always {
            // Security metrics collection
            sh '''
                # Collect and send metrics
                python3 collect_security_metrics.py \
                    --build-id ${BUILD_ID} \
                    --duration ${currentBuild.durationString} \
                    --status ${currentBuild.result}
            '''
            
            // Clean up
            sh 'docker rmi ${IMAGE_NAME} || true'
        }
        
        failure {
            // Security incident notification
            emailext (
                subject: "Security Check Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
                body: '''${SCRIPT, template="security-failure-email.template"}''',
                to: '${SECURITY_TEAM_EMAIL}'
            )
        }
    }
}