CodeQL SARIF in Java: Complete Guide for Security Analysis Results

Introduction to CodeQL and SARIF

CodeQL is GitHub's semantic code analysis engine that lets you query code as data. SARIF (Static Analysis Results Interchange Format) is a standard format for outputting static analysis results. Combining CodeQL with SARIF enables comprehensive security analysis with standardized reporting.

Key Features

  • CodeQL Analysis: Semantic code analysis for security vulnerabilities
  • SARIF Output: Standardized format for analysis results
  • GitHub Integration: Native integration with GitHub Advanced Security
  • Custom Queries: Extensible with custom security queries
  • Multi-language Support: Analyzes Java, JavaScript, Python, C#, etc.

Implementation Guide

Dependencies

Add to your pom.xml:

<properties>
<codeql.version>2.13.5</codeql.version>
<sarif.version>2.1.0</sarif.version>
<jackson.version>2.15.2</jackson.version>
</properties>
<dependencies>
<!-- SARIF SDK -->
<dependency>
<groupId>com.microsoft.sarif</groupId>
<artifactId>sarif-sdk</artifactId>
<version>${sarif.version}</version>
</dependency>
<!-- Jackson for JSON processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- CodeQL database access -->
<dependency>
<groupId>com.github.github</groupId>
<artifactId>codeql</artifactId>
<version>${codeql.version}</version>
</dependency>
<!-- For file operations -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
</dependency>
<!-- For logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>
</dependencies>

Core SARIF Implementation

SARIF Report Generator

package com.example.codeql.sarif;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.microsoft.sarif.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
public class SarifReportGenerator {
private static final Logger logger = LoggerFactory.getLogger(SarifReportGenerator.class);
private final ObjectMapper objectMapper;
private final SarifSchema210 sarif;
public SarifReportGenerator() {
this.objectMapper = new ObjectMapper();
this.sarif = new SarifSchema210();
initializeSarif();
}
private void initializeSarif() {
sarif.setVersion("2.1.0");
sarif.setSchema("https://json.schemastore.org/sarif-2.1.0.json");
List<Run> runs = new ArrayList<>();
sarif.setRuns(runs);
}
public void createRun(String toolName, String toolVersion) {
Run run = new Run();
// Tool information
Tool tool = new Tool();
ToolComponent driver = new ToolComponent();
driver.setName(toolName);
driver.setVersion(toolVersion);
driver.setInformationUri("https://github.com/your-org/your-tool");
tool.setDriver(driver);
run.setTool(tool);
// Results list
List<Result> results = new ArrayList<>();
run.setResults(results);
// Resources (files analyzed)
List<Artifact> artifacts = new ArrayList<>();
run.setArtifacts(artifacts);
sarif.getRuns().add(run);
}
public void addArtifact(Path filePath, String contents) {
if (sarif.getRuns().isEmpty()) return;
Run run = sarif.getRuns().get(0);
List<Artifact> artifacts = run.getArtifacts();
if (artifacts == null) {
artifacts = new ArrayList<>();
run.setArtifacts(artifacts);
}
Artifact artifact = new Artifact();
artifact.setLocation(new ArtifactLocation()
.withUri(filePath.toUri().toString()));
if (contents != null) {
artifact.setContents(new ArtifactContent()
.withText(contents));
}
artifacts.add(artifact);
}
public void addSecurityIssue(SecurityIssue issue) {
if (sarif.getRuns().isEmpty()) return;
Run run = sarif.getRuns().get(0);
List<Result> results = run.getResults();
if (results == null) {
results = new ArrayList<>();
run.setResults(results);
}
Result result = new Result();
result.setRuleId(issue.getRuleId());
result.setLevel(issue.getLevel());
result.setMessage(new Message()
.withText(issue.getMessage()));
// Location
List<Location> locations = new ArrayList<>();
Location location = new Location();
location.setPhysicalLocation(new PhysicalLocation()
.withArtifactLocation(new ArtifactLocation()
.withUri(issue.getFilePath().toUri().toString()))
.withRegion(new Region()
.withStartLine(issue.getStartLine())
.withStartColumn(issue.getStartColumn())
.withEndLine(issue.getEndLine())
.withEndColumn(issue.getEndColumn())));
locations.add(location);
result.setLocations(locations);
// Properties for additional metadata
PropertyBag properties = new PropertyBag();
properties.setProperty("security-severity", issue.getSecuritySeverity());
properties.setProperty("precision", issue.getPrecision());
properties.setProperty("tags", issue.getTags());
properties.setProperty("cwe", issue.getCweIds());
result.setProperties(properties);
results.add(result);
// Ensure rule is defined
addRule(issue);
}
private void addRule(SecurityIssue issue) {
Run run = sarif.getRuns().get(0);
ToolComponent driver = run.getTool().getDriver();
List<ReportingDescriptor> rules = driver.getRules();
if (rules == null) {
rules = new ArrayList<>();
driver.setRules(rules);
}
// Check if rule already exists
boolean ruleExists = rules.stream()
.anyMatch(rule -> rule.getId().equals(issue.getRuleId()));
if (!ruleExists) {
ReportingDescriptor rule = new ReportingDescriptor();
rule.setId(issue.getRuleId());
rule.setName(issue.getRuleName());
rule.setShortDescription(new Message()
.withText(issue.getShortDescription()));
rule.setFullDescription(new Message()
.withText(issue.getFullDescription()));
rule.setDefaultConfiguration(new ReportingConfiguration()
.withLevel(issue.getLevel()));
rule.setProperties(new PropertyBag()
.withProperty("security-severity", issue.getSecuritySeverity())
.withProperty("precision", issue.getPrecision())
.withProperty("tags", issue.getTags())
.withProperty("cwe", issue.getCweIds()));
rules.add(rule);
}
}
public void writeToFile(Path outputPath) throws IOException {
objectMapper.writerWithDefaultPrettyPrinter()
.writeValue(outputPath.toFile(), sarif);
logger.info("SARIF report written to: {}", outputPath);
}
public String toJson() throws IOException {
return objectMapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(sarif);
}
}

Security Issue Model

