CodeQL SARIF in Java

Introduction to CodeQL and SARIF

CodeQL is a semantic code analysis engine that lets you query code as data. SARIF (Static Analysis Results Interchange Format) is a standard format for output from static analysis tools. This guide covers generating, parsing, and working with SARIF files in Java.

Maven Dependencies

<properties>
<sarif.version>2.1.0</sarif.version>
<codeql.version>2.14.5</codeql.version>
<jackson.version>2.15.2</jackson.version>
</properties>
<dependencies>
<!-- SARIF SDK -->
<dependency>
<groupId>com.microsoft</groupId>
<artifactId>sarif-java</artifactId>
<version>${sarif.version}</version>
</dependency>
<!-- CodeQL -->
<dependency>
<groupId>com.github.codeql</groupId>
<artifactId>codeql-java</artifactId>
<version>${codeql.version}</version>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- File Operations -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
</dependency>
</dependencies>

SARIF Data Models

Core SARIF Objects

public class SarifModels {
// SARIF result severity levels
public enum SeverityLevel {
ERROR,
WARNING,
NOTE,
NONE
}
// SARIF result kind
public enum ResultKind {
FAIL("fail"),
PASS("pass"),
REVIEW("review"),
OPEN("open"),
NOT_APPLICABLE("notApplicable");
private final String value;
ResultKind(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
// CodeQL-specific metadata
public static class CodeQLMetadata {
private String queryId;
private String queryName;
private String querySuite;
private String precision;
private List<String> tags;
private Map<String, String> properties;
// Constructors, getters, setters
public CodeQLMetadata() {}
public CodeQLMetadata(String queryId, String queryName, String querySuite) {
this.queryId = queryId;
this.queryName = queryName;
this.querySuite = querySuite;
this.tags = new ArrayList<>();
this.properties = new HashMap<>();
}
public String getQueryId() { return queryId; }
public void setQueryId(String queryId) { this.queryId = queryId; }
public String getQueryName() { return queryName; }
public void setQueryName(String queryName) { this.queryName = queryName; }
public String getQuerySuite() { return querySuite; }
public void setQuerySuite(String querySuite) { this.querySuite = querySuite; }
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 Map<String, String> getProperties() { return properties; }
public void setProperties(Map<String, String> properties) { this.properties = properties; }
}
// Custom rule metadata
public static class RuleMetadata {
private String id;
private String name;
private String shortDescription;
private String fullDescription;
private String helpUri;
private SeverityLevel defaultSeverity;
private List<String> tags;
// Constructors, getters, setters
public RuleMetadata() {}
public RuleMetadata(String id, String name, String shortDescription, SeverityLevel defaultSeverity) {
this.id = id;
this.name = name;
this.shortDescription = shortDescription;
this.defaultSeverity = defaultSeverity;
this.tags = new ArrayList<>();
}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
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 getHelpUri() { return helpUri; }
public void setHelpUri(String helpUri) { this.helpUri = helpUri; }
public SeverityLevel getDefaultSeverity() { return defaultSeverity; }
public void setDefaultSeverity(SeverityLevel defaultSeverity) { this.defaultSeverity = defaultSeverity; }
public List<String> getTags() { return tags; }
public void setTags(List<String> tags) { this.tags = tags; }
}
}

SARIF Report Generation

Basic SARIF Generator

@Component
public class SarifReportGenerator {
private static final String SARIF_VERSION = "2.1.0";
private static final String SCHEMA_URL = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json";
private final ObjectMapper objectMapper;
public SarifReportGenerator() {
this.objectMapper = new ObjectMapper();
this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
}
public SarifReport createEmptyReport() {
SarifReport report = new SarifReport();
report.setVersion(SARIF_VERSION);
report.setSchema(SCHEMA_URL);
report.setRuns(new ArrayList<>());
return report;
}
public Run 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://example.com/tool");
tool.setDriver(driver);
run.setTool(tool);
// Results and rules
run.setResults(new ArrayList<>());
run.setRules(new HashMap<>());
// Additional properties
run.setProperties(new HashMap<>());
return run;
}
public void addRuleToRun(Run run, RuleMetadata ruleMetadata) {
ReportingDescriptor rule = new ReportingDescriptor();
rule.setId(ruleMetadata.getId());
rule.setName(ruleMetadata.getName());
// Description
MultiformatMessageString shortDescription = new MultiformatMessageString();
shortDescription.setText(ruleMetadata.getShortDescription());
rule.setShortDescription(shortDescription);
if (ruleMetadata.getFullDescription() != null) {
MultiformatMessageString fullDescription = new MultiformatMessageString();
fullDescription.setText(ruleMetadata.getFullDescription());
rule.setFullDescription(fullDescription);
}
// Help URI
if (ruleMetadata.getHelpUri() != null) {
rule.setHelpUri(ruleMetadata.getHelpUri());
}
// Properties
rule.setProperties(new HashMap<>());
rule.getProperties().put("defaultSeverity", ruleMetadata.getDefaultSeverity().name());
if (!ruleMetadata.getTags().isEmpty()) {
rule.getProperties().put("tags", ruleMetadata.getTags());
}
run.getRules().put(ruleMetadata.getId(), rule);
}
public Result createResult(String ruleId, String message, SeverityLevel severity, ResultKind kind) {
Result result = new Result();
result.setRuleId(ruleId);
// Message
Message messageObj = new Message();
messageObj.setText(message);
result.setMessage(messageObj);
// Severity and kind
result.setLevel(severity.name().toLowerCase());
result.setKind(kind.getValue());
// Locations will be added separately
result.setLocations(new ArrayList<>());
// Properties
result.setProperties(new HashMap<>());
return result;
}
public Location createLocation(String filePath, int startLine, int startColumn, 
int endLine, int endColumn) {
Location location = new Location();
// Physical location
PhysicalLocation physicalLocation = new PhysicalLocation();
ArtifactLocation artifactLocation = new ArtifactLocation();
artifactLocation.setUri(filePath);
physicalLocation.setArtifactLocation(artifactLocation);
// Region
Region region = new Region();
region.setStartLine(startLine);
region.setStartColumn(startColumn);
region.setEndLine(endLine);
region.setEndColumn(endColumn);
physicalLocation.setRegion(region);
location.setPhysicalLocation(physicalLocation);
return location;
}
public void addCodeQLMetadata(Result result, CodeQLMetadata metadata) {
if (result.getProperties() == null) {
result.setProperties(new HashMap<>());
}
Map<String, Object> codeqlProps = new HashMap<>();
codeqlProps.put("queryId", metadata.getQueryId());
codeqlProps.put("queryName", metadata.getQueryName());
codeqlProps.put("querySuite", metadata.getQuerySuite());
if (metadata.getPrecision() != null) {
codeqlProps.put("precision", metadata.getPrecision());
}
if (!metadata.getTags().isEmpty()) {
codeqlProps.put("tags", metadata.getTags());
}
if (!metadata.getProperties().isEmpty()) {
codeqlProps.put("properties", metadata.getProperties());
}
result.getProperties().put("codeql", codeqlProps);
}
public void writeReportToFile(SarifReport report, Path outputPath) throws IOException {
objectMapper.writeValue(outputPath.toFile(), report);
}
public String toJsonString(SarifReport report) throws JsonProcessingException {
return objectMapper.writeValueAsString(report);
}
}

CodeQL Integration

CodeQL Results Processor

@Component
public class CodeQLResultsProcessor {
private final SarifReportGenerator sarifGenerator;
public CodeQLResultsProcessor(SarifReportGenerator sarifGenerator) {
this.sarifGenerator = sarifGenerator;
}
public SarifReport processCodeQLResults(Path codeqlResultsDir, String toolName, String toolVersion) 
throws IOException {
SarifReport report = sarifGenerator.createEmptyReport();
Run run = sarifGenerator.createRun(toolName, toolVersion);
// Process all SARIF files in the results directory
Files.walk(codeqlResultsDir)
.filter(path -> path.toString().endsWith(".sarif"))
.forEach(sarifFile -> processSarifFile(sarifFile, run));
report.getRuns().add(run);
return report;
}
private void processSarifFile(Path sarifFile, Run run) {
try {
ObjectMapper mapper = new ObjectMapper();
SarifReport fileReport = mapper.readValue(sarifFile.toFile(), SarifReport.class);
if (fileReport.getRuns() != null) {
fileReport.getRuns().forEach(fileRun -> {
// Merge results
if (fileRun.getResults() != null) {
run.getResults().addAll(fileRun.getResults());
}
// Merge rules
if (fileRun.getRules() != null) {
if (run.getRules() == null) {
run.setRules(new HashMap<>());
}
run.getRules().putAll(fileRun.getRules());
}
});
}
} catch (IOException e) {
throw new RuntimeException("Failed to process SARIF file: " + sarifFile, e);
}
}
public void filterResultsBySeverity(SarifReport report, SeverityLevel minSeverity) {
for (Run run : report.getRuns()) {
if (run.getResults() != null) {
run.setResults(
run.getResults().stream()
.filter(result -> meetsSeverityThreshold(result, minSeverity))
.collect(Collectors.toList())
);
}
}
}
private boolean meetsSeverityThreshold(Result result, SeverityLevel minSeverity) {
String resultSeverity = result.getLevel();
if (resultSeverity == null) {
return false;
}
SeverityLevel severity = SeverityLevel.valueOf(resultSeverity.toUpperCase());
return severity.ordinal() <= minSeverity.ordinal();
}
public void groupResultsByRule(SarifReport report) {
for (Run run : report.getRuns()) {
if (run.getResults() != null) {
Map<String, List<Result>> resultsByRule = run.getResults().stream()
.collect(Collectors.groupingBy(Result::getRuleId));
// You can process grouped results here
resultsByRule.forEach((ruleId, results) -> {
System.out.printf("Rule %s: %d results%n", ruleId, results.size());
});
}
}
}
}

Custom Analysis Results

Security Vulnerability Reporter

@Component
public class SecurityVulnerabilityReporter {
private final SarifReportGenerator sarifGenerator;
private final Map<String, RuleMetadata> securityRules;
public SecurityVulnerabilityReporter(SarifReportGenerator sarifGenerator) {
this.sarifGenerator = sarifGenerator;
this.securityRules = initializeSecurityRules();
}
private Map<String, RuleMetadata> initializeSecurityRules() {
Map<String, RuleMetadata> rules = new HashMap<>();
// SQL Injection rule
rules.put("SQLI001", new RuleMetadata(
"SQLI001",
"SqlInjectionVulnerability",
"Potential SQL injection vulnerability",
SeverityLevel.ERROR
));
// Path Traversal rule
rules.put("PT001", new RuleMetadata(
"PT001",
"PathTraversalVulnerability",
"Potential path traversal vulnerability",
SeverityLevel.ERROR
));
// Hardcoded Password rule
rules.put("HCP001", new RuleMetadata(
"HCP001",
"HardcodedPassword",
"Hardcoded password detected",
SeverityLevel.WARNING
));
// Insecure Deserialization rule
rules.put("ID001", new RuleMetadata(
"ID001",
"InsecureDeserialization",
"Potential insecure deserialization",
SeverityLevel.ERROR
));
return rules;
}
public SarifReport generateSecurityReport(List<SecurityFinding> findings) {
SarifReport report = sarifGenerator.createEmptyReport();
Run run = sarifGenerator.createRun("JavaSecurityScanner", "1.0.0");
// Add security rules to the run
securityRules.values().forEach(rule -> 
sarifGenerator.addRuleToRun(run, rule));
// Convert findings to SARIF results
List<Result> results = findings.stream()
.map(this::convertFindingToResult)
.collect(Collectors.toList());
run.setResults(results);
report.getRuns().add(run);
return report;
}
private Result convertFindingToResult(SecurityFinding finding) {
RuleMetadata rule = securityRules.get(finding.getRuleId());
if (rule == null) {
throw new IllegalArgumentException("Unknown rule ID: " + finding.getRuleId());
}
Result result = sarifGenerator.createResult(
finding.getRuleId(),
finding.getMessage(),
rule.getDefaultSeverity(),
ResultKind.FAIL
);
// Add location
Location location = sarifGenerator.createLocation(
finding.getFilePath(),
finding.getStartLine(),
finding.getStartColumn(),
finding.getEndLine(),
finding.getEndColumn()
);
result.getLocations().add(location);
// Add additional properties
if (result.getProperties() == null) {
result.setProperties(new HashMap<>());
}
result.getProperties().put("confidence", finding.getConfidence());
result.getProperties().put("cweId", finding.getCweId());
// Add code flow if available
if (finding.getCodeFlow() != null) {
result.setCodeFlows(createCodeFlows(finding.getCodeFlow()));
}
return result;
}
private List<CodeFlow> createCodeFlows(List<CodeFlowStep> steps) {
CodeFlow codeFlow = new CodeFlow();
ThreadFlow threadFlow = new ThreadFlow();
List<ThreadFlowLocation> locations = steps.stream()
.map(this::createThreadFlowLocation)
.collect(Collectors.toList());
threadFlow.setLocations(locations);
codeFlow.setThreadFlows(Arrays.asList(threadFlow));
return Arrays.asList(codeFlow);
}
private ThreadFlowLocation createThreadFlowLocation(CodeFlowStep step) {
ThreadFlowLocation location = new ThreadFlowLocation();
Location sarifLocation = sarifGenerator.createLocation(
step.getFilePath(),
step.getLine(),
step.getColumn(),
step.getLine(),
step.getColumn()
);
location.setLocation(sarifLocation);
// Add step-specific properties
if (step.getMessage() != null) {
Message message = new Message();
message.setText(step.getMessage());
location.setMessage(message);
}
return location;
}
public static class SecurityFinding {
private String ruleId;
private String message;
private String filePath;
private int startLine;
private int startColumn;
private int endLine;
private int endColumn;
private String confidence;
private String cweId;
private List<CodeFlowStep> codeFlow;
// Constructors, getters, setters
public SecurityFinding() {}
public SecurityFinding(String ruleId, String message, String filePath, 
int startLine, int startColumn) {
this.ruleId = ruleId;
this.message = message;
this.filePath = filePath;
this.startLine = startLine;
this.startColumn = startColumn;
this.endLine = startLine;
this.endColumn = startColumn;
this.codeFlow = new ArrayList<>();
}
// Getters and setters...
public String getRuleId() { return ruleId; }
public void setRuleId(String ruleId) { this.ruleId = ruleId; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getFilePath() { return filePath; }
public void setFilePath(String filePath) { this.filePath = filePath; }
public int getStartLine() { return startLine; }
public void setStartLine(int startLine) { this.startLine = startLine; }
public int getStartColumn() { return startColumn; }
public void setStartColumn(int startColumn) { this.startColumn = startColumn; }
public int getEndLine() { return endLine; }
public void setEndLine(int endLine) { this.endLine = endLine; }
public int getEndColumn() { return endColumn; }
public void setEndColumn(int endColumn) { this.endColumn = endColumn; }
public String getConfidence() { return confidence; }
public void setConfidence(String confidence) { this.confidence = confidence; }
public String getCweId() { return cweId; }
public void setCweId(String cweId) { this.cweId = cweId; }
public List<CodeFlowStep> getCodeFlow() { return codeFlow; }
public void setCodeFlow(List<CodeFlowStep> codeFlow) { this.codeFlow = codeFlow; }
}
public static class CodeFlowStep {
private String filePath;
private int line;
private int column;
private String message;
// Constructors, getters, setters
public CodeFlowStep() {}
public CodeFlowStep(String filePath, int line, int column) {
this.filePath = filePath;
this.line = line;
this.column = column;
}
public String getFilePath() { return filePath; }
public void setFilePath(String filePath) { this.filePath = filePath; }
public int getLine() { return line; }
public void setLine(int line) { this.line = line; }
public int getColumn() { return column; }
public void setColumn(int column) { this.column = column; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}
}

SARIF Report Analysis

SARIF Report Analyzer

@Component
public class SarifReportAnalyzer {
public AnalysisResult analyzeSarifReport(SarifReport report) {
AnalysisResult result = new AnalysisResult();
for (Run run : report.getRuns()) {
RunAnalysis runAnalysis = analyzeRun(run);
result.addRunAnalysis(runAnalysis);
}
return result;
}
private RunAnalysis analyzeRun(Run run) {
RunAnalysis analysis = new RunAnalysis();
if (run.getResults() != null) {
// Count by severity
Map<String, Long> severityCounts = run.getResults().stream()
.collect(Collectors.groupingBy(
result -> result.getLevel() != null ? result.getLevel() : "unknown",
Collectors.counting()
));
analysis.setSeverityCounts(severityCounts);
// Count by rule
Map<String, Long> ruleCounts = run.getResults().stream()
.collect(Collectors.groupingBy(
Result::getRuleId,
Collectors.counting()
));
analysis.setRuleCounts(ruleCounts);
// Find most common issues
analysis.setMostCommonRules(findMostCommonRules(ruleCounts));
// Calculate statistics
analysis.setTotalIssues(run.getResults().size());
analysis.setErrorCount(severityCounts.getOrDefault("error", 0L));
analysis.setWarningCount(severityCounts.getOrDefault("warning", 0L));
}
return analysis;
}
private List<String> findMostCommonRules(Map<String, Long> ruleCounts) {
return ruleCounts.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.limit(10)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
public List<Result> findResultsByRule(SarifReport report, String ruleId) {
List<Result> results = new ArrayList<>();
for (Run run : report.getRuns()) {
if (run.getResults() != null) {
results.addAll(
run.getResults().stream()
.filter(result -> ruleId.equals(result.getRuleId()))
.collect(Collectors.toList())
);
}
}
return results;
}
public Map<String, List<Result>> groupResultsByFile(SarifReport report) {
Map<String, List<Result>> resultsByFile = new HashMap<>();
for (Run run : report.getRuns()) {
if (run.getResults() != null) {
for (Result result : run.getResults()) {
if (result.getLocations() != null && !result.getLocations().isEmpty()) {
String filePath = extractFilePath(result.getLocations().get(0));
resultsByFile.computeIfAbsent(filePath, k -> new ArrayList<>())
.add(result);
}
}
}
}
return resultsByFile;
}
private String extractFilePath(Location location) {
if (location.getPhysicalLocation() != null &&
location.getPhysicalLocation().getArtifactLocation() != null) {
return location.getPhysicalLocation().getArtifactLocation().getUri();
}
return "unknown";
}
public static class AnalysisResult {
private List<RunAnalysis> runAnalyses = new ArrayList<>();
private Map<String, Object> summary = new HashMap<>();
public void addRunAnalysis(RunAnalysis runAnalysis) {
runAnalyses.add(runAnalysis);
}
// Getters and setters
public List<RunAnalysis> getRunAnalyses() { return runAnalyses; }
public void setRunAnalyses(List<RunAnalysis> runAnalyses) { this.runAnalyses = runAnalyses; }
public Map<String, Object> getSummary() { return summary; }
public void setSummary(Map<String, Object> summary) { this.summary = summary; }
}
public static class RunAnalysis {
private Map<String, Long> severityCounts = new HashMap<>();
private Map<String, Long> ruleCounts = new HashMap<>();
private List<String> mostCommonRules = new ArrayList<>();
private long totalIssues;
private long errorCount;
private long warningCount;
// Getters and setters
public Map<String, Long> getSeverityCounts() { return severityCounts; }
public void setSeverityCounts(Map<String, Long> severityCounts) { this.severityCounts = severityCounts; }
public Map<String, Long> getRuleCounts() { return ruleCounts; }
public void setRuleCounts(Map<String, Long> ruleCounts) { this.ruleCounts = ruleCounts; }
public List<String> getMostCommonRules() { return mostCommonRules; }
public void setMostCommonRules(List<String> mostCommonRules) { this.mostCommonRules = mostCommonRules; }
public long getTotalIssues() { return totalIssues; }
public void setTotalIssues(long totalIssues) { this.totalIssues = totalIssues; }
public long getErrorCount() { return errorCount; }
public void setErrorCount(long errorCount) { this.errorCount = errorCount; }
public long getWarningCount() { return warningCount; }
public void setWarningCount(long warningCount) { this.warningCount = warningCount; }
}
}

SARIF Report Rendering

HTML Report Generator

@Component
public class SarifHtmlRenderer {
private final TemplateEngine templateEngine;
public SarifHtmlRenderer() {
this.templateEngine = new ThymeleafTemplateEngine();
}
public String generateHtmlReport(SarifReport report, SarifReportAnalyzer.AnalysisResult analysis) {
Context context = new Context();
context.setVariable("report", report);
context.setVariable("analysis", analysis);
context.setVariable("timestamp", Instant.now());
return templateEngine.process("sarif-report", context);
}
public void writeHtmlReport(SarifReport report, Path outputPath) throws IOException {
SarifReportAnalyzer analyzer = new SarifReportAnalyzer();
SarifReportAnalyzer.AnalysisResult analysis = analyzer.analyzeSarifReport(report);
String html = generateHtmlReport(report, analysis);
Files.writeString(outputPath, html, StandardCharsets.UTF_8);
}
public String generateResultsTable(List<Result> results) {
StringBuilder html = new StringBuilder();
html.append("<table class=\"sarif-results\">");
html.append("<thead><tr><th>Rule</th><th>Message</th><th>Location</th><th>Severity</th></tr></thead>");
html.append("<tbody>");
for (Result result : results) {
html.append("<tr>");
html.append("<td>").append(escapeHtml(result.getRuleId())).append("</td>");
html.append("<td>").append(escapeHtml(result.getMessage().getText())).append("</td>");
html.append("<td>").append(formatLocation(result)).append("</td>");
html.append("<td class=\"severity-").append(result.getLevel()).append("\">")
.append(result.getLevel()).append("</td>");
html.append("</tr>");
}
html.append("</tbody></table>");
return html.toString();
}
private String formatLocation(Result result) {
if (result.getLocations() == null || result.getLocations().isEmpty()) {
return "Unknown location";
}
Location location = result.getLocations().get(0);
if (location.getPhysicalLocation() == null) {
return "Unknown location";
}
ArtifactLocation artifact = location.getPhysicalLocation().getArtifactLocation();
Region region = location.getPhysicalLocation().getRegion();
StringBuilder sb = new StringBuilder();
if (artifact != null && artifact.getUri() != null) {
sb.append(artifact.getUri());
}
if (region != null) {
sb.append(":").append(region.getStartLine());
if (region.getStartColumn() != null) {
sb.append(":").append(region.getStartColumn());
}
}
return sb.toString();
}
private String escapeHtml(String text) {
if (text == null) {
return "";
}
return text.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\"", "&quot;")
.replace("'", "&#39;");
}
// Simple Thymeleaf-like template engine for demonstration
private static class ThymeleafTemplateEngine {
public String process(String templateName, Context context) {
// In real implementation, this would use actual Thymeleaf
return generateBasicHtmlReport(context);
}
private String generateBasicHtmlReport(Context context) {
SarifReport report = (SarifReport) context.getVariable("report");
SarifReportAnalyzer.AnalysisResult analysis = 
(SarifReportAnalyzer.AnalysisResult) context.getVariable("analysis");
StringBuilder html = new StringBuilder();
html.append("""
<!DOCTYPE html>
<html>
<head>
<title>SARIF Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.summary { background: #f5f5f5; padding: 15px; border-radius: 5px; }
.severity-error { color: #d32f2f; font-weight: bold; }
.severity-warning { color: #f57c00; font-weight: bold; }
.severity-note { color: #1976d2; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
th { background-color: #f2f2f2; }
</style>
</head>
<body>
<h1>SARIF Analysis Report</h1>
<div class="summary">
<h2>Summary</h2>
<p>Total Issues: %d</p>
<p>Errors: %d</p>
<p>Warnings: %d</p>
</div>
""".formatted(
analysis.getRunAnalyses().stream().mapToLong(RunAnalysis::getTotalIssues).sum(),
analysis.getRunAnalyses().stream().mapToLong(RunAnalysis::getErrorCount).sum(),
analysis.getRunAnalyses().stream().mapToLong(RunAnalysis::getWarningCount).sum()
));
// Add results tables for each run
for (int i = 0; i < report.getRuns().size(); i++) {
Run run = report.getRuns().get(i);
html.append("<h2>Run ").append(i + 1).append(": ").append(run.getTool().getDriver().getName()).append("</h2>");
if (run.getResults() != null && !run.getResults().isEmpty()) {
html.append(generateResultsTable(run.getResults()));
} else {
html.append("<p>No results found.</p>");
}
}
html.append("</body></html>");
return html.toString();
}
}
}

Integration with CI/CD

GitHub Actions Integration

name: CodeQL SARIF Processing
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
codeql-analysis:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: java
- name: Build application
run: mvn compile -DskipTests
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:java"
- name: Process SARIF Results
run: |
java -cp target/classes com.example.SarifProcessor \
--input results/java.sarif \
--output processed-results.sarif \
--format html
- name: Upload SARIF Results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: processed-results.sarif
- name: Generate HTML Report
run: |
java -cp target/classes com.example.HtmlReportGenerator \
--input processed-results.sarif \
--output report.html
- name: Upload HTML Report
uses: actions/upload-artifact@v3
with:
name: codeql-report
path: report.html

Conclusion

This comprehensive CodeQL SARIF implementation in Java provides:

  1. SARIF Generation - Create standardized static analysis reports
  2. CodeQL Integration - Process and enhance CodeQL results
  3. Security Reporting - Generate security-focused SARIF reports
  4. Report Analysis - Analyze and statistics from SARIF files
  5. HTML Rendering - Convert SARIF to human-readable HTML reports
  6. CI/CD Integration - GitHub Actions workflow integration

The implementation enables seamless processing of static analysis results, providing actionable insights and beautiful reports for development teams.

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.

Leave a Reply

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


Macro Nepal Helper