Jenkins Pipeline Integration

Jenkins Pipeline Integration

Jenkins remains popular in enterprise environments. Here's a comprehensive Jenkinsfile implementing container security scanning:

pipeline {
    agent any
    
    options {
        buildDiscarder(logRotator(numToKeepStr: '10'))
        timeout(time: 30, unit: 'MINUTES')
    }
    
    environment {
        DOCKER_REGISTRY = 'registry.company.com'
        IMAGE_NAME = "${env.JOB_NAME.toLowerCase()}"
        IMAGE_TAG = "${DOCKER_REGISTRY}/${IMAGE_NAME}:${env.BUILD_NUMBER}"
        TRIVY_VERSION = '0.45.0'
        SNYK_TOKEN = credentials('snyk-api-token')
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
                script {
                    env.GIT_COMMIT_SHORT = sh(
                        script: "git rev-parse --short HEAD",
                        returnStdout: true
                    ).trim()
                }
            }
        }
        
        stage('Build Image') {
            steps {
                script {
                    docker.build(IMAGE_TAG, 
                        "--build-arg BUILD_NUMBER=${env.BUILD_NUMBER} " +
                        "--build-arg GIT_COMMIT=${env.GIT_COMMIT_SHORT} " +
                        "--label build.number=${env.BUILD_NUMBER} " +
                        "--label git.commit=${env.GIT_COMMIT_SHORT} " +
                        ".")
                }
            }
        }
        
        stage('Security Scans') {
            parallel {
                stage('Trivy Scan') {
                    steps {
                        script {
                            // Run Trivy in Docker
                            def trivyExitCode = sh(
                                script: """
                                    docker run --rm \
                                        -v /var/run/docker.sock:/var/run/docker.sock \
                                        -v ${WORKSPACE}:/workspace \
                                        aquasec/trivy:${TRIVY_VERSION} \
                                        image --format json \
                                        --output /workspace/trivy-report.json \
                                        ${IMAGE_TAG}
                                """,
                                returnStatus: true
                            )
                            
                            // Parse results
                            def trivyReport = readJSON file: 'trivy-report.json'
                            def criticalCount = 0
                            def highCount = 0
                            
                            trivyReport.Results.each { result ->
                                result.Vulnerabilities?.each { vuln ->
                                    if (vuln.Severity == 'CRITICAL') criticalCount++
                                    if (vuln.Severity == 'HIGH') highCount++
                                }
                            }
                            
                            echo "Trivy found ${criticalCount} CRITICAL and ${highCount} HIGH vulnerabilities"
                            
                            // Generate HTML report
                            publishHTML(target: [
                                allowMissing: false,
                                alwaysLinkToLastBuild: true,
                                keepAll: true,
                                reportDir: '.',
                                reportFiles: 'trivy-report.json',
                                reportName: 'Trivy Security Report'
                            ])
                            
                            if (criticalCount > 0) {
                                error("Build failed due to ${criticalCount} critical vulnerabilities")
                            }
                        }
                    }
                }
                
                stage('Snyk Scan') {
                    steps {
                        script {
                            sh """
                                docker run --rm \
                                    -e SNYK_TOKEN=${SNYK_TOKEN} \
                                    -v /var/run/docker.sock:/var/run/docker.sock \
                                    -v ${WORKSPACE}:/project \
                                    snyk/snyk:docker \
                                    container test ${IMAGE_TAG} \
                                    --json > snyk-report.json || true
                            """
                            
                            def snykReport = readJSON file: 'snyk-report.json'
                            
                            if (snykReport.vulnerabilities) {
                                echo "Snyk found ${snykReport.vulnerabilities.size()} vulnerabilities"
                                
                                // Create issues for high/critical vulnerabilities
                                snykReport.vulnerabilities.findAll { 
                                    it.severity in ['high', 'critical'] 
                                }.each { vuln ->
                                    echo "High/Critical: ${vuln.title} in ${vuln.packageName}"
                                }
                            }
                        }
                    }
                }
                
                stage('License Check') {
                    steps {
                        script {
                            sh """
                                docker run --rm \
                                    -v /var/run/docker.sock:/var/run/docker.sock \
                                    aquasec/trivy:${TRIVY_VERSION} \
                                    image --license-full \
                                    --format json \
                                    ${IMAGE_TAG} > license-report.json
                            """
                            
                            def licenses = readJSON file: 'license-report.json'
                            
                            // Check for problematic licenses
                            def problematicLicenses = ['GPL', 'AGPL', 'LGPL']
                            def found = []
                            
                            licenses.Results?.each { result ->
                                result.Licenses?.each { license ->
                                    if (problematicLicenses.any { 
                                        license.Name?.contains(it) 
                                    }) {
                                        found.add(license)
                                    }
                                }
                            }
                            
                            if (found.size() > 0) {
                                echo "WARNING: Found ${found.size()} potentially problematic licenses"
                                found.each { echo "  - ${it.Name}" }
                            }
                        }
                    }
                }
            }
        }
        
        stage('Generate SBOM') {
            steps {
                script {
                    sh """
                        # Generate SBOM using Syft
                        docker run --rm \
                            -v /var/run/docker.sock:/var/run/docker.sock \
                            anchore/syft:latest \
                            ${IMAGE_TAG} \
                            -o cyclonedx-json > sbom.json
                    """
                    
                    archiveArtifacts artifacts: 'sbom.json', 
                                     fingerprint: true
                }
            }
        }
        
        stage('Security Gate') {
            steps {
                script {
                    def proceed = input(
                        message: 'Security scan complete. Review results and proceed?',
                        parameters: [
                            booleanParam(
                                name: 'OVERRIDE_SECURITY',
                                defaultValue: false,
                                description: 'Override security failures?'
                            )
                        ]
                    )
                    
                    if (!proceed && env.BRANCH_NAME == 'main') {
                        error("Security gate failed - deployment blocked")
                    }
                }
            }
        }
        
        stage('Push Image') {
            when {
                branch pattern: "(main|develop)", comparator: "REGEXP"
            }
            steps {
                script {
                    docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry-creds') {
                        docker.image(IMAGE_TAG).push()
                        docker.image(IMAGE_TAG).push('latest')
                    }
                }
            }
        }
    }
    
    post {
        always {
            // Clean up
            sh "docker rmi ${IMAGE_TAG} || true"
            
            // Archive reports
            archiveArtifacts artifacts: '*-report.json', 
                             allowEmptyArchive: true,
                             fingerprint: true
            
            // Send notifications
            emailext(
                subject: "Security Scan Results: ${currentBuild.fullDisplayName}",
                body: '''${FILE,path="security-summary.md"}''',
                to: '${DEFAULT_RECIPIENTS}',
                attachmentsPattern: '*-report.json'
            )
        }
        
        failure {
            slackSend(
                color: 'danger',
                message: "Security scan failed for ${env.JOB_NAME} #${env.BUILD_NUMBER}"
            )
        }
    }
}