package com.example.codeql.sarif;
import java.nio.file.Path;
import java.util.List;
public class SecurityIssue {
private final String ruleId;
private final String ruleName;
private final String level; // "error", "warning", "note"
private final String message;
private final String shortDescription;
private final String fullDescription;
private final Path filePath;
private final int startLine;
private final int startColumn;
private final int endLine;
private final int endColumn;
private final String securitySeverity; // "critical", "high", "medium", "low"
private final String precision; // "high", "medium", "low"
private final List<String> tags;
private final List<String> cweIds;
public SecurityIssue(Builder builder) {
this.ruleId = builder.ruleId;
this.ruleName = builder.ruleName;
this.level = builder.level;
this.message = builder.message;
this.shortDescription = builder.shortDescription;
this.fullDescription = builder.fullDescription;
this.filePath = builder.filePath;
this.startLine = builder.startLine;
this.startColumn = builder.startColumn;
this.endLine = builder.endLine;
this.endColumn = builder.endColumn;
this.securitySeverity = builder.securitySeverity;
this.precision = builder.precision;
this.tags = builder.tags;
this.cweIds = builder.cweIds;
}
// Getters
public String getRuleId() { return ruleId; }
public String getRuleName() { return ruleName; }
public String getLevel() { return level; }
public String getMessage() { return message; }
public String getShortDescription() { return shortDescription; }
public String getFullDescription() { return fullDescription; }
public Path getFilePath() { return filePath; }
public int getStartLine() { return startLine; }
public int getStartColumn() { return startColumn; }
public int getEndLine() { return endLine; }
public int getEndColumn() { return endColumn; }
public String getSecuritySeverity() { return securitySeverity; }
public String getPrecision() { return precision; }
public List<String> getTags() { return tags; }
public List<String> getCweIds() { return cweIds; }
public static class Builder {
private String ruleId;
private String ruleName;
private String level = "warning";
private String message;
private String shortDescription;
private String fullDescription;
private Path filePath;
private int startLine = 1;
private int startColumn = 1;
private int endLine = 1;
private int endColumn = 1;
private String securitySeverity = "medium";
private String precision = "medium";
private List<String> tags = List.of();
private List<String> cweIds = List.of();
public Builder ruleId(String ruleId) {
this.ruleId = ruleId;
return this;
}
public Builder ruleName(String ruleName) {
this.ruleName = ruleName;
return this;
}
public Builder level(String level) {
this.level = level;
return this;
}
public Builder message(String message) {
this.message = message;
return this;
}
public Builder shortDescription(String shortDescription) {
this.shortDescription = shortDescription;
return this;
}
public Builder fullDescription(String fullDescription) {
this.fullDescription = fullDescription;
return this;
}
public Builder filePath(Path filePath) {
this.filePath = filePath;
return this;
}
public Builder location(int startLine, int startColumn, int endLine, int endColumn) {
this.startLine = startLine;
this.startColumn = startColumn;
this.endLine = endLine;
this.endColumn = endColumn;
return this;
}
public Builder securitySeverity(String securitySeverity) {
this.securitySeverity = securitySeverity;
return this;
}
public Builder precision(String precision) {
this.precision = precision;
return this;
}
public Builder tags(List<String> tags) {
this.tags = tags;
return this;
}
public Builder cweIds(List<String> cweIds) {
this.cweIds = cweIds;
return this;
}
public SecurityIssue build() {
return new SecurityIssue(this);
}
}
}

CodeQL Integration

CodeQL Analyzer

