GitHub Code Scanning provides automated security vulnerability scanning and code quality analysis directly within your GitHub repositories. For Java projects, it integrates with tools like CodeQL, SonarCloud, and third-party scanners to identify issues early in the development lifecycle.
Understanding GitHub Code Scanning Architecture
Key Components:
- CodeQL - GitHub's semantic code analysis engine
- SARIF (Static Analysis Results Interchange Format) support
- Security alerts and vulnerability reporting
- Pull request integration with status checks
- Custom query support for project-specific rules
Core Implementation Patterns
1. GitHub Actions Configuration
Configure comprehensive code scanning workflows for Java projects.
.github/workflows/codeql-analysis.yml:
name: "CodeQL Advanced Analysis" on: push: branches: [ main, develop, feature/** ] pull_request: branches: [ main ] schedule: - cron: '0 0 * * 0' # Weekly scan env: JAVA_VERSION: '17' MAVEN_OPTS: '--add-opens java.base/java.lang=ALL-UNNAMED' jobs: analyze: name: Analyze Java Code runs-on: ubuntu-latest permissions: security-events: write actions: read contents: read strategy: fail-fast: false matrix: language: [ 'java' ] steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Java uses: actions/setup-java@v3 with: java-version: ${{ env.JAVA_VERSION }} distribution: 'temurin' cache: 'maven' - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} queries: security-and-quality,security-extended config-file: ./.github/codeql/codeql-config.yml - name: Build with Maven run: | mvn -B clean compile test-compile -DskipTests # Build for autobuilder mvn -B package -DskipTests --file pom.xml - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 with: category: "/language:${{ matrix.language }}" output: codeql-results.sarif upload-database: true - name: Upload SARIF results uses: github/codeql-action/upload-sarif@v2 with: sarif_file: codeql-results.sarif wait-for-processing: true security-scan: name: Security Vulnerability Scan runs-on: ubuntu-latest permissions: security-events: write contents: read steps: - name: Checkout code uses: actions/checkout@v4 - name: Run OWASP Dependency Check uses: dependency-check/Dependency-Check@main with: project: 'my-java-app' path: '.' format: 'SARIF' out: 'dependency-check-results.sarif' - name: Upload Dependency Check Results uses: github/codeql-action/upload-sarif@v2 with: sarif_file: dependency-check-results.sarif quality-scan: name: Code Quality Analysis runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' - name: Run SpotBugs Analysis run: | mvn -B spotbugs:spotbugs mvn -B spotbugs:spotbugs -Dspotbugs.includeTests=true - name: Run PMD Analysis run: | mvn -B pmd:pmd mvn -B pmd:pmd -Dpmd.includeTests=true - name: Generate SARIF from SpotBugs uses: advanced-security/spotbugs-sarif@v1 with: spotbugs-version: '4.7.3' path: target/spotbugs.xml output: spotbugs-results.sarif - name: Upload Quality Results uses: github/codeql-action/upload-sarif@v2 with: sarif_file: spotbugs-results.sarif .github/codeql/codeql-config.yml:
name: "Custom Java CodeQL Configuration" paths: - src/main/java - src/test/java paths-ignore: - '**/generated/**' - '**/target/**' - '**/test/resources/**' - '**/build/**' queries: - uses: security-and-quality - uses: security-extended - name: Custom Java queries uses: ./.github/codeql/custom-queries query-filters: - exclude: id: java/example/too-many-parameters - include: problem.severity: error languages: - java packs: - codeql/java-queries
2. CodeQL Custom Queries
Implement project-specific CodeQL queries for custom security and quality rules.
.github/codeql/custom-queries/java/security/SqlInjection.ql:
/** * @name Potential SQL injection * @description Detects potential SQL injection vulnerabilities in Java code * @kind path-problem * @problem.severity error * @security-severity 9.8 * @precision high * @id java/sql-injection * @tags security * external/cwe/cwe-089 */ import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking import SqlInjectionQuery from SqlInjectionQuery query, Expr sink where query.hasFlowTo(sink) select sink, "Potential SQL injection vulnerability", sink, "User input reaches this SQL query execution point"
.github/codeql/custom-queries/java/quality/ThreadSafeCollection.ql:
/** * @name Non-thread-safe collection usage * @description Detects potential thread safety issues with collections * @kind problem * @problem.severity warning * @precision medium * @id java/non-thread-safe-collection * @tags maintainability * reliability */ import java from MethodAccess access, Method method where method.hasName("add") or method.hasName("remove") or method.hasName("put") or method.hasName("get") and method.getDeclaringType().hasQualifiedName("java.util", "ArrayList") or method.getDeclaringType().hasQualifiedName("java.util", "HashMap") or method.getDeclaringType().hasQualifiedName("java.util", "HashSet") and exists(Field field | field.getType() = method.getDeclaringType() | access.getQualifier() = field.getAnAccess() | not field.isVolatile() | not field.isSynchronized() ) select access, "Non-thread-safe collection operation in potentially shared context" .github/codeql/custom-queries/java/security/UnvalidatedRedirect.ql:
/** * @name Unvalidated redirect * @description Detects potential unvalidated redirect vulnerabilities * @kind path-problem * @problem.severity warning * @security-severity 6.1 * @precision medium * @id java/unvalidated-redirect * @tags security * external/cwe/cwe-601 */ import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking class RedirectSink extends DataFlow::Node { RedirectSink() { exists(MethodAccess ma | ma.getMethod().hasName("sendRedirect") and ma.getMethod().getDeclaringType().hasQualifiedName("javax.servlet.http", "HttpServletResponse") and this.asExpr() = ma.getArgument(0) ) } } class UserInputSource extends DataFlow::Node { UserInputSource() { exists(Method m | m.hasName("getParameter") and m.getDeclaringType().hasQualifiedName("javax.servlet.http", "HttpServletRequest") and this.asExpr() = m.getACall() ) } } from UserInputSource source, RedirectSink sink, DataFlow::PathNode path where DataFlow::localFlow(path, source, sink) select sink, "Potential unvalidated redirect from user input", path 3. Security Scanning Integration
Integrate multiple security scanning tools with GitHub Code Scanning.
Security Scanning Service:
@Service @Slf4j public class SecurityScanningService { private final CodeQLAnalysisService codeqlService; private final DependencyScanService dependencyScanService; private final SastScanService sastScanService; private final GitHubSecurityClient githubClient; public SecurityScanningService(CodeQLAnalysisService codeqlService, DependencyScanService dependencyScanService, SastScanService sastScanService, GitHubSecurityClient githubClient) { this.codeqlService = codeqlService; this.dependencyScanService = dependencyScanService; this.sastScanService = sastScanService; this.githubClient = githubClient; } /** * Perform comprehensive security scan */ public SecurityScanResult performSecurityScan(String repository, String commitHash) { log.info("Starting security scan for repository: {}, commit: {}", repository, commitHash); SecurityScanResult result = new SecurityScanResult(); result.setRepository(repository); result.setCommitHash(commitHash); result.setScanDate(Instant.now()); try { // Run CodeQL analysis CodeQLResult codeqlResult = codeqlService.analyzeRepository(repository, commitHash); result.setCodeqlResults(codeqlResult); // Run dependency vulnerability scan DependencyScanResult dependencyResult = dependencyScanService.scanDependencies(repository); result.setDependencyResults(dependencyResult); // Run SAST scan SastScanResult sastResult = sastScanService.performSastScan(repository); result.setSastResults(sastResult); // Upload results to GitHub Security uploadSecurityResults(result); result.setStatus(SecurityScanStatus.COMPLETED); log.info("Security scan completed for repository: {}", repository); } catch (Exception e) { log.error("Security scan failed for repository: {}", repository, e); result.setStatus(SecurityScanStatus.FAILED); result.setErrorMessage(e.getMessage()); } return result; } /** * Check security gate status */ public SecurityGateStatus checkSecurityGate(String repository, String commitHash) { SecurityScanResult scanResult = performSecurityScan(repository, commitHash); SecurityGateStatus gateStatus = new SecurityGateStatus(); gateStatus.setRepository(repository); gateStatus.setCommitHash(commitHash); gateStatus.setCheckTime(Instant.now()); // Evaluate security criteria gateStatus.setPassed(evaluateSecurityCriteria(scanResult)); gateStatus.setCriticalIssues(scanResult.getCriticalIssues()); gateStatus.setHighIssues(scanResult.getHighIssues()); gateStatus.setMediumIssues(scanResult.getMediumIssues()); gateStatus.setBlockingIssues(identifyBlockingIssues(scanResult)); // Update GitHub status check updateGitHubSecurityStatus(repository, commitHash, gateStatus); return gateStatus; } /** * Generate security report */ public SecurityReport generateSecurityReport(String repository, String branch, int days) { List<SecurityScanResult> historicalScans = getHistoricalScans(repository, branch, days); SecurityTrends trends = analyzeSecurityTrends(historicalScans); SecurityReport report = new SecurityReport(); report.setRepository(repository); report.setBranch(branch); report.setReportDate(Instant.now()); report.setAnalysisPeriod(days); report.setCurrentSecurityState(assessCurrentSecurityState(historicalScans)); report.setSecurityTrends(trends); report.setRecommendations(generateSecurityRecommendations(trends)); report.setRiskAssessment(assessSecurityRisk(historicalScans)); return report; } private boolean evaluateSecurityCriteria(SecurityScanResult result) { // Block on critical security issues if (result.getCriticalIssues() > 0) { return false; } // Block on high severity issues exceeding threshold if (result.getHighIssues() > getSecurityThreshold("high_issues")) { return false; } // Check for specific high-risk vulnerability patterns if (hasHighRiskVulnerabilities(result)) { return false; } return true; } private void uploadSecurityResults(SecurityScanResult result) { // Convert to SARIF format SarifReport sarifReport = convertToSarif(result); // Upload to GitHub Security githubClient.uploadSecurityScanResults( result.getRepository(), result.getCommitHash(), sarifReport ); log.info("Uploaded security results for commit: {}", result.getCommitHash()); } private void updateGitHubSecurityStatus(String repository, String commitHash, SecurityGateStatus gateStatus) { GitHubStatus status = new GitHubStatus(); status.setContext("security/gate"); status.setState(gateStatus.isPassed() ? "success" : "failure"); status.setDescription(generateSecurityStatusDescription(gateStatus)); status.setTargetUrl(generateSecurityReportUrl(repository, commitHash)); githubClient.createStatus(repository, commitHash, status); } // Additional security scanning methods... } @Data public class SecurityScanResult { private String repository; private String commitHash; private Instant scanDate; private SecurityScanStatus status; private String errorMessage; private CodeQLResult codeqlResults; private DependencyScanResult dependencyResults; private SastScanResult sastResults; private int criticalIssues; private int highIssues; private int mediumIssues; private int lowIssues; private List<SecurityIssue> blockingIssues; public int getTotalIssues() { return criticalIssues + highIssues + mediumIssues + lowIssues; } public boolean hasCriticalIssues() { return criticalIssues > 0; } } @Data public class SecurityGateStatus { private String repository; private String commitHash; private Instant checkTime; private boolean passed; private int criticalIssues; private int highIssues; private int mediumIssues; private List<SecurityIssue> blockingIssues; private String rejectionReason; public boolean isBlocked() { return !passed || !blockingIssues.isEmpty(); } } @Data public class SecurityReport { private String repository; private String branch; private Instant reportDate; private int analysisPeriod; private SecurityState currentSecurityState; private SecurityTrends securityTrends; private List<SecurityRecommendation> recommendations; private RiskAssessment riskAssessment; public enum SecurityState { EXCELLENT, GOOD, FAIR, POOR, CRITICAL } } 4. Dependency Vulnerability Scanning
Implement comprehensive dependency vulnerability scanning.
Dependency Scanning Service:
@Service @Slf4j public class DependencyScanService { private final OWASPDependencyCheckService owaspService; private final SnykScanService snykService; private final GitHubAdvisoryService advisoryService; private final VulnerabilityDatabase vulnerabilityDb; public DependencyScanService(OWASPDependencyCheckService owaspService, SnykScanService snykService, GitHubAdvisoryService advisoryService, VulnerabilityDatabase vulnerabilityDb) { this.owaspService = owaspService; this.snykService = snykService; this.advisoryService = advisoryService; this.vulnerabilityDb = vulnerabilityDb; } /** * Scan project dependencies for vulnerabilities */ public DependencyScanResult scanDependencies(String repository) { log.info("Starting dependency scan for repository: {}", repository); DependencyScanResult result = new DependencyScanResult(); result.setRepository(repository); result.setScanDate(Instant.now()); try { // Parse project dependencies List<ProjectDependency> dependencies = parseDependencies(repository); result.setDependencies(dependencies); // Scan with multiple vulnerability databases List<VulnerabilityFinding> owaspFindings = owaspService.scanDependencies(dependencies); List<VulnerabilityFinding> snykFindings = snykService.scanDependencies(dependencies); List<VulnerabilityFinding> githubFindings = advisoryService.checkAdvisories(dependencies); // Merge and deduplicate findings List<VulnerabilityFinding> allFindings = mergeFindings( owaspFindings, snykFindings, githubFindings); result.setVulnerabilities(allFindings); // Calculate risk metrics calculateRiskMetrics(result); // Generate remediation advice result.setRemediationAdvice(generateRemediationAdvice(allFindings)); result.setStatus(ScanStatus.COMPLETED); log.info("Dependency scan completed for repository: {}", repository); } catch (Exception e) { log.error("Dependency scan failed for repository: {}", repository, e); result.setStatus(ScanStatus.FAILED); result.setErrorMessage(e.getMessage()); } return result; } /** * Check for vulnerable dependencies in pull request */ public PullRequestDependencyCheck checkPullRequestDependencies(PullRequestEvent event) { List<ProjectDependency> currentDependencies = parseDependencies(event.getBaseCommit()); List<ProjectDependency> prDependencies = parseDependencies(event.getHeadCommit()); PullRequestDependencyCheck check = new PullRequestDependencyCheck(); check.setPullRequestId(event.getPrNumber()); check.setRepository(event.getRepository()); check.setCheckDate(Instant.now()); // Identify new and updated dependencies List<DependencyChange> changes = identifyDependencyChanges( currentDependencies, prDependencies); check.setDependencyChanges(changes); // Check new dependencies for vulnerabilities List<VulnerabilityFinding> newVulnerabilities = scanNewDependencies(changes); check.setNewVulnerabilities(newVulnerabilities); // Check for dependency upgrades that fix vulnerabilities List<VulnerabilityFix> vulnerabilityFixes = identifyVulnerabilityFixes(changes); check.setVulnerabilityFixes(vulnerabilityFixes); // Generate PR comment String prComment = generateDependencyCheckComment(check); check.setPrComment(prComment); return check; } /** * Generate dependency security report */ public DependencySecurityReport generateSecurityReport(String repository) { DependencyScanResult scanResult = scanDependencies(repository); List<DependencyScanResult> historicalScans = getHistoricalDependencyScans(repository, 90); DependencySecurityReport report = new DependencySecurityReport(); report.setRepository(repository); report.setReportDate(Instant.now()); report.setCurrentState(scanResult); report.setHistoricalTrends(analyzeDependencyTrends(historicalScans)); report.setRiskAssessment(assessDependencyRisk(scanResult)); report.setActionPlan(generateDependencyActionPlan(scanResult)); return report; } /** * Monitor dependencies for new vulnerabilities */ public List<VulnerabilityAlert> monitorDependencies(String repository) { DependencyScanResult lastScan = getLastDependencyScan(repository); List<VulnerabilityFinding> newVulnerabilities = checkForNewVulnerabilities(lastScan); List<VulnerabilityAlert> alerts = newVulnerabilities.stream() .map(this::createVulnerabilityAlert) .collect(Collectors.toList()); // Send alerts if new vulnerabilities found if (!alerts.isEmpty()) { sendVulnerabilityAlerts(repository, alerts); } return alerts; } private List<ProjectDependency> parseDependencies(String repository) { // Parse Maven pom.xml or Gradle build files try { Path pomPath = Paths.get(repository, "pom.xml"); if (Files.exists(pomPath)) { return parseMavenDependencies(pomPath); } Path gradlePath = Paths.get(repository, "build.gradle"); if (Files.exists(gradlePath)) { return parseGradleDependencies(gradlePath); } throw new DependencyScanException("No dependency file found in repository: " + repository); } catch (Exception e) { throw new DependencyScanException("Failed to parse dependencies", e); } } private List<ProjectDependency> parseMavenDependencies(Path pomPath) throws Exception { List<ProjectDependency> dependencies = new ArrayList<>(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(pomPath.toFile()); NodeList dependencyNodes = document.getElementsByTagName("dependency"); for (int i = 0; i < dependencyNodes.getLength(); i++) { Element dependencyElement = (Element) dependencyNodes.item(i); String groupId = getElementText(dependencyElement, "groupId"); String artifactId = getElementText(dependencyElement, "artifactId"); String version = getElementText(dependencyElement, "version"); String scope = getElementText(dependencyElement, "scope"); if (groupId != null && artifactId != null && version != null) { ProjectDependency dependency = new ProjectDependency(); dependency.setGroupId(groupId); dependency.setArtifactId(artifactId); dependency.setVersion(version); dependency.setScope(scope != null ? scope : "compile"); dependencies.add(dependency); } } return dependencies; } // Additional dependency scanning methods... } @Data public class DependencyScanResult { private String repository; private Instant scanDate; private ScanStatus status; private String errorMessage; private List<ProjectDependency> dependencies; private List<VulnerabilityFinding> vulnerabilities; private int criticalVulnerabilities; private int highVulnerabilities; private int mediumVulnerabilities; private int lowVulnerabilities; private DependencyRiskLevel overallRisk; private List<RemediationAdvice> remediationAdvice; public int getTotalVulnerabilities() { return criticalVulnerabilities + highVulnerabilities + mediumVulnerabilities + lowVulnerabilities; } public boolean hasCriticalVulnerabilities() { return criticalVulnerabilities > 0; } } @Data public class ProjectDependency { private String groupId; private String artifactId; private String version; private String scope; private String type; // direct, transitive private List<LicenseInfo> licenses; public String getCoordinates() { return groupId + ":" + artifactId + ":" + version; } } @Data public class VulnerabilityFinding { private String id; private String cveId; private String description; private VulnerabilitySeverity severity; private double cvssScore; private List<String> affectedVersions; private String fixedVersion; private List<String> references; private ProjectDependency affectedDependency; private Instant publishedDate; private List<String> attackVectors; public boolean isFixAvailable() { return fixedVersion != null && !fixedVersion.isEmpty(); } } 5. SAST (Static Application Security Testing) Integration
Integrate multiple SAST tools for comprehensive security analysis.
SAST Scanning Service:
@Service @Slf4j public class SastScanService { private final SpotBugsService spotBugsService; private final PMDService pmdService; private final CheckstyleService checkstyleService; private final SonarQubeService sonarQubeService; private final SARIFConverter sarifConverter; public SastScanService(SpotBugsService spotBugsService, PMDService pmdService, CheckstyleService checkstyleService, SonarQubeService sonarQubeService, SARIFConverter sarifConverter) { this.spotBugsService = spotBugsService; this.pmdService = pmdService; this.checkstyleService = checkstyleService; this.sonarQubeService = sonarQubeService; this.sarifConverter = sarifConverter; } /** * Perform comprehensive SAST scan */ public SastScanResult performSastScan(String repository) { log.info("Starting SAST scan for repository: {}", repository); SastScanResult result = new SastScanResult(); result.setRepository(repository); result.setScanDate(Instant.now()); try { // Run multiple SAST tools SpotBugsResult spotBugsResult = spotBugsService.analyze(repository); PMDResult pmdResult = pmdService.analyze(repository); CheckstyleResult checkstyleResult = checkstyleService.analyze(repository); SonarQubeResult sonarResult = sonarQubeService.analyze(repository); // Merge results result.setSpotBugsResults(spotBugsResult); result.setPmdResults(pmdResult); result.setCheckstyleResults(checkstyleResult); result.setSonarQubeResults(sonarResult); // Aggregate findings List<SastFinding> allFindings = aggregateFindings( spotBugsResult, pmdResult, checkstyleResult, sonarResult); result.setFindings(allFindings); // Calculate metrics calculateSastMetrics(result); // Generate SARIF report for GitHub SarifReport sarifReport = sarifConverter.convertToSarif(allFindings); result.setSarifReport(sarifReport); result.setStatus(ScanStatus.COMPLETED); log.info("SAST scan completed for repository: {}", repository); } catch (Exception e) { log.error("SAST scan failed for repository: {}", repository, e); result.setStatus(ScanStatus.FAILED); result.setErrorMessage(e.getMessage()); } return result; } /** * Scan specific security patterns */ public SecurityPatternScanResult scanSecurityPatterns(String repository) { SecurityPatternScanResult result = new SecurityPatternScanResult(); result.setRepository(repository); result.setScanDate(Instant.now()); // Scan for specific security anti-patterns result.setSqlInjection(findSqlInjectionVulnerabilities(repository)); result.setXssVulnerabilities(findXssVulnerabilities(repository)); result.setInsecureDeserialization(findInsecureDeserialization(repository)); result.setHardcodedSecrets(findHardcodedSecrets(repository)); result.setInsecureCrypto(findInsecureCryptoUsage(repository)); result.setMassAssignment(findMassAssignmentVulnerabilities(repository)); return result; } /** * Generate SAST quality gate */ public SastQualityGate evaluateQualityGate(String repository, String commitHash) { SastScanResult scanResult = performSastScan(repository); SastQualityGate gate = new SastQualityGate(); gate.setRepository(repository); gate.setCommitHash(commitHash); gate.setEvaluationDate(Instant.now()); // Evaluate against quality criteria gate.setPassed(evaluateSastCriteria(scanResult)); gate.setCriticalFindings(scanResult.getCriticalFindings()); gate.setHighFindings(scanResult.getHighFindings()); gate.setBlockingIssues(identifyBlockingSastIssues(scanResult)); gate.setQualityScore(calculateSastQualityScore(scanResult)); return gate; } private List<SastFinding> aggregateFindings(SpotBugsResult spotBugs, PMDResult pmd, CheckstyleResult checkstyle, SonarQubeResult sonar) { List<SastFinding> findings = new ArrayList<>(); findings.addAll(convertSpotBugsFindings(spotBugs)); findings.addAll(convertPmdFindings(pmd)); findings.addAll(convertCheckstyleFindings(checkstyle)); findings.addAll(convertSonarFindings(sonar)); // Deduplicate findings return deduplicateFindings(findings); } private List<SastFinding> findSqlInjectionVulnerabilities(String repository) { List<SastFinding> sqlInjectionFindings = new ArrayList<>(); // Pattern 1: String concatenation in SQL queries sqlInjectionFindings.addAll(findPattern( repository, "Statement.executeQuery.*\\+.*", "Potential SQL injection via string concatenation" )); // Pattern 2: PreparedStatement without parameterization sqlInjectionFindings.addAll(findPattern( repository, "PreparedStatement.*setString.*\\+", "Potential SQL injection in PreparedStatement" )); // Pattern 3: JPA with unparameterized queries sqlInjectionFindings.addAll(findPattern( repository, "@Query\\(\".*\\+.*\"\\)", "Potential SQL injection in JPA @Query" )); return sqlInjectionFindings; } private List<SastFinding> findHardcodedSecrets(String repository) { List<SastFinding> secretFindings = new ArrayList<>(); // AWS keys secretFindings.addAll(findPattern( repository, "AKIA[0-9A-Z]{16}", "Hardcoded AWS access key" )); // API keys secretFindings.addAll(findPattern( repository, "api[_-]?key[\\s]*=[\\s]*['\"][^'\"\\s]+['\"]", "Hardcoded API key" )); // Database passwords secretFindings.addAll(findPattern( repository, "password[\\s]*=[\\s]*['\"][^'\"\\s]+['\"]", "Hardcoded database password" )); // JWT secrets secretFindings.addAll(findPattern( repository, "JWT.*secret[\\s]*=[\\s]*['\"][^'\"\\s]+['\"]", "Hardcoded JWT secret" )); return secretFindings; } // Additional SAST scanning methods... } @Data public class SastScanResult { private String repository; private Instant scanDate; private ScanStatus status; private String errorMessage; private SpotBugsResult spotBugsResults; private PMDResult pmdResults; private CheckstyleResult checkstyleResults; private SonarQubeResult sonarQubeResults; private List<SastFinding> findings; private SarifReport sarifReport; private int criticalFindings; private int highFindings; private int mediumFindings; private int lowFindings; private int infoFindings; private double qualityScore; private double securityScore; private double maintainabilityScore; public int getTotalFindings() { return criticalFindings + highFindings + mediumFindings + lowFindings + infoFindings; } } @Data public class SastFinding { private String id; private String tool; // spotbugs, pmd, checkstyle, sonarqube private String ruleId; private String category; // security, performance, maintainability, reliability private FindingSeverity severity; private String filePath; private int lineNumber; private int columnNumber; private String message; private String description; private List<String> remediationSteps; private List<String> references; private double confidence; private String cweId; private String owaspCategory; public String getFormattedLocation() { return String.format("%s:%d:%d", filePath, lineNumber, columnNumber); } } 6. GitHub Security Integration
Integrate with GitHub Security features and APIs.
GitHub Security Client:
```java
@Service
@Slf4j
public class GitHubSecurityClient {
private final GitHubClient githubClient;
private final String apiToken;
private final ObjectMapper objectMapper;
public GitHubSecurityClient(GitHubConfig config) { this.githubClient = new GitHubClient(config); this.apiToken = config.getApiToken(); this.objectMapper = new ObjectMapper(); } /** * Upload SARIF results to GitHub Security */ public void uploadSarifResults(String repository, String commitHash, SarifReport sarifReport) { try { String sarifJson = objectMapper.writeValueAsString(sarifReport); GitHubApiResponse response = githubClient.post( String.format("/repos/%s/code-scanning/sarifs", repository), Map.of( "commit_sha", commitHash, "ref", "refs/heads/main", "sarif", sarifJson, "checkout_uri", "file:///github/workspace", "started_at", Instant.now().toString(), "tool_name", "custom-security-scan" ) ); if (response.isSuccessful()) { log.info("Successfully uploaded SARIF results for commit: {}", commitHash); } else { log.error("Failed to upload SARIF results: {}", response.getErrorMessage()); } } catch (Exception e) { log.error("Failed to upload SARIF results to GitHub Security", e); throw new GitHubSecurityException("SARIF upload failed", e); } } /** * Get code scanning alerts for repository */ public List<CodeScanningAlert> getCodeScanningAlerts(String repository, String branch) { try { GitHubApiResponse response = githubClient.get( String.format("/repos/%s/code-scanning/alerts?ref=%s", repository, branch) ); if (response.isSuccessful()) { return objectMapper.readValue( response.getBody(), new TypeReference<List<CodeScanningAlert>>() {} ); } else { log.error("Failed to fetch code scanning alerts: {}", response.getErrorMessage()); return Collections.emptyList(); } } catch (Exception e) { log.error("Failed to fetch code scanning alerts", e); return Collections.emptyList(); } } /** * Update code scanning alert status */ public void updateAlertStatus(String repository, int alertId, AlertStatus status, String reason) { try { GitHubApiResponse response = githubClient.patch( String.format("/repos/%s/code-scanning/alerts/%d", repository, alertId), Map.of( "state", status.name().toLowerCase(), "dismissed_reason", reason ) ); if (response.isSuccessful()) { log.info("Updated alert {} status to {}", alertId, status); } else { log.error("Failed to update alert status: {}", response.getErrorMessage()); } } catch (Exception e) { log.error("Failed to update alert status", e); } } /** * Create security advisory */ public SecurityAdvisory createSecurityAdvisory(String repository, SecurityAdvisory advisory) { try { GitHubApiResponse response = githubClient.post( String.format("/repos/%s/security-advisories", repository), advisory ); if (response.isSuccessful()) { return objectMapper.readValue(response.getBody(), SecurityAdvisory.class); } else { log.error("Failed to create security advisory: {}", response.getErrorMessage()); throw new GitHubSecurityException("Failed to create security advisory"); } } catch (Exception e) { log.error("Failed to create security advisory", e); throw new GitHubSecurityException("Security advisory creation failed", e); } } /** * Get dependency graph insights */ public DependencyGraphInsights getDependencyGraphInsights(String repository) { try { GitHubApiResponse response = githubClient.get( String.format("/repos/%s/dependency-graph/insights", repository) ); if (response.isSuccessful()) Secure Java Dependency Management, Vulnerability Scanning & Software Supply Chain Protection (SBOM, SCA, CI Security & License Compliance)
https://macronepal.com/blog/github-code-scanning-in-java-complete-guide/
Explains GitHub Code Scanning for Java using tools like CodeQL to automatically analyze source code and detect security vulnerabilities directly inside CI/CD pipelines before deployment.
https://macronepal.com/blog/license-compliance-in-java-comprehensive-guide/
Explains software license compliance in Java projects, ensuring dependencies follow legal requirements (MIT, Apache, GPL, etc.) and preventing license violations in enterprise software.
https://macronepal.com/blog/container-security-for-java-uncovering-vulnerabilities-with-grype/
Explains using Grype to scan Java container images and filesystems for known CVEs in OS packages and application dependencies to improve container security.
https://macronepal.com/blog/syft-sbom-generation-in-java-comprehensive-software-bill-of-materials-for-jvm-applications/
Explains using Syft to generate SBOMs (Software Bill of Materials) for Java applications, listing all dependencies, libraries, and components for supply chain transparency.
https://macronepal.com/blog/comprehensive-dependency-analysis-generating-and-scanning-sboms-with-trivy-for-java/
Explains using Trivy to generate SBOMs and scan Java dependencies and container images for vulnerabilities, integrating security checks into CI/CD pipelines.
https://macronepal.com/blog/dependabot-for-java-in-java/
Explains GitHub Dependabot for Java projects, which automatically detects vulnerable dependencies and creates pull requests to update them securely.
https://macronepal.com/blog/parasoft-jtest-in-java-comprehensive-guide-to-code-analysis-and-testing/
Explains Parasoft Jtest, a static analysis and testing tool for Java that helps detect bugs, security issues, and code quality problems early in development.
https://macronepal.com/blog/snyk-open-source-in-java-comprehensive-dependency-vulnerability-management-2/
Explains Snyk Open Source for Java, which continuously scans dependencies for vulnerabilities and provides automated fix suggestions and monitoring.
https://macronepal.com/blog/owasp-dependency-check-in-java-complete-vulnerability-scanning-guide/
Explains OWASP Dependency-Check, which scans Java dependencies against the National Vulnerability Database (NVD) to detect known security vulnerabilities.
https://macronepal.com/blog/securing-your-dependencies-a-java-developers-guide-to-whitesource-mend-bolt/
Explains Mend (WhiteSource) Bolt for Java, a dependency management and SCA tool that provides vulnerability detection, license compliance, and security policy enforcement in enterprise environments.