1. Core Security Workflows
Main Security Pipeline
# .github/workflows/security.yml name: Security Pipeline on: push: branches: [ main, develop ] pull_request: branches: [ main, develop ] schedule: - cron: '0 0 * * 1' # Weekly security scans env: JAVA_VERSION: '17' MAVEN_VERSION: '3.8.6' jobs: sast: name: Static Application Security Testing runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ env.JAVA_VERSION }} distribution: 'temurin' cache: 'maven' - name: Run SpotBugs Security Analysis run: | mvn compile spotbugs:spotbugs -Dspotbugs.includeTests=true mvn spotbugs:check - name: Upload SpotBugs Report uses: actions/upload-artifact@v4 with: name: spotbugs-report path: target/spotbugs.xml retention-days: 30 sca: name: Software Composition Analysis runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ env.JAVA_VERSION }} distribution: 'temurin' cache: 'maven' - name: Run OWASP Dependency Check run: | mvn org.owasp:dependency-check-maven:check -Dformat=ALL - name: Upload Dependency Check Report uses: actions/upload-artifact@v4 with: name: dependency-check-report path: target/dependency-check-report.* retention-days: 30 - name: Run Snyk Security Scan uses: snyk/actions/maven@master env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: args: --severity-threshold=high --sarif-file-output=snyk.sarif - name: Upload Snyk SARIF Results uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: snyk.sarif secrets-detection: name: Secrets Detection runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Detect Secrets with Gitleaks uses: gitleaks/gitleaks-action@v2 env: GITLEAKS_VERSION: v8.18.0 with: config-path: .gitleaks.toml report-format: sarif redact: true - name: Upload Gitleaks Results uses: github/codeql-action/upload-sarif@v3 with: sarif_file: gitleaks-report.sarif codeql: name: CodeQL Analysis runs-on: ubuntu-latest permissions: security-events: write strategy: fail-fast: false matrix: language: [ 'java' ] steps: - uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} queries: security-and-quality - name: Autobuild uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" container-security: name: Container Security Scan runs-on: ubuntu-latest if: github.event_name == 'push' || github.event_name == 'schedule' steps: - uses: actions/checkout@v4 - name: Build Docker image run: docker build -t myapp:${{ github.sha }} . - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: image-ref: 'myapp:${{ github.sha }}' format: 'sarif' output: 'trivy-results.sarif' - name: Upload Trivy scan results uses: github/codeql-action/upload-sarif@v3 with: sarif_file: trivy-results.sarif security-summary: name: Security Summary runs-on: ubuntu-latest needs: [sast, sca, secrets-detection, codeql, container-security] if: always() steps: - name: Generate Security Report uses: actions/github-script@v7 with: script: | const { generateSecurityReport } = require('./.github/scripts/security-report.js'); await generateSecurityReport(github, context); 2. Advanced Security Workflows
SAST with Multiple Tools
# .github/workflows/sast-advanced.yml name: Advanced SAST on: pull_request: branches: [ main, develop ] push: branches: [ main ] jobs: spotbugs-security: name: SpotBugs Security runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - name: Run SpotBugs with Security Rules run: | mvn compile spotbugs:spotbugs -Dspotbugs.includeTests=true \ -Dspotbugs.effort=Max \ -Dspotbugs.threshold=Low \ -Dspotbugs.visitors=FindSecurityBugs - name: Convert to SARIF uses: spotbugs/[email protected] with: path: target/spotbugsXml.xml name: SpotBugs Security Report pmd-security: name: PMD Security Analysis runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - name: Run PMD Security Rules run: | mvn pmd:pmd -Dpmd.rulesets=category/java/security.xml - name: Upload PMD Report uses: actions/upload-artifact@v4 with: name: pmd-security-report path: target/pmd.xml checkstyle-security: name: CheckStyle Security Checks runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - name: Run CheckStyle with Security Configuration run: | mvn checkstyle:checkstyle -Dcheckstyle.config.location=google_checks.xml - name: Upload CheckStyle Report uses: actions/upload-artifact@v4 with: name: checkstyle-report path: target/checkstyle-result.xml sonarqube: name: SonarQube Analysis runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Java uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - name: Cache SonarQube packages uses: actions/cache@v4 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Run SonarQube Analysis run: | mvn sonar:sonar \ -Dsonar.projectKey=my-java-app \ -Dsonar.host.url=${{ secrets.SONAR_HOST_URL }} \ -Dsonar.login=${{ secrets.SONAR_TOKEN }} \ -Dsonar.java.spotbugs.reportPaths=target/spotbugsXml.xml \ -Dsonar.java.pmd.reportPaths=target/pmd.xml \ -Dsonar.java.checkstyle.reportPaths=target/checkstyle-result.xml
Dependency Vulnerability Management
# .github/workflows/dependency-security.yml name: Dependency Security on: push: branches: [ main ] pull_request: branches: [ main ] schedule: - cron: '0 6 * * 1' # Weekly on Monday jobs: dependency-audit: name: Dependency Audit runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' cache: 'maven' - name: Run OWASP Dependency Check run: | mvn org.owasp:dependency-check-maven:aggregate \ -Dformat=HTML \ -Dformat=JSON \ -Dformat=JUNIT \ -DfailBuildOnCVSS=7 \ -DsuppressionFile=suppressions.xml - name: Upload Dependency Check Reports uses: actions/upload-artifact@v4 with: name: dependency-check-reports path: | target/dependency-check-report.html target/dependency-check-report.json retention-days: 30 - name: Publish Dependency Check Results if: failure() uses: actions/github-script@v7 with: script: | github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: '🚨 Dependency check failed! Critical vulnerabilities found. Please review the report.' }) snyk-monitor: name: Snyk Continuous Monitoring runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run Snyk to check for vulnerabilities uses: snyk/actions/maven@master env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: args: --all-projects --detection-depth=6 - name: Snyk Monitor uses: snyk/actions/maven@master env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: args: monitor --all-projects license-compliance: name: License Compliance Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - name: Run License Maven Plugin run: | mvn license:add-third-party \ -Dlicense.thirdPartyFilename=THIRD-PARTY.txt \ -Dlicense.failOnMissing=true \ -Dlicense.failOnBlacklist=true - name: Check Forbidden Licenses run: | if grep -q "GPL" THIRD-PARTY.txt; then echo "❌ Forbidden GPL license detected" exit 1 fi out-of-date-dependencies: name: Out-of-Date Dependencies runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - name: Check for outdated dependencies run: | mvn versions:display-dependency-updates versions:display-plugin-updates - name: Create Dependency Update Issue if: failure() uses: actions/github-script@v7 with: script: | const { checkDependencyUpdates } = require('./.github/scripts/dependency-updates.js'); await checkDependencyUpdates(github, context); 3. Secret Management and Detection
# .github/workflows/secrets-management.yml name: Secrets Management on: push: branches: [ main, develop ] pull_request: branches: [ main, develop ] jobs: gitleaks: name: Gitleaks Secret Detection runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Run Gitleaks uses: gitleaks/gitleaks-action@v2 with: config-path: .gitleaks.toml report-format: sarif no-git: false redact: true verbose: true - name: Upload Gitleaks Results uses: github/codeql-action/upload-sarif@v3 with: sarif_file: gitleaks-report.sarif trufflehog: name: TruffleHog Deep Secret Scan runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Run TruffleHog uses: trufflesecurity/trufflehog@main with: path: ./ base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} extra_args: --debug --only-verified git-secrets: name: AWS git-secrets runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install git-secrets run: | git clone https://github.com/awslabs/git-secrets.git cd git-secrets && sudo make install - name: Configure git-secrets run: | git secrets --register-aws git secrets --scan-history secret-scan-summary: name: Secret Scan Summary runs-on: ubuntu-latest needs: [gitleaks, trufflehog, git-secrets] if: always() steps: - name: Generate Secret Scan Report uses: actions/github-script@v7 with: script: | const { generateSecretScanReport } = require('./.github/scripts/secret-scan-report.js'); await generateSecretScanReport(github, context); 4. Container Security
# .github/workflows/container-security.yml name: Container Security on: push: branches: [ main ] tags: [ 'v*' ] pull_request: branches: [ main ] jobs: trivy-scan: name: Trivy Vulnerability Scan runs-on: ubuntu-latest permissions: security-events: write steps: - uses: actions/checkout@v4 - name: Build Docker image run: docker build -t myapp:${{ github.sha }} . - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: image-ref: 'myapp:${{ github.sha }}' format: 'sarif' output: 'trivy-results.sarif' severity: 'CRITICAL,HIGH' - name: Upload Trivy scan results uses: github/codeql-action/upload-sarif@v3 with: sarif_file: trivy-results.sarif - name: Run Trivy filesystem scan uses: aquasecurity/trivy-action@master with: scan-type: 'fs' scan-ref: '.' format: 'table' exit-code: 1 dockerfile-lint: name: Dockerfile Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run Hadolint uses: hadolint/[email protected] with: dockerfile: Dockerfile failure-threshold: warning - name: Check Dockerfile best practices run: | docker run --rm -i hadolint/hadolint < Dockerfile image-hardening: name: Image Hardening Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build image run: docker build -t myapp:${{ github.sha }} . - name: Check for root user run: | if docker run myapp:${{ github.sha }} whoami | grep -q root; then echo "❌ Container running as root user" exit 1 fi - name: Check image size run: | SIZE=$(docker images myapp:${{ github.sha }} --format "{{.Size}}") echo "Image size: $SIZE" # Add size limits as needed cosign-sign: name: Cosign Image Signing runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 - name: Build image run: docker build -t myapp:${{ github.sha }} . - name: Install Cosign uses: sigstore/cosign-installer@main - name: Sign container image run: | cosign sign --key env://COSIGN_PRIVATE_KEY myapp:${{ github.sha }} env: COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }} COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} 5. Infrastructure as Code Security
# .github/workflows/iac-security.yml name: IaC Security on: push: paths: - '**.tf' - '**.tfvars' - 'kubernetes/**' pull_request: paths: - '**.tf' - '**.tfvars' - 'kubernetes/**' jobs: terraform-security: name: Terraform Security Scan runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run tfsec uses: aquasecurity/tfsec-action@master with: soft-fail: false output-format: sarif output-file: tfsec.sarif - name: Upload tfsec results uses: github/codeql-action/upload-sarif@v3 with: sarif_file: tfsec.sarif - name: Run Checkov uses: bridgecrewio/checkov-action@master with: directory: . framework: terraform output_format: sarif output_file_path: checkov.sarif - name: Upload Checkov results uses: github/codeql-action/upload-sarif@v3 with: sarif_file: checkov.sarif k8s-security: name: Kubernetes Security Scan runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run KubeSec uses: controlplaneio/kubectl@master with: args: kubesec scan kubernetes/ - name: Run Kubeaudit uses: controlplaneio/kubectl@master with: args: kubeaudit all -f kubernetes/ - name: Run Polaris uses: FairwindsOps/polaris@master with: audit-path: kubernetes/ output-format: sarif
6. Custom Security Scripts in Java
// .github/scripts/SecurityReportGenerator.java package com.github.scripts; import org.json.JSONArray; import org.json.JSONObject; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; public class SecurityReportGenerator { public static void main(String[] args) throws IOException { String workflow = args.length > 0 ? args[0] : "security"; generateSecurityReport(workflow); } public static void generateSecurityReport(String workflowName) throws IOException { Map<String, Object> report = new HashMap<>(); report.put("workflow", workflowName); report.put("timestamp", System.currentTimeMillis()); report.put("repository", System.getenv("GITHUB_REPOSITORY")); // Collect security findings Map<String, Object> findings = collectSecurityFindings(); report.put("findings", findings); // Calculate security score double securityScore = calculateSecurityScore(findings); report.put("securityScore", securityScore); report.put("securityLevel", getSecurityLevel(securityScore)); // Generate report String reportJson = new JSONObject(report).toString(2); Files.write(Paths.get("security-report.json"), reportJson.getBytes()); System.out.println("Security Report Generated:"); System.out.println(reportJson); } private static Map<String, Object> collectSecurityFindings() throws IOException { Map<String, Object> findings = new HashMap<>(); // Check for dependency vulnerabilities findings.put("dependencyVulnerabilities", checkDependencyVulnerabilities()); // Check for SAST issues findings.put("sastIssues", checkSastIssues()); // Check for secrets findings.put("secretsFound", checkSecrets()); // Check for container vulnerabilities findings.put("containerVulnerabilities", checkContainerVulnerabilities()); // Check for IaC issues findings.put("iacIssues", checkIacIssues()); return findings; } private static Map<String, Object> checkDependencyVulnerabilities() throws IOException { Map<String, Object> vulnerabilities = new HashMap<>(); // Parse OWASP Dependency Check report Path reportPath = Paths.get("target/dependency-check-report.json"); if (Files.exists(reportPath)) { String content = Files.readString(reportPath); JSONObject report = new JSONObject(content); JSONArray dependencies = report.getJSONArray("dependencies"); int critical = 0; int high = 0; int medium = 0; for (int i = 0; i < dependencies.length(); i++) { JSONObject dep = dependencies.getJSONObject(i); if (dep.has("vulnerabilities")) { JSONArray vulns = dep.getJSONArray("vulnerabilities"); for (int j = 0; j < vulns.length(); j++) { JSONObject vuln = vulns.getJSONObject(j); String severity = vuln.getString("severity"); switch (severity.toUpperCase()) { case "CRITICAL" -> critical++; case "HIGH" -> high++; case "MEDIUM" -> medium++; } } } } vulnerabilities.put("critical", critical); vulnerabilities.put("high", high); vulnerabilities.put("medium", medium); vulnerabilities.put("total", critical + high + medium); } return vulnerabilities; } private static Map<String, Object> checkSastIssues() throws IOException { Map<String, Object> sastIssues = new HashMap<>(); // Parse SpotBugs report Path spotbugsPath = Paths.get("target/spotbugsXml.xml"); if (Files.exists(spotbugsPath)) { // Parse XML and count issues by priority String content = Files.readString(spotbugsPath); // Implementation for parsing SpotBugs XML sastIssues.put("spotbugs", parseSpotBugsReport(content)); } // Parse PMD report Path pmdPath = Paths.get("target/pmd.xml"); if (Files.exists(pmdPath)) { String content = Files.readString(pmdPath); sastIssues.put("pmd", parsePmdReport(content)); } return sastIssues; } private static Map<String, Object> checkSecrets() { Map<String, Object> secrets = new HashMap<>(); // Implementation for checking secret detection results return secrets; } private static Map<String, Object> checkContainerVulnerabilities() { Map<String, Object> containerVulns = new HashMap<>(); // Implementation for checking container scan results return containerVulns; } private static Map<String, Object> checkIacIssues() { Map<String, Object> iacIssues = new HashMap<>(); // Implementation for checking IaC scan results return iacIssues; } private static double calculateSecurityScore(Map<String, Object> findings) { double score = 100.0; // Deduct points based on findings Map<String, Object> vulnerabilities = (Map<String, Object>) findings.get("dependencyVulnerabilities"); int criticalVulns = (int) vulnerabilities.getOrDefault("critical", 0); int highVulns = (int) vulnerabilities.getOrDefault("high", 0); score -= criticalVulns * 10; score -= highVulns * 5; // Ensure score doesn't go below 0 return Math.max(score, 0); } private static String getSecurityLevel(double score) { if (score >= 90) return "EXCELLENT"; if (score >= 80) return "GOOD"; if (score >= 70) return "FAIR"; if (score >= 60) return "POOR"; return "CRITICAL"; } private static Map<String, Object> parseSpotBugsReport(String xmlContent) { Map<String, Object> spotbugs = new HashMap<>(); // Implementation for parsing SpotBugs XML return spotbugs; } private static Map<String, Object> parsePmdReport(String xmlContent) { Map<String, Object> pmd = new HashMap<>(); // Implementation for parsing PMD XML return pmd; } } 7. Security Configuration Files
OWASP Dependency Check Configuration
<!-- dependency-check-suppressions.xml --> <?xml version="1.0" encoding="UTF-8"?> <suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd"> <!-- Suppress false positives --> <suppress> <notes><![CDATA[False positive per issue #1234]]></notes> <cve>CVE-2021-44228</cve> </suppress> <!-- Suppress by package --> <suppress> <notes><![CDATA[Suppress log4j in test scope]]></notes> <packageUrl regex="true">^pkg:maven/org\.apache\.logging\.log4j.*@.*$</packageUrl> <scope>test</scope> </suppress> </suppressions>
Gitleaks Configuration
# .gitleaks.toml title = "Gitleaks configuration" [[rules]] description = "AWS Access Key" regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' tags = ["key", "AWS"] [[rules]] description = "GitHub Personal Access Token" regex = '''ghp_[0-9a-zA-Z]{36}''' tags = ["key", "GitHub"] [[rules]] description = "Generic API Key" regex = '''[a|A][p|P][i|I][_]?[k|K][e|E][y|Y].*[''|"][0-9a-zA-Z]{32,45}[''|"]''' tags = ["key", "API"] [[rules]] description = "Password in URL" regex = '''[a-zA-Z]{3,10}://[^/\\s:@]{3,20}:[^/\\s:@]{3,20}@.{1,100}["'\\s]''' tags = ["key", "password"] # Allow list specific files [[allowlist]] files = [''] SpotBugs Security Configuration
<!-- spotbugs-security.xml --> <FindBugsFilter> <!-- Focus on security-related bugs --> <Match> <Bug category="SECURITY"/> </Match> <Match> <Bug pattern="SQL_INJECTION"/> </Match> <Match> <Bug pattern="COMMAND_INJECTION"/> </Match> <Match> <Bug pattern="PATH_TRAVERSAL_IN"/> </Match> <Match> <Bug pattern="XSS_REQUEST_PARAMETER_TO_SEND_ERROR"/> </Match> <!-- Exclude test code from some checks --> <Match> <Source name="~.*Test\.java"/> <Bug pattern="HARD_CODE_PASSWORD"/> </Match> </FindBugsFilter>
8. Maven Security Configuration
<!-- pom.xml security plugins configuration --> <build> <plugins> <!-- OWASP Dependency Check --> <plugin> <groupId>org.owasp</groupId> <artifactId>dependency-check-maven</artifactId> <version>8.2.1</version> <configuration> <format>ALL</format> <failBuildOnCVSS>7</failBuildOnCVSS> <suppressionFile>dependency-check-suppressions.xml</suppressionFile> <skipTestScope>false</skipTestScope> </configuration> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin> <!-- SpotBugs Security --> <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> <version>4.7.3.0</version> <configuration> <includeFilterFile>spotbugs-security.xml</includeFilterFile> <excludeFilterFile>spotbugs-excludes.xml</excludeFilterFile> <effort>Max</effort> <threshold>Low</threshold> <failOnError>true</failOnError> </configuration> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin> <!-- PMD Security Rules --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> <version>3.20.0</version> <configuration> <rulesets> <ruleset>category/java/security.xml</ruleset> </rulesets> <failOnViolation>true</failOnViolation> <printFailingErrors>true</printFailingErrors> </configuration> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin> <!-- CheckStyle Security --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> <version>3.2.1</version> <configuration> <configLocation>google_checks.xml</configLocation> <failOnViolation>true</failOnViolation> </configuration> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
Key Security Workflow Features:
- Comprehensive SAST: Multiple static analysis tools (SpotBugs, PMD, CheckStyle)
- SCA Integration: OWASP Dependency Check, Snyk, and license compliance
- Secret Detection: Gitleaks, TruffleHog, and AWS git-secrets
- Container Security: Trivy vulnerability scanning and Docker best practices
- IaC Security: Terraform and Kubernetes security scanning
- CodeQL Integration: GitHub's advanced code analysis
- Automated Reporting: Custom security reports and score calculation
- CI/CD Integration: Seamless integration with development workflows
These workflows provide enterprise-grade security scanning for Java projects, ensuring code quality, dependency security, and compliance with security best practices.