package com.example.codeql.analyzer;
import com.example.codeql.sarif.SecurityIssue;
import com.example.codeql.sarif.SarifReportGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
public class CodeQLAnalyzer {
private static final Logger logger = LoggerFactory.getLogger(CodeQLAnalyzer.class);
private final Path codeqlDatabasePath;
private final Path sourceCodePath;
private final SarifReportGenerator reportGenerator;
public CodeQLAnalyzer(Path codeqlDatabasePath, Path sourceCodePath) {
this.codeqlDatabasePath = codeqlDatabasePath;
this.sourceCodePath = sourceCodePath;
this.reportGenerator = new SarifReportGenerator();
}
public void analyze() throws IOException, InterruptedException {
logger.info("Starting CodeQL analysis...");
// Initialize SARIF report
reportGenerator.createRun("CodeQL Java Analyzer", "1.0.0");
// Run CodeQL analysis
List<SecurityIssue> issues = runCodeQLAnalysis();
// Add issues to SARIF report
for (SecurityIssue issue : issues) {
reportGenerator.addSecurityIssue(issue);
}
logger.info("Analysis completed. Found {} security issues.", issues.size());
}
private List<SecurityIssue> runCodeQLAnalysis() throws IOException, InterruptedException {
List<SecurityIssue> issues = new ArrayList<>();
// In a real implementation, this would execute the CodeQL CLI
// For demonstration, we'll simulate analysis results
// Simulate SQL injection findings
issues.addAll(analyzeSqlInjection());
// Simulate XSS findings
issues.addAll(analyzeXss());
// Simulate insecure deserialization
issues.addAll(analyzeInsecureDeserialization());
// Simulate hardcoded credentials
issues.addAll(analyzeHardcodedCredentials());
return issues;
}
private List<SecurityIssue> analyzeSqlInjection() throws IOException {
List<SecurityIssue> issues = new ArrayList<>();
// Find Java files with potential SQL injection
Files.walk(sourceCodePath)
.filter(path -> path.toString().endsWith(".java"))
.forEach(path -> {
try {
List<String> lines = Files.readAllLines(path);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (containsSqlInjectionPattern(line)) {
issues.add(createSqlInjectionIssue(path, i + 1, line));
}
}
} catch (IOException e) {
logger.warn("Failed to analyze file: {}", path, e);
}
});
return issues;
}
private boolean containsSqlInjectionPattern(String line) {
String lowerLine = line.toLowerCase();
return (lowerLine.contains("statement.executequery") ||
lowerLine.contains("statement.executeupdate") ||
lowerLine.contains("connection.createstatement")) &&
line.contains("+") && line.contains("\"");
}
private SecurityIssue createSqlInjectionIssue(Path filePath, int lineNumber, String line) {
return new SecurityIssue.Builder()
.ruleId("java/sql-injection")
.ruleName("SQL injection vulnerability")
.level("error")
.message("Potential SQL injection vulnerability. Use prepared statements instead of string concatenation.")
.shortDescription("SQL injection vulnerability")
.fullDescription("This code constructs an SQL query using string concatenation, which can lead to SQL injection attacks. Use parameterized queries or prepared statements instead.")
.filePath(filePath)
.location(lineNumber, 1, lineNumber, line.length())
.securitySeverity("high")
.precision("high")
.tags(Arrays.asList("security", "external/cwe/cwe-89", "owasp-top-ten/a1"))
.cweIds(Arrays.asList("CWE-89"))
.build();
}
private List<SecurityIssue> analyzeXss() throws IOException {
List<SecurityIssue> issues = new ArrayList<>();
Files.walk(sourceCodePath)
.filter(path -> path.toString().endsWith(".java"))
.forEach(path -> {
try {
List<String> lines = Files.readAllLines(path);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (containsXssPattern(line)) {
issues.add(createXssIssue(path, i + 1, line));
}
}
} catch (IOException e) {
logger.warn("Failed to analyze file: {}", path, e);
}
});
return issues;
}
private boolean containsXssPattern(String line) {
String lowerLine = line.toLowerCase();
return (lowerLine.contains("response.getwriter") ||
lowerLine.contains("response.setcontenttype") &&
lowerLine.contains("text/html")) &&
line.contains("+") && line.contains("request.getparameter");
}
private SecurityIssue createXssIssue(Path filePath, int lineNumber, String line) {
return new SecurityIssue.Builder()
.ruleId("java/xss")
.ruleName("Cross-site scripting vulnerability")
.level("error")
.message("Potential XSS vulnerability. User input should be properly encoded before output.")
.shortDescription("Cross-site scripting vulnerability")
.fullDescription("This code directly outputs user input to an HTML response without proper encoding, which can lead to cross-site scripting attacks.")
.filePath(filePath)
.location(lineNumber, 1, lineNumber, line.length())
.securitySeverity("high")
.precision("medium")
.tags(Arrays.asList("security", "external/cwe/cwe-79", "owasp-top-ten/a7"))
.cweIds(Arrays.asList("CWE-79"))
.build();
}
private List<SecurityIssue> analyzeInsecureDeserialization() throws IOException {
List<SecurityIssue> issues = new ArrayList<>();
Files.walk(sourceCodePath)
.filter(path -> path.toString().endsWith(".java"))
.forEach(path -> {
try {
List<String> lines = Files.readAllLines(path);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (containsInsecureDeserializationPattern(line)) {
issues.add(createInsecureDeserializationIssue(path, i + 1, line));
}
}
} catch (IOException e) {
logger.warn("Failed to analyze file: {}", path, e);
}
});
return issues;
}
private boolean containsInsecureDeserializationPattern(String line) {
return line.contains("ObjectInputStream") && 
line.contains("readObject") &&
!line.contains("SafeObjectInputStream") &&
!line.contains("ValidatingObjectInputStream");
}
private SecurityIssue createInsecureDeserializationIssue(Path filePath, int lineNumber, String line) {
return new SecurityIssue.Builder()
.ruleId("java/insecure-deserialization")
.ruleName("Insecure deserialization")
.level("error")
.message("Insecure deserialization detected. Deserializing untrusted data can lead to remote code execution.")
.shortDescription("Insecure deserialization")
.fullDescription("This code deserializes data without proper validation, which can lead to remote code execution attacks.")
.filePath(filePath)
.location(lineNumber, 1, lineNumber, line.length())
.securitySeverity("critical")
.precision("high")
.tags(Arrays.asList("security", "external/cwe/cwe-502", "owasp-top-ten/a8"))
.cweIds(Arrays.asList("CWE-502"))
.build();
}
private List<SecurityIssue> analyzeHardcodedCredentials() throws IOException {
List<SecurityIssue> issues = new ArrayList<>();
Files.walk(sourceCodePath)
.filter(path -> path.toString().endsWith(".java"))
.forEach(path -> {
try {
List<String> lines = Files.readAllLines(path);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (containsHardcodedCredentialsPattern(line)) {
issues.add(createHardcodedCredentialsIssue(path, i + 1, line));
}
}
} catch (IOException e) {
logger.warn("Failed to analyze file: {}", path, e);
}
});
return issues;
}
private boolean containsHardcodedCredentialsPattern(String line) {
String lowerLine = line.toLowerCase();
return (lowerLine.contains("password") || 
lowerLine.contains("pwd") || 
lowerLine.contains("pass")) &&
line.contains("=") &&
line.contains("\"") &&
line.trim().length() > 10; // Simple length check for password-like values
}
private SecurityIssue createHardcodedCredentialsIssue(Path filePath, int lineNumber, String line) {
return new SecurityIssue.Builder()
.ruleId("java/hardcoded-credentials")
.ruleName("Hardcoded credentials")
.level("warning")
.message("Hardcoded credentials detected. Store sensitive information in secure configuration.")
.shortDescription("Hardcoded credentials")
.fullDescription("This code contains hardcoded credentials, which is a security risk. Store credentials in environment variables or secure configuration files.")
.filePath(filePath)
.location(lineNumber, 1, lineNumber, line.length())
.securitySeverity("medium")
.precision("high")
.tags(Arrays.asList("security", "external/cwe/cwe-259", "owasp-top-ten/a2"))
.cweIds(Arrays.asList("CWE-259"))
.build();
}
public void generateSarifReport(Path outputPath) throws IOException {
reportGenerator.writeToFile(outputPath);
}
public String getSarifJson() throws IOException {
return reportGenerator.toJson();
}
}

SARIF Report Processor

SARIF Report Analyzer

package com.example.codeql.sarif;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.microsoft.sarif.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
public class SarifReportAnalyzer {
private static final Logger logger = LoggerFactory.getLogger(SarifReportAnalyzer.class);
private final ObjectMapper objectMapper;
public SarifReportAnalyzer() {
this.objectMapper = new ObjectMapper();
}
public AnalysisResult analyzeSarifFile(Path sarifFilePath) throws IOException {
SarifSchema210 sarif = objectMapper.readValue(sarifFilePath.toFile(), SarifSchema210.class);
return analyzeSarif(sarif);
}
public AnalysisResult analyzeSarif(SarifSchema210 sarif) {
AnalysisResult result = new AnalysisResult();
if (sarif.getRuns() == null) {
return result;
}
for (Run run : sarif.getRuns()) {
analyzeRun(run, result);
}
return result;
}
private void analyzeRun(Run run, AnalysisResult result) {
if (run.getResults() == null) return;
for (Result sarifResult : run.getResults()) {
SecurityFinding finding = convertToSecurityFinding(sarifResult, run);
result.addFinding(finding);
// Update statistics
result.incrementIssueCount();
if ("error".equalsIgnoreCase(sarifResult.getLevel())) {
result.incrementErrorCount();
} else if ("warning".equalsIgnoreCase(sarifResult.getLevel())) {
result.incrementWarningCount();
}
}
}
private SecurityFinding convertToSecurityFinding(Result sarifResult, Run run) {
SecurityFinding finding = new SecurityFinding();
finding.setRuleId(sarifResult.getRuleId());
finding.setLevel(sarifResult.getLevel());
finding.setMessage(sarifResult.getMessage().getText());
// Extract location information
if (sarifResult.getLocations() != null && !sarifResult.getLocations().isEmpty()) {
Location location = sarifResult.getLocations().get(0);
if (location.getPhysicalLocation() != null) {
PhysicalLocation physicalLocation = location.getPhysicalLocation();
if (physicalLocation.getArtifactLocation() != null) {
finding.setFilePath(physicalLocation.getArtifactLocation().getUri());
}
if (physicalLocation.getRegion() != null) {
Region region = physicalLocation.getRegion();
finding.setStartLine(region.getStartLine());
finding.setStartColumn(region.getStartColumn());
finding.setEndLine(region.getEndLine());
finding.setEndColumn(region.getEndColumn());
}
}
}
// Extract properties
if (sarifResult.getProperties() != null) {
PropertyBag properties = sarifResult.getProperties();
finding.setSecuritySeverity(properties.getProperty("security-severity"));
finding.setPrecision(properties.getProperty("precision"));
Object tags = properties.getProperty("tags");
if (tags instanceof List) {
finding.setTags((List<String>) tags);
}
Object cweIds = properties.getProperty("cwe");
if (cweIds instanceof List) {
finding.setCweIds((List<String>) cweIds);
}
}
// Get rule details from tool driver
if (run.getTool() != null && run.getTool().getDriver() != null) {
ToolComponent driver = run.getTool().getDriver();
if (driver.getRules() != null) {
driver.getRules().stream()
.filter(rule -> rule.getId().equals(sarifResult.getRuleId()))
.findFirst()
.ifPresent(rule -> {
finding.setRuleName(rule.getName());
if (rule.getShortDescription() != null) {
finding.setShortDescription(rule.getShortDescription().getText());
}
if (rule.getFullDescription() != null) {
finding.setFullDescription(rule.getFullDescription().getText());
}
});
}
}
return finding;
}
public Map<String, Integer> getIssueSummaryByRule(SarifSchema210 sarif) {
Map<String, Integer> summary = new HashMap<>();
if (sarif.getRuns() == null) return summary;
for (Run run : sarif.getRuns()) {
if (run.getResults() == null) continue;
for (Result result : run.getResults()) {
String ruleId = result.getRuleId();
summary.put(ruleId, summary.getOrDefault(ruleId, 0) + 1);
}
}
return summary;
}
public List<SecurityFinding> getCriticalFindings(SarifSchema210 sarif) {
List<SecurityFinding> criticalFindings = new ArrayList<>();
AnalysisResult result = analyzeSarif(sarif);
for (SecurityFinding finding : result.getFindings()) {
if ("critical".equalsIgnoreCase(finding.getSecuritySeverity()) ||
"error".equalsIgnoreCase(finding.getLevel())) {
criticalFindings.add(finding);
}
}
return criticalFindings;
}
public boolean hasCriticalIssues(SarifSchema210 sarif) {
return !getCriticalFindings(sarif).isEmpty();
}
}
// Analysis result models
class AnalysisResult {
private int totalIssues;
private int errorCount;
private int warningCount;
private int noteCount;
private final List<SecurityFinding> findings;
public AnalysisResult() {
this.findings = new ArrayList<>();
}
public void addFinding(SecurityFinding finding) {
findings.add(finding);
}
public void incrementIssueCount() { totalIssues++; }
public void incrementErrorCount() { errorCount++; }
public void incrementWarningCount() { warningCount++; }
public void incrementNoteCount() { noteCount++; }
// Getters
public int getTotalIssues() { return totalIssues; }
public int getErrorCount() { return errorCount; }
public int getWarningCount() { return warningCount; }
public int getNoteCount() { return noteCount; }
public List<SecurityFinding> getFindings() { return findings; }
public Map<String, Long> getIssuesBySeverity() {
Map<String, Long> severityCount = new HashMap<>();
for (SecurityFinding finding : findings) {
String severity = finding.getSecuritySeverity();
if (severity != null) {
severityCount.put(severity, severityCount.getOrDefault(severity, 0L) + 1);
}
}
return severityCount;
}
}
class SecurityFinding {
private String ruleId;
private String ruleName;
private String level;
private String message;
private String shortDescription;
private String fullDescription;
private String filePath;
private Integer startLine;
private Integer startColumn;
private Integer endLine;
private Integer endColumn;
private String securitySeverity;
private String precision;
private List<String> tags;
private List<String> cweIds;
// Getters and setters
public String getRuleId() { return ruleId; }
public void setRuleId(String ruleId) { this.ruleId = ruleId; }
public String getRuleName() { return ruleName; }
public void setRuleName(String ruleName) { this.ruleName = ruleName; }
public String getLevel() { return level; }
public void setLevel(String level) { this.level = level; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getShortDescription() { return shortDescription; }
public void setShortDescription(String shortDescription) { this.shortDescription = shortDescription; }
public String getFullDescription() { return fullDescription; }
public void setFullDescription(String fullDescription) { this.fullDescription = fullDescription; }
public String getFilePath() { return filePath; }
public void setFilePath(String filePath) { this.filePath = filePath; }
public Integer getStartLine() { return startLine; }
public void setStartLine(Integer startLine) { this.startLine = startLine; }
public Integer getStartColumn() { return startColumn; }
public void setStartColumn(Integer startColumn) { this.startColumn = startColumn; }
public Integer getEndLine() { return endLine; }
public void setEndLine(Integer endLine) { this.endLine = endLine; }
public Integer getEndColumn() { return endColumn; }
public void setEndColumn(Integer endColumn) { this.endColumn = endColumn; }
public String getSecuritySeverity() { return securitySeverity; }
public void setSecuritySeverity(String securitySeverity) { this.securitySeverity = securitySeverity; }
public String getPrecision() { return precision; }
public void setPrecision(String precision) { this.precision = precision; }
public List<String> getTags() { return tags; }
public void setTags(List<String> tags) { this.tags = tags; }
public List<String> getCweIds() { return cweIds; }
public void setCweIds(List<String> cweIds) { this.cweIds = cweIds; }
@Override
public String toString() {
return String.format("[%s] %s: %s (%s:%d)", 
level, ruleId, message, filePath, startLine);
}
}

GitHub Actions Integration

GitHub Actions Workflow

name: "CodeQL Security Analysis"
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * 0'  # Weekly on Sunday
jobs:
codeql-analysis:
name: "CodeQL Security Analysis"
runs-on: ubuntu-latest
permissions:
security-events: write
actions: read
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: java
queries: security-and-quality
- name: Build project
run: |
mvn -B compile -DskipTests
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:java"
output: codeql-results.sarif
- name: Upload SARIF results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: codeql-results.sarif
wait-for-processing: true
- name: Run Custom Security Analysis
run: |
mvn -B exec:java -Dexec.mainClass="com.example.codeql.Main" -Dexec.args="analyze"
- name: Upload Custom SARIF Results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: custom-security-results.sarif
security-report:
name: "Generate Security Report"
runs-on: ubuntu-latest
needs: codeql-analysis
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Download SARIF results
uses: actions/download-artifact@v3
with:
name: codeql-results
path: codeql-results
- name: Generate Security Report
run: |
mvn -B exec:java -Dexec.mainClass="com.example.codeql.report.SecurityReportGenerator" \
-Dexec.args="--input codeql-results --output security-report.html"
- name: Upload Security Report
uses: actions/upload-artifact@v3
with:
name: security-report
path: security-report.html

Custom Security Queries

Custom CodeQL Query Implementation

package com.example.codeql.queries;
import com.example.codeql.sarif.SecurityIssue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public class CustomSecurityQueries {
private static final Logger logger = LoggerFactory.getLogger(CustomSecurityQueries.class);
public List<SecurityIssue> runCustomQueries(Path sourcePath) throws IOException {
List<SecurityIssue> issues = new ArrayList<>();
issues.addAll(findLogForgingVulnerabilities(sourcePath));
issues.addAll(findPathTraversalVulnerabilities(sourcePath));
issues.addAll(findUnvalidatedRedirects(sourcePath));
issues.addAll(findInsecureRandomness(sourcePath));
return issues;
}
private List<SecurityIssue> findLogForgingVulnerabilities(Path sourcePath) throws IOException {
List<SecurityIssue> issues = new ArrayList<>();
Pattern logPattern = Pattern.compile(".*log\\.(info|warn|error|debug)\\(.*");
Files.walk(sourcePath)
.filter(path -> path.toString().endsWith(".java"))
.forEach(path -> {
try {
List<String> lines = Files.readAllLines(path);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (logPattern.matcher(line).matches() && 
line.contains("+") && 
containsUserInput(line)) {
issues.add(new SecurityIssue.Builder()
.ruleId("java/log-forging")
.ruleName("Log forging vulnerability")
.level("warning")
.message("Potential log forging vulnerability. User input should be validated before logging.")
.shortDescription("Log forging vulnerability")
.fullDescription("This code logs user input without proper validation, which can lead to log forging attacks.")
.filePath(path)
.location(i + 1, 1, i + 1, line.length())
.securitySeverity("medium")
.precision("medium")
.tags(List.of("security", "external/cwe/cwe-117"))
.cweIds(List.of("CWE-117"))
.build());
}
}
} catch (IOException e) {
logger.warn("Failed to analyze file for log forging: {}", path, e);
}
});
return issues;
}
private List<SecurityIssue> findPathTraversalVulnerabilities(Path sourcePath) throws IOException {
List<SecurityIssue> issues = new ArrayList<>();
Pattern fileOperationPattern = Pattern.compile(
".*(FileInputStream|FileOutputStream|Files\\.(read|write)|new File).*"
);
Files.walk(sourcePath)
.filter(path -> path.toString().endsWith(".java"))
.forEach(path -> {
try {
List<String> lines = Files.readAllLines(path);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (fileOperationPattern.matcher(line).matches() && 
containsUserInput(line) &&
!containsPathValidation(line)) {
issues.add(new SecurityIssue.Builder()
.ruleId("java/path-traversal")
.ruleName("Path traversal vulnerability")
.level("error")
.message("Potential path traversal vulnerability. Validate and sanitize file paths.")
.shortDescription("Path traversal vulnerability")
.fullDescription("This code uses user input to construct file paths without proper validation, which can lead to path traversal attacks.")
.filePath(path)
.location(i + 1, 1, i + 1, line.length())
.securitySeverity("high")
.precision("medium")
.tags(List.of("security", "external/cwe/cwe-22"))
.cweIds(List.of("CWE-22"))
.build());
}
}
} catch (IOException e) {
logger.warn("Failed to analyze file for path traversal: {}", path, e);
}
});
return issues;
}
private List<SecurityIssue> findUnvalidatedRedirects(Path sourcePath) throws IOException {
List<SecurityIssue> issues = new ArrayList<>();
Files.walk(sourcePath)
.filter(path -> path.toString().endsWith(".java"))
.forEach(path -> {
try {
List<String> lines = Files.readAllLines(path);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if ((line.contains("sendRedirect") || line.contains("redirect:")) &&
containsUserInput(line) &&
!containsUrlValidation(line)) {
issues.add(new SecurityIssue.Builder()
.ruleId("java/unvalidated-redirect")
.ruleName("Unvalidated redirect vulnerability")
.level("warning")
.message("Potential unvalidated redirect vulnerability. Validate redirect URLs.")
.shortDescription("Unvalidated redirect vulnerability")
.fullDescription("This code redirects to a URL based on user input without proper validation, which can lead to open redirect attacks.")
.filePath(path)
.location(i + 1, 1, i + 1, line.length())
.securitySeverity("medium")
.precision("medium")
.tags(List.of("security", "external/cwe/cwe-601"))
.cweIds(List.of("CWE-601"))
.build());
}
}
} catch (IOException e) {
logger.warn("Failed to analyze file for unvalidated redirects: {}", path, e);
}
});
return issues;
}
private List<SecurityIssue> findInsecureRandomness(Path sourcePath) throws IOException {
List<SecurityIssue> issues = new ArrayList<>();
Files.walk(sourcePath)
.filter(path -> path.toString().endsWith(".java"))
.forEach(path -> {
try {
List<String> lines = Files.readAllLines(path);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (line.contains("java.util.Random") && 
!line.contains("java.security.SecureRandom") &&
isSecuritySensitiveContext(line, lines, i)) {
issues.add(new SecurityIssue.Builder()
.ruleId("java/insecure-randomness")
.ruleName("Insecure randomness")
.level("warning")
.message("Insecure randomness used in security-sensitive context. Use SecureRandom instead.")
.shortDescription("Insecure randomness")
.fullDescription("This code uses java.util.Random in a security-sensitive context, which is cryptographically weak. Use java.security.SecureRandom instead.")
.filePath(path)
.location(i + 1, 1, i + 1, line.length())
.securitySeverity("medium")
.precision("low")
.tags(List.of("security", "external/cwe/cwe-338"))
.cweIds(List.of("CWE-338"))
.build());
}
}
} catch (IOException e) {
logger.warn("Failed to analyze file for insecure randomness: {}", path, e);
}
});
return issues;
}
private boolean containsUserInput(String line) {
return line.contains("getParameter") || 
line.contains("getHeader") ||
line.contains("getAttribute") ||
line.contains("request.get") ||
line.contains("HttpServletRequest");
}
private boolean containsPathValidation(String line) {
return line.contains("normalize") ||
line.contains("getCanonicalPath") ||
line.contains("PathValidator") ||
line.contains("sanitize");
}
private boolean containsUrlValidation(String line) {
return line.contains("isValidRedirect") ||
line.contains("UrlValidator") ||
line.contains("whitelist") ||
line.contains("allowedDomains");
}
private boolean isSecuritySensitiveContext(String line, List<String> lines, int currentLine) {
// Check if the randomness is used for security purposes
String context = String.join(" ", 
lines.subList(Math.max(0, currentLine - 3), 
Math.min(lines.size(), currentLine + 3)));
return context.toLowerCase().contains("password") ||
context.toLowerCase().contains("token") ||
context.toLowerCase().contains("session") ||
context.toLowerCase().contains("crypto") ||
context.toLowerCase().contains("key") ||
context.toLowerCase().contains("secret");
}
}

Security Report Generator

package com.example.codeql.report;
import com.example.codeql.sarif.SecurityFinding;
import com.example.codeql.sarif.SarifReportAnalyzer;
import com.microsoft.sarif.SarifSchema210;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
public class SecurityReportGenerator {
private static final Logger logger = LoggerFactory.getLogger(SecurityReportGenerator.class);
private final SarifReportAnalyzer analyzer;
private final ObjectMapper objectMapper;
public SecurityReportGenerator() {
this.analyzer = new SarifReportAnalyzer();
this.objectMapper = new ObjectMapper();
}
public void generateHtmlReport(Path sarifFilePath, Path outputPath) throws IOException {
SarifSchema210 sarif = objectMapper.readValue(sarifFilePath.toFile(), SarifSchema210.class);
AnalysisResult result = analyzer.analyzeSarif(sarif);
String html = generateHtmlContent(result);
Files.write(outputPath, html.getBytes());
logger.info("Security report generated: {}", outputPath);
}
private String generateHtmlContent(AnalysisResult result) {
StringBuilder html = new StringBuilder();
html.append("""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Security Analysis Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background: #f5f5f5; padding: 20px; border-radius: 5px; }
.summary { margin: 20px 0; }
.finding { border: 1px solid #ddd; margin: 10px 0; padding: 15px; border-radius: 5px; }
.critical { border-left: 5px solid #d73a49; background: #ffebee; }
.high { border-left: 5px solid #f66a0a; background: #fff3e0; }
.medium { border-left: 5px solid #ffd33d; background: #fffde7; }
.low { border-left: 5px solid #28a745; background: #f0fff4; }
.severity-badge { display: inline-block; padding: 3px 8px; border-radius: 3px; color: white; font-size: 12px; }
.severity-critical { background: #d73a49; }
.severity-high { background: #f66a0a; }
.severity-medium { background: #ffd33d; color: black; }
.severity-low { background: #28a745; }
.rule-id { font-weight: bold; color: #0366d6; }
.location { color: #586069; font-size: 14px; }
</style>
</head>
<body>
<div class="header">
<h1>Security Analysis Report</h1>
<p>Generated on: """ + new java.util.Date() + """</p>
</div>
""");
// Summary section
html.append("""
<div class="summary">
<h2>Summary</h2>
<p>Total Issues: """ + result.getTotalIssues() + """</p>
<p>Errors: """ + result.getErrorCount() + """</p>
<p>Warnings: """ + result.getWarningCount() + """</p>
</div>
""");
// Issues by severity
Map<String, Long> severityCount = result.getIssuesBySeverity();
if (!severityCount.isEmpty()) {
html.append("<div class='summary'><h3>Issues by Severity</h3><ul>");
for (Map.Entry<String, Long> entry : severityCount.entrySet()) {
html.append("<li>").append(entry.getKey()).append(": ").append(entry.getValue()).append("</li>");
}
html.append("</ul></div>");
}
// Findings list
html.append("<div class='findings'><h2>Security Findings</h2>");
for (SecurityFinding finding : result.getFindings()) {
String severityClass = getSeverityClass(finding.getSecuritySeverity());
String severityBadge = getSeverityBadge(finding.getSecuritySeverity());
html.append("""
<div class="finding """ + severityClass + """">
<div class="rule-id">""" + finding.getRuleId() + """</div>
<span class="severity-badge """ + severityBadge + """">""" + 
finding.getSecuritySeverity().toUpperCase() + """</span>
<h3>""" + finding.getMessage() + """</h3>
<div class="location">""" + finding.getFilePath() + ":" + finding.getStartLine() + """</div>
<p>""" + finding.getFullDescription() + """</p>
""");
if (finding.getCweIds() != null && !finding.getCweIds().isEmpty()) {
html.append("<p><strong>CWE:</strong> ").append(String.join(", ", finding.getCweIds())).append("</p>");
}
html.append("</div>");
}
html.append("""
</div>
</body>
</html>
""");
return html.toString();
}
private String getSeverityClass(String severity) {
if (severity == null) return "low";
return switch (severity.toLowerCase()) {
case "critical" -> "critical";
case "high" -> "high";
case "medium" -> "medium";
default -> "low";
};
}
private String getSeverityBadge(String severity) {
if (severity == null) return "severity-low";
return switch (severity.toLowerCase()) {
case "critical" -> "severity-critical";
case "high" -> "severity-high";
case "medium" -> "severity-medium";
default -> "severity-low";
};
}
public void generateConsoleReport(Path sarifFilePath) throws IOException {
SarifSchema210 sarif = objectMapper.readValue(sarifFilePath.toFile(), SarifSchema210.class);
AnalysisResult result = analyzer.analyzeSarif(sarif);
System.out.println("=== SECURITY ANALYSIS REPORT ===");
System.out.printf("Total Issues: %d%n", result.getTotalIssues());
System.out.printf("Errors: %d%n", result.getErrorCount());
System.out.printf("Warnings: %d%n", result.getWarningCount());
System.out.println();
Map<String, Long> severityCount = result.getIssuesBySeverity();
if (!severityCount.isEmpty()) {
System.out.println("Issues by Severity:");
for (Map.Entry<String, Long> entry : severityCount.entrySet()) {
System.out.printf("  %s: %d%n", entry.getKey(), entry.getValue());
}
System.out.println();
}
List<SecurityFinding> criticalFindings = analyzer.getCriticalFindings(sarif);
if (!criticalFindings.isEmpty()) {
System.out.println("CRITICAL FINDINGS:");
for (SecurityFinding finding : criticalFindings) {
System.out.printf("[%s] %s: %s%n", 
finding.getSecuritySeverity(), finding.getRuleId(), finding.getMessage());
System.out.printf("  Location: %s:%d%n", finding.getFilePath(), finding.getStartLine());
System.out.println();
}
}
}
}

Usage Example

package com.example.codeql;
import com.example.codeql.analyzer.CodeQLAnalyzer;
import com.example.codeql.queries.CustomSecurityQueries;
import com.example.codeql.report.SecurityReportGenerator;
import com.example.codeql.sarif.SecurityIssue;
import com.example.codeql.sarif.SarifReportGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
public class CodeQLSecurityMain {
private static final Logger logger = LoggerFactory.getLogger(CodeQLSecurityMain.class);
public static void main(String[] args) {
if (args.length < 2) {
System.err.println("Usage: java CodeQLSecurityMain <command> <source-path>");
System.err.println("Commands: analyze, report, custom");
System.exit(1);
}
String command = args[0];
Path sourcePath = Paths.get(args[1]);
try {
switch (command) {
case "analyze":
runCodeQLAnalysis(sourcePath);
break;
case "report":
generateSecurityReport(sourcePath);
break;
case "custom":
runCustomSecurityQueries(sourcePath);
break;
default:
System.err.println("Unknown command: " + command);
System.exit(1);
}
} catch (Exception e) {
logger.error("Analysis failed", e);
System.exit(1);
}
}
private static void runCodeQLAnalysis(Path sourcePath) throws IOException, InterruptedException {
Path codeqlDatabasePath = sourcePath.resolve("codeql-database");
Path outputPath = sourcePath.resolve("codeql-results.sarif");
CodeQLAnalyzer analyzer = new CodeQLAnalyzer(codeqlDatabasePath, sourcePath);
analyzer.analyze();
analyzer.generateSarifReport(outputPath);
logger.info("CodeQL analysis completed. Results: {}", outputPath);
}
private static void generateSecurityReport(Path sourcePath) throws IOException {
Path sarifPath = sourcePath.resolve("codeql-results.sarif");
Path reportPath = sourcePath.resolve("security-report.html");
SecurityReportGenerator reportGenerator = new SecurityReportGenerator();
reportGenerator.generateHtmlReport(sarifPath, reportPath);
reportGenerator.generateConsoleReport(sarifPath);
logger.info("Security report generated: {}", reportPath);
}
private static void runCustomSecurityQueries(Path sourcePath) throws IOException {
CustomSecurityQueries customQueries = new CustomSecurityQueries();
List<SecurityIssue> issues = customQueries.runCustomQueries(sourcePath);
SarifReportGenerator reportGenerator = new SarifReportGenerator();
reportGenerator.createRun("Custom Security Queries", "1.0.0");
for (SecurityIssue issue : issues) {
reportGenerator.addSecurityIssue(issue);
}
Path outputPath = sourcePath.resolve("custom-security-results.sarif");
reportGenerator.writeToFile(outputPath);
logger.info("Custom security analysis completed. Found {} issues. Results: {}", 
issues.size(), outputPath);
}
}

Conclusion

This comprehensive CodeQL SARIF implementation for Java provides:

  • SARIF report generation for standardized security analysis results
  • CodeQL integration for semantic code analysis
  • Custom security queries for organization-specific requirements
  • GitHub Actions integration for CI/CD pipelines
  • Security reporting with HTML and console outputs
  • Advanced analysis with severity classification and CWE mapping

Key benefits include:

  1. Standardized Output - SARIF format for tool interoperability
  2. Comprehensive Security Analysis - Multiple vulnerability detection
  3. GitHub Integration - Native support for GitHub Advanced Security
  4. Customizable Rules - Extensible with organization-specific queries
  5. Actionable Reports - Clear, prioritized security findings

The implementation follows industry best practices and provides a solid foundation for integrating CodeQL security analysis into Java development workflows.

Secure Java Supply Chain, Minimal Containers & Runtime Security (Alpine, Distroless, Signing, SBOM & Kubernetes Controls)

https://macronepal.com/blog/alpine-linux-security-in-java-complete-guide/
Explains how Alpine Linux is used as a lightweight base for Java containers to reduce image size and attack surface, while discussing tradeoffs like musl compatibility, CVE handling, and additional hardening requirements for production security.

https://macronepal.com/blog/the-minimalists-approach-building-ultra-secure-java-applications-with-scratch-base-images/
Explains using scratch base images for Java applications to create extremely minimal containers with almost zero attack surface, where only the compiled Java application and runtime dependencies exist.

https://macronepal.com/blog/distroless-containers-in-java-minimal-secure-containers-for-jvm-applications/
Explains distroless Java containers that remove shells, package managers, and unnecessary OS tools, significantly reducing vulnerabilities while improving security posture for JVM workloads.

https://macronepal.com/blog/revolutionizing-container-security-implementing-chainguard-images-for-java-applications/
Explains Chainguard images for Java, which are secure-by-default, CVE-minimized container images with SBOMs and cryptographic signing, designed for modern supply-chain security.

https://macronepal.com/blog/seccomp-filtering-in-java-comprehensive-security-sandboxing/
Explains seccomp syscall filtering in Linux to restrict what system calls Java applications can make, reducing the impact of exploits by limiting kernel-level access.

https://macronepal.com/blog/in-toto-attestations-in-java/
Explains in-toto framework integration in Java to create cryptographically verifiable attestations across the software supply chain, ensuring every build step is trusted and auditable.

https://macronepal.com/blog/fulcio-integration-in-java-code-signing-certificate-infrastructure/
Explains Fulcio integration for Java, which issues short-lived certificates for code signing in a zero-trust supply chain, enabling secure identity-based signing of artifacts.

https://macronepal.com/blog/tekton-supply-chain-in-java-comprehensive-ci-cd-pipeline-implementation/
Explains using Tekton CI/CD pipelines for Java applications to automate secure builds, testing, signing, and deployment with supply-chain security controls built in.

https://macronepal.com/blog/slsa-provenance-in-java-complete-guide-to-supply-chain-security-2/
Explains SLSA (Supply-chain Levels for Software Artifacts) provenance in Java builds, ensuring traceability of how software is built, from source code to final container image.

https://macronepal.com/blog/notary-project-in-java-complete-implementation-guide/
Explains the Notary Project for Java container security, enabling cryptographic signing and verification of container images and artifacts to prevent tampering in deployment pipelines.

Secure Java Supply Chain, Minimal Containers & Runtime Security (Alpine, Distroless, Signing, SBOM & Kubernetes Controls)

https://macronepal.com/blog/alpine-linux-security-in-java-complete-guide/
Explains how Alpine Linux is used as a lightweight base for Java containers to reduce image size and attack surface, while discussing tradeoffs like musl compatibility, CVE handling, and additional hardening requirements for production security.

https://macronepal.com/blog/the-minimalists-approach-building-ultra-secure-java-applications-with-scratch-base-images/
Explains using scratch base images for Java applications to create extremely minimal containers with almost zero attack surface, where only the compiled Java application and runtime dependencies exist.

https://macronepal.com/blog/distroless-containers-in-java-minimal-secure-containers-for-jvm-applications/
Explains distroless Java containers that remove shells, package managers, and unnecessary OS tools, significantly reducing vulnerabilities while improving security posture for JVM workloads.

https://macronepal.com/blog/revolutionizing-container-security-implementing-chainguard-images-for-java-applications/
Explains Chainguard images for Java, which are secure-by-default, CVE-minimized container images with SBOMs and cryptographic signing, designed for modern supply-chain security.

https://macronepal.com/blog/seccomp-filtering-in-java-comprehensive-security-sandboxing/
Explains seccomp syscall filtering in Linux to restrict what system calls Java applications can make, reducing the impact of exploits by limiting kernel-level access.

https://macronepal.com/blog/in-toto-attestations-in-java/
Explains in-toto framework integration in Java to create cryptographically verifiable attestations across the software supply chain, ensuring every build step is trusted and auditable.

https://macronepal.com/blog/fulcio-integration-in-java-code-signing-certificate-infrastructure/
Explains Fulcio integration for Java, which issues short-lived certificates for code signing in a zero-trust supply chain, enabling secure identity-based signing of artifacts.

https://macronepal.com/blog/tekton-supply-chain-in-java-comprehensive-ci-cd-pipeline-implementation/
Explains using Tekton CI/CD pipelines for Java applications to automate secure builds, testing, signing, and deployment with supply-chain security controls built in.

https://macronepal.com/blog/slsa-provenance-in-java-complete-guide-to-supply-chain-security-2/
Explains SLSA (Supply-chain Levels for Software Artifacts) provenance in Java builds, ensuring traceability of how software is built, from source code to final container image.

https://macronepal.com/blog/notary-project-in-java-complete-implementation-guide/
Explains the Notary Project for Java container security, enabling cryptographic signing and verification of container images and artifacts to prevent tampering in deployment pipelines.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper