Titliel – ShiftLeft Scan in Java

Article: Comprehensive Static Application Security Testing (SAST) with ShiftLeft

ShiftLeft provides next-generation static application security testing (SAST) that uses code property graphs (CPG) to identify vulnerabilities across your codebase. This article covers comprehensive integration strategies for Java applications.

Core ShiftLeft Integration

package com.titliel.shiftleft;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import org.json.JSONObject;
import org.json.JSONArray;
/**
* Titliel ShiftLeft Scanner
* Advanced SAST integration with custom rules and CI/CD support
*/
public class TitlielShiftLeftScanner {
private final ShiftLeftConfig config;
private final VulnerabilityProcessor vulnerabilityProcessor;
private final ScanResultAggregator resultAggregator;
private final ExecutorService scanExecutor;
private final ReportGenerator reportGenerator;
public TitlielShiftLeftScanner() {
this.config = ShiftLeftConfig.getInstance();
this.vulnerabilityProcessor = new VulnerabilityProcessor();
this.resultAggregator = new ScanResultAggregator();
this.scanExecutor = Executors.newFixedThreadPool(config.getThreadCount());
this.reportGenerator = new ReportGenerator();
}
/**
* Perform comprehensive security scan
*/
public ScanResult scanProject(ProjectContext context) {
long startTime = System.currentTimeMillis();
ScanResult result = new ScanResult(context);
try {
// Pre-scan validation
validateProjectStructure(context);
// Execute multiple scan types in parallel
List<Future<ScanResult>> futures = new ArrayList<>();
futures.add(scanExecutor.submit(() -> performStaticAnalysis(context)));
futures.add(scanExecutor.submit(() -> performDependencyScan(context)));
futures.add(scanExecutor.submit(() -> performSecretsScan(context)));
futures.add(scanExecutor.submit(() -> performCustomRulesScan(context)));
// Wait for all scans to complete
for (Future<ScanResult> future : futures) {
try {
ScanResult partialResult = future.get();
result.merge(partialResult);
} catch (Exception e) {
result.addError(new ScanError("Scan execution failed", e));
}
}
// Post-process vulnerabilities
vulnerabilityProcessor.processVulnerabilities(result);
// Generate reports
generateReports(result);
} catch (Exception e) {
result.addError(new ScanError("Scan failed", e));
} finally {
result.setScanDuration(System.currentTimeMillis() - startTime);
}
return result;
}
/**
* Perform static code analysis using ShiftLeft NG-SAST
*/
private ScanResult performStaticAnalysis(ProjectContext context) {
ScanResult result = new ScanResult(context, ScanType.STATIC_ANALYSIS);
try {
// Build ShiftLeft command
List<String> command = buildShiftLeftCommand(context);
// Execute ShiftLeft scan
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.directory(context.getProjectDir().toFile());
Process process = processBuilder.start();
int exitCode = process.waitFor();
if (exitCode == 0) {
// Parse ShiftLeft output
Path outputFile = getOutputPath(context, "static-analysis");
parseShiftLeftOutput(outputFile, result);
} else {
result.addError(new ScanError("ShiftLeft scan failed with exit code: " + exitCode));
}
} catch (Exception e) {
result.addError(new ScanError("Static analysis failed", e));
}
return result;
}
/**
* Perform dependency vulnerability scanning
*/
private ScanResult performDependencyScan(ProjectContext context) {
ScanResult result = new ScanResult(context, ScanType.DEPENDENCY_SCAN);
try {
// Analyze dependencies for known vulnerabilities
DependencyAnalyzer analyzer = new DependencyAnalyzer();
List<Vulnerability> vulnerabilities = analyzer.analyzeDependencies(context);
for (Vulnerability vuln : vulnerabilities) {
result.addVulnerability(vuln);
}
} catch (Exception e) {
result.addError(new ScanError("Dependency scan failed", e));
}
return result;
}
/**
* Scan for hardcoded secrets and credentials
*/
private ScanResult performSecretsScan(ProjectContext context) {
ScanResult result = new ScanResult(context, ScanType.SECRETS_SCAN);
try {
SecretsDetector detector = new SecretsDetector();
List<Vulnerability> secrets = detector.scanForSecrets(context);
for (Vulnerability secret : secrets) {
result.addVulnerability(secret);
}
} catch (Exception e) {
result.addError(new ScanError("Secrets scan failed", e));
}
return result;
}
/**
* Apply custom security rules
*/
private ScanResult performCustomRulesScan(ProjectContext context) {
ScanResult result = new ScanResult(context, ScanType.CUSTOM_RULES);
try {
CustomRuleEngine ruleEngine = new CustomRuleEngine();
List<Vulnerability> customVulns = ruleEngine.applyCustomRules(context);
for (Vulnerability vuln : customVulns) {
result.addVulnerability(vuln);
}
} catch (Exception e) {
result.addError(new ScanError("Custom rules scan failed", e));
}
return result;
}
/**
* Build ShiftLeft CLI command
*/
private List<String> buildShiftLeftCommand(ProjectContext context) {
List<String> command = new ArrayList<>();
command.add(config.getShiftLeftExecutable());
command.add("scan");
command.add("--app");
command.add(context.getApplicationName());
command.add("--workspace");
command.add(context.getProjectDir().toString());
command.add("--output");
command.add(getOutputPath(context, "static-analysis").toString());
command.add("--format");
command.add("json");
// Add custom options
if (config.isDataFlowAnalysisEnabled()) {
command.add("--data-flow");
}
if (config.isTaintAnalysisEnabled()) {
command.add("--taint-analysis");
}
// Add severity threshold
command.add("--min-severity");
command.add(config.getMinSeverity().toString());
return command;
}
/**
* Parse ShiftLeft JSON output
*/
private void parseShiftLeftOutput(Path outputFile, ScanResult result) {
try {
String content = new String(Files.readAllBytes(outputFile));
JSONObject json = new JSONObject(content);
JSONArray findings = json.getJSONArray("findings");
for (int i = 0; i < findings.length(); i++) {
JSONObject finding = findings.getJSONObject(i);
Vulnerability vulnerability = parseVulnerability(finding);
result.addVulnerability(vulnerability);
}
} catch (Exception e) {
result.addError(new ScanError("Failed to parse ShiftLeft output", e));
}
}
/**
* Parse individual vulnerability from ShiftLeft finding
*/
private Vulnerability parseVulnerability(JSONObject finding) {
Vulnerability vuln = new Vulnerability();
vuln.setId(finding.getString("id"));
vuln.setTitle(finding.getString("title"));
vuln.setDescription(finding.optString("description", ""));
vuln.setSeverity(SeverityLevel.fromString(finding.getString("severity")));
vuln.setCategory(finding.getString("category"));
vuln.setCweId(finding.optString("cwe", ""));
vuln.setFilePath(finding.getString("file"));
vuln.setLineNumber(finding.optInt("line", 0));
vuln.setConfidence(finding.optDouble("confidence", 0.0));
// Parse data flow if available
if (finding.has("dataFlow")) {
DataFlow dataFlow = parseDataFlow(finding.getJSONObject("dataFlow"));
vuln.setDataFlow(dataFlow);
}
// Parse fix recommendations
if (finding.has("remediation")) {
String remediation = finding.getJSONObject("remediation").getString("text");
vuln.setRemediation(remediation);
}
return vuln;
}
/**
* Parse data flow information
*/
private DataFlow parseDataFlow(JSONObject dataFlowJson) {
DataFlow dataFlow = new DataFlow();
JSONArray sources = dataFlowJson.getJSONArray("sources");
for (int i = 0; i < sources.length(); i++) {
JSONObject source = sources.getJSONObject(i);
DataFlowNode node = parseDataFlowNode(source);
dataFlow.addSource(node);
}
JSONArray sinks = dataFlowJson.getJSONArray("sinks");
for (int i = 0; i < sinks.length(); i++) {
JSONObject sink = sinks.getJSONObject(i);
DataFlowNode node = parseDataFlowNode(sink);
dataFlow.addSink(node);
}
return dataFlow;
}
private DataFlowNode parseDataFlowNode(JSONObject nodeJson) {
return new DataFlowNode(
nodeJson.getString("file"),
nodeJson.getInt("line"),
nodeJson.getString("method"),
nodeJson.optString("variable", "")
);
}
/**
* Generate comprehensive reports
*/
private void generateReports(ScanResult result) {
try {
// HTML Report
if (config.isHtmlReportEnabled()) {
Path htmlReport = reportGenerator.generateHtmlReport(result);
result.addReport(htmlReport, ReportType.HTML);
}
// JSON Report
if (config.isJsonReportEnabled()) {
Path jsonReport = reportGenerator.generateJsonReport(result);
result.addReport(jsonReport, ReportType.JSON);
}
// SARIF Report for GitHub
if (config.isSarifReportEnabled()) {
Path sarifReport = reportGenerator.generateSarifReport(result);
result.addReport(sarifReport, ReportType.SARIF);
}
// JUnit Report for CI/CD
if (config.isJunitReportEnabled()) {
Path junitReport = reportGenerator.generateJunitReport(result);
result.addReport(junitReport, ReportType.JUNIT);
}
} catch (Exception e) {
result.addError(new ScanError("Report generation failed", e));
}
}
/**
* Validate project structure before scanning
*/
private void validateProjectStructure(ProjectContext context) {
List<String> errors = new ArrayList<>();
if (!Files.exists(context.getProjectDir())) {
errors.add("Project directory does not exist: " + context.getProjectDir());
}
// Check for build files
if (!hasBuildFile(context.getProjectDir())) {
errors.add("No build file (pom.xml, build.gradle, etc.) found in project directory");
}
// Check for source code
if (!hasSourceCode(context.getProjectDir())) {
errors.add("No Java source code found in project directory");
}
if (!errors.isEmpty()) {
throw new ScanValidationException("Project validation failed: " + String.join(", ", errors));
}
}
private boolean hasBuildFile(Path projectDir) {
return Files.exists(projectDir.resolve("pom.xml")) ||
Files.exists(projectDir.resolve("build.gradle")) ||
Files.exists(projectDir.resolve("build.gradle.kts"));
}
private boolean hasSourceCode(Path projectDir) {
try {
return Files.walk(projectDir)
.filter(path -> path.toString().endsWith(".java"))
.findFirst()
.isPresent();
} catch (IOException e) {
return false;
}
}
private Path getOutputPath(ProjectContext context, String scanType) {
return context.getOutputDir().resolve(scanType + "-results.json");
}
public void shutdown() {
scanExecutor.shutdown();
try {
if (!scanExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
scanExecutor.shutdownNow();
}
} catch (InterruptedException e) {
scanExecutor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}

Data Models and Configuration

package com.titliel.shiftleft;
import java.nio.file.Path;
import java.util.*;
/**
* Project context for scanning
*/
public class ProjectContext {
private final Path projectDir;
private final Path outputDir;
private final String applicationName;
private final String version;
private final Map<String, String> properties;
private final List<String> scanIncludes;
private final List<String> scanExcludes;
public ProjectContext(Path projectDir, String applicationName) {
this.projectDir = projectDir;
this.applicationName = applicationName;
this.outputDir = projectDir.resolve("shiftleft-reports");
this.version = "1.0.0";
this.properties = new HashMap<>();
this.scanIncludes = new ArrayList<>();
this.scanExcludes = new ArrayList<>();
// Default excludes
this.scanExcludes.add("**/test/**");
this.scanExcludes.add("**/target/**");
this.scanExcludes.add("**/build/**");
}
// Builder pattern
public static class Builder {
private Path projectDir;
private String applicationName;
private String version = "1.0.0";
private Map<String, String> properties = new HashMap<>();
private List<String> includes = new ArrayList<>();
private List<String> excludes = new ArrayList<>();
public Builder setProjectDir(Path projectDir) {
this.projectDir = projectDir;
return this;
}
public Builder setApplicationName(String applicationName) {
this.applicationName = applicationName;
return this;
}
public Builder setVersion(String version) {
this.version = version;
return this;
}
public Builder addProperty(String key, String value) {
this.properties.put(key, value);
return this;
}
public Builder addInclude(String pattern) {
this.includes.add(pattern);
return this;
}
public Builder addExclude(String pattern) {
this.excludes.add(pattern);
return this;
}
public ProjectContext build() {
ProjectContext context = new ProjectContext(projectDir, applicationName);
context.version = version;
context.properties.putAll(properties);
context.scanIncludes.addAll(includes);
context.scanExcludes.addAll(excludes);
return context;
}
}
// Getters
public Path getProjectDir() { return projectDir; }
public Path getOutputDir() { return outputDir; }
public String getApplicationName() { return applicationName; }
public String getVersion() { return version; }
public Map<String, String> getProperties() { return Collections.unmodifiableMap(properties); }
public List<String> getScanIncludes() { return Collections.unmodifiableList(scanIncludes); }
public List<String> getScanExcludes() { return Collections.unmodifiableList(scanExcludes); }
}
/**
* Scan result container
*/
public class ScanResult {
private final ProjectContext context;
private final ScanType scanType;
private final List<Vulnerability> vulnerabilities;
private final List<ScanError> errors;
private final Map<ReportType, Path> reports;
private long scanDuration;
private Date scanTimestamp;
public ScanResult(ProjectContext context) {
this(context, ScanType.COMPREHENSIVE);
}
public ScanResult(ProjectContext context, ScanType scanType) {
this.context = context;
this.scanType = scanType;
this.vulnerabilities = new ArrayList<>();
this.errors = new ArrayList<>();
this.reports = new HashMap<>();
this.scanTimestamp = new Date();
}
public void addVulnerability(Vulnerability vulnerability) {
this.vulnerabilities.add(vulnerability);
}
public void addError(ScanError error) {
this.errors.add(error);
}
public void addReport(Path reportPath, ReportType reportType) {
this.reports.put(reportType, reportPath);
}
public void merge(ScanResult other) {
this.vulnerabilities.addAll(other.vulnerabilities);
this.errors.addAll(other.errors);
this.reports.putAll(other.reports);
}
// Getters
public ProjectContext getContext() { return context; }
public ScanType getScanType() { return scanType; }
public List<Vulnerability> getVulnerabilities() { return Collections.unmodifiableList(vulnerabilities); }
public List<ScanError> getErrors() { return Collections.unmodifiableList(errors); }
public Map<ReportType, Path> getReports() { return Collections.unmodifiableMap(reports); }
public long getScanDuration() { return scanDuration; }
public void setScanDuration(long scanDuration) { this.scanDuration = scanDuration; }
public Date getScanTimestamp() { return scanTimestamp; }
// Statistics
public int getTotalVulnerabilities() { return vulnerabilities.size(); }
public int getCriticalCount() { return countVulnerabilitiesBySeverity(SeverityLevel.CRITICAL); }
public int getHighCount() { return countVulnerabilitiesBySeverity(SeverityLevel.HIGH); }
public int getMediumCount() { return countVulnerabilitiesBySeverity(SeverityLevel.MEDIUM); }
public int getLowCount() { return countVulnerabilitiesBySeverity(SeverityLevel.LOW); }
private int countVulnerabilitiesBySeverity(SeverityLevel severity) {
return (int) vulnerabilities.stream()
.filter(v -> v.getSeverity() == severity)
.count();
}
public boolean hasCriticalVulnerabilities() {
return getCriticalCount() > 0;
}
public boolean hasHighVulnerabilities() {
return getHighCount() > 0;
}
public boolean isScanSuccessful() {
return errors.isEmpty();
}
}
/**
* Vulnerability representation
*/
public class Vulnerability {
private String id;
private String title;
private String description;
private SeverityLevel severity;
private String category;
private String cweId;
private String filePath;
private int lineNumber;
private String methodName;
private double confidence;
private DataFlow dataFlow;
private String remediation;
private Map<String, Object> metadata;
public Vulnerability() {
this.metadata = new HashMap<>();
}
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public SeverityLevel getSeverity() { return severity; }
public void setSeverity(SeverityLevel severity) { this.severity = severity; }
public String getCategory() { return category; }
public void setCategory(String category) { this.category = category; }
public String getCweId() { return cweId; }
public void setCweId(String cweId) { this.cweId = cweId; }
public String getFilePath() { return filePath; }
public void setFilePath(String filePath) { this.filePath = filePath; }
public int getLineNumber() { return lineNumber; }
public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; }
public String getMethodName() { return methodName; }
public void setMethodName(String methodName) { this.methodName = methodName; }
public double getConfidence() { return confidence; }
public void setConfidence(double confidence) { this.confidence = confidence; }
public DataFlow getDataFlow() { return dataFlow; }
public void setDataFlow(DataFlow dataFlow) { this.dataFlow = dataFlow; }
public String getRemediation() { return remediation; }
public void setRemediation(String remediation) { this.remediation = remediation; }
public Map<String, Object> getMetadata() { return Collections.unmodifiableMap(metadata); }
public void addMetadata(String key, Object value) { this.metadata.put(key, value); }
}
/**
* Data flow information for tainted data
*/
public class DataFlow {
private List<DataFlowNode> sources;
private List<DataFlowNode> sinks;
private List<DataFlowNode> propagationPath;
public DataFlow() {
this.sources = new ArrayList<>();
this.sinks = new ArrayList<>();
this.propagationPath = new ArrayList<>();
}
public void addSource(DataFlowNode source) {
this.sources.add(source);
}
public void addSink(DataFlowNode sink) {
this.sinks.add(sink);
}
public void addPropagationStep(DataFlowNode step) {
this.propagationPath.add(step);
}
// Getters
public List<DataFlowNode> getSources() { return Collections.unmodifiableList(sources); }
public List<DataFlowNode> getSinks() { return Collections.unmodifiableList(sinks); }
public List<DataFlowNode> getPropagationPath() { return Collections.unmodifiableList(propagationPath); }
}
/**
* Data flow node in source code
*/
public class DataFlowNode {
private final String filePath;
private final int lineNumber;
private final String methodName;
private final String variableName;
public DataFlowNode(String filePath, int lineNumber, String methodName, String variableName) {
this.filePath = filePath;
this.lineNumber = lineNumber;
this.methodName = methodName;
this.variableName = variableName;
}
// Getters
public String getFilePath() { return filePath; }
public int getLineNumber() { return lineNumber; }
public String getMethodName() { return methodName; }
public String getVariableName() { return variableName; }
@Override
public String toString() {
return String.format("%s:%d in %s (%s)", filePath, lineNumber, methodName, variableName);
}
}
/**
* Enums and constants
*/
public enum ScanType {
STATIC_ANALYSIS, DEPENDENCY_SCAN, SECRETS_SCAN, CUSTOM_RULES, COMPREHENSIVE
}
public enum SeverityLevel {
CRITICAL, HIGH, MEDIUM, LOW, INFO;
public static SeverityLevel fromString(String severity) {
try {
return SeverityLevel.valueOf(severity.toUpperCase());
} catch (IllegalArgumentException e) {
return INFO;
}
}
}
public enum ReportType {
HTML, JSON, SARIF, JUNIT, PDF
}
/**
* Scan error representation
*/
public class ScanError {
private final String message;
private final Throwable cause;
private final Date timestamp;
public ScanError(String message) {
this(message, null);
}
public ScanError(String message, Throwable cause) {
this.message = message;
this.cause = cause;
this.timestamp = new Date();
}
// Getters
public String getMessage() { return message; }
public Throwable getCause() { return cause; }
public Date getTimestamp() { return timestamp; }
}
class ScanValidationException extends RuntimeException {
public ScanValidationException(String message) {
super(message);
}
}

Vulnerability Processing and Analysis

package com.titliel.shiftleft;
import java.util.*;
import java.util.stream.Collectors;
/**
* Advanced vulnerability processing and analysis
*/
public class VulnerabilityProcessor {
private final List<VulnerabilityFilter> filters;
private final List<VulnerabilityEnricher> enrichers;
private final RiskCalculator riskCalculator;
public VulnerabilityProcessor() {
this.filters = new ArrayList<>();
this.enrichers = new ArrayList<>();
this.riskCalculator = new RiskCalculator();
initializeFilters();
initializeEnrichers();
}
private void initializeFilters() {
// Filter out low confidence vulnerabilities
filters.add(new ConfidenceFilter(0.7));
// Filter out vulnerabilities in test code
filters.add(new FilePathFilter("**/test/**"));
// Filter by severity threshold
filters.add(new SeverityFilter(SeverityLevel.LOW));
}
private void initializeEnrichers() {
// Add CWE information
enrichers.add(new CWEEnricher());
// Add exploitability information
enrichers.add(new ExploitabilityEnricher());
// Add business context
enrichers.add(new BusinessContextEnricher());
}
/**
* Process and enhance vulnerabilities
*/
public void processVulnerabilities(ScanResult result) {
List<Vulnerability> vulnerabilities = result.getVulnerabilities();
// Apply filters
vulnerabilities = applyFilters(vulnerabilities);
// Apply enrichers
vulnerabilities = applyEnrichers(vulnerabilities);
// Calculate risk scores
vulnerabilities = calculateRiskScores(vulnerabilities);
// Sort by severity and risk
vulnerabilities.sort(new VulnerabilityComparator());
// Update result with processed vulnerabilities
result.getVulnerabilities().clear();
result.getVulnerabilities().addAll(vulnerabilities);
}
private List<Vulnerability> applyFilters(List<Vulnerability> vulnerabilities) {
List<Vulnerability> filtered = new ArrayList<>(vulnerabilities);
for (VulnerabilityFilter filter : filters) {
filtered = filter.apply(filtered);
}
return filtered;
}
private List<Vulnerability> applyEnrichers(List<Vulnerability> vulnerabilities) {
List<Vulnerability> enriched = new ArrayList<>();
for (Vulnerability vulnerability : vulnerabilities) {
Vulnerability enrichedVuln = vulnerability;
for (VulnerabilityEnricher enricher : enrichers) {
enrichedVuln = enricher.enrich(enrichedVuln);
}
enriched.add(enrichedVuln);
}
return enriched;
}
private List<Vulnerability> calculateRiskScores(List<Vulnerability> vulnerabilities) {
return vulnerabilities.stream()
.map(riskCalculator::calculateRisk)
.collect(Collectors.toList());
}
/**
* Group vulnerabilities by category and severity
*/
public Map<String, List<Vulnerability>> groupVulnerabilities(ScanResult result) {
return result.getVulnerabilities().stream()
.collect(Collectors.groupingBy(Vulnerability::getCategory));
}
/**
* Get vulnerability trends over time
*/
public VulnerabilityTrends analyzeTrends(List<ScanResult> historicalResults) {
VulnerabilityTrends trends = new VulnerabilityTrends();
for (ScanResult result : historicalResults) {
trends.addSnapshot(result.getScanTimestamp(), result);
}
return trends;
}
}
/**
* Vulnerability filter interface
*/
interface VulnerabilityFilter {
List<Vulnerability> apply(List<Vulnerability> vulnerabilities);
}
/**
* Filter by confidence level
*/
class ConfidenceFilter implements VulnerabilityFilter {
private final double minConfidence;
public ConfidenceFilter(double minConfidence) {
this.minConfidence = minConfidence;
}
@Override
public List<Vulnerability> apply(List<Vulnerability> vulnerabilities) {
return vulnerabilities.stream()
.filter(v -> v.getConfidence() >= minConfidence)
.collect(Collectors.toList());
}
}
/**
* Filter by file path pattern
*/
class FilePathFilter implements VulnerabilityFilter {
private final String excludePattern;
public FilePathFilter(String excludePattern) {
this.excludePattern = excludePattern;
}
@Override
public List<Vulnerability> apply(List<Vulnerability> vulnerabilities) {
return vulnerabilities.stream()
.filter(v -> !v.getFilePath().matches(convertPattern(excludePattern)))
.collect(Collectors.toList());
}
private String convertPattern(String pattern) {
return pattern.replace("**", ".*").replace("*", "[^/]*");
}
}
/**
* Filter by severity
*/
class SeverityFilter implements VulnerabilityFilter {
private final SeverityLevel minSeverity;
public SeverityFilter(SeverityLevel minSeverity) {
this.minSeverity = minSeverity;
}
@Override
public List<Vulnerability> apply(List<Vulnerability> vulnerabilities) {
return vulnerabilities.stream()
.filter(v -> v.getSeverity().ordinal() <= minSeverity.ordinal())
.collect(Collectors.toList());
}
}
/**
* Vulnerability enricher interface
*/
interface VulnerabilityEnricher {
Vulnerability enrich(Vulnerability vulnerability);
}
/**
* Add CWE information
*/
class CWEEnricher implements VulnerabilityEnricher {
private final Map<String, String> cweDescriptions;
public CWEEnricher() {
this.cweDescriptions = loadCWEDescriptions();
}
@Override
public Vulnerability enrich(Vulnerability vulnerability) {
String cweId = vulnerability.getCweId();
if (cweId != null && cweDescriptions.containsKey(cweId)) {
vulnerability.addMetadata("cweDescription", cweDescriptions.get(cweId));
}
return vulnerability;
}
private Map<String, String> loadCWEDescriptions() {
// Load CWE descriptions from file or database
Map<String, String> descriptions = new HashMap<>();
descriptions.put("CWE-79", "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')");
descriptions.put("CWE-89", "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')");
// Add more CWE descriptions
return descriptions;
}
}
/**
* Risk calculator for vulnerabilities
*/
class RiskCalculator {
public Vulnerability calculateRisk(Vulnerability vulnerability) {
double riskScore = calculateBaseRisk(vulnerability);
// Adjust based on context
riskScore *= getContextMultiplier(vulnerability);
vulnerability.addMetadata("riskScore", riskScore);
vulnerability.addMetadata("riskLevel", getRiskLevel(riskScore));
return vulnerability;
}
private double calculateBaseRisk(Vulnerability vulnerability) {
double baseScore = vulnerability.getSeverity().ordinal() * 2.5;
baseScore += vulnerability.getConfidence() * 0.5;
// Adjust for data flow complexity
if (vulnerability.getDataFlow() != null) {
baseScore += vulnerability.getDataFlow().getPropagationPath().size() * 0.2;
}
return Math.min(baseScore, 10.0);
}
private double getContextMultiplier(Vulnerability vulnerability) {
double multiplier = 1.0;
// Increase risk for authentication-related vulnerabilities
if (vulnerability.getCategory().toLowerCase().contains("auth")) {
multiplier *= 1.5;
}
// Decrease risk for info-level vulnerabilities
if (vulnerability.getSeverity() == SeverityLevel.INFO) {
multiplier *= 0.5;
}
return multiplier;
}
private String getRiskLevel(double riskScore) {
if (riskScore >= 7.5) return "CRITICAL";
if (riskScore >= 5.0) return "HIGH";
if (riskScore >= 2.5) return "MEDIUM";
return "LOW";
}
}
/**
* Vulnerability comparator for sorting
*/
class VulnerabilityComparator implements Comparator<Vulnerability> {
@Override
public int compare(Vulnerability v1, Vulnerability v2) {
// First by severity
int severityCompare = Integer.compare(v1.getSeverity().ordinal(), v2.getSeverity().ordinal());
if (severityCompare != 0) return severityCompare;
// Then by confidence
return Double.compare(v2.getConfidence(), v1.getConfidence());
}
}
/**
* Vulnerability trends analysis
*/
class VulnerabilityTrends {
private final Map<Date, ScanResult> snapshots;
public VulnerabilityTrends() {
this.snapshots = new TreeMap<>();
}
public void addSnapshot(Date timestamp, ScanResult result) {
snapshots.put(timestamp, result);
}
public Map<String, List<Integer>> getTrendByCategory() {
Map<String, List<Integer>> trends = new HashMap<>();
for (Map.Entry<Date, ScanResult> entry : snapshots.entrySet()) {
ScanResult result = entry.getValue();
Map<String, List<Vulnerability>> grouped = result.getVulnerabilities().stream()
.collect(Collectors.groupingBy(Vulnerability::getCategory));
for (Map.Entry<String, List<Vulnerability>> group : grouped.entrySet()) {
trends.computeIfAbsent(group.getKey(), k -> new ArrayList<>())
.add(group.getValue().size());
}
}
return trends;
}
public boolean isImproving() {
// Analyze if vulnerability counts are decreasing over time
List<Integer> totalCounts = snapshots.values().stream()
.map(ScanResult::getTotalVulnerabilities)
.collect(Collectors.toList());
if (totalCounts.size() < 2) return false;
for (int i = 1; i < totalCounts.size(); i++) {
if (totalCounts.get(i) > totalCounts.get(i - 1)) {
return false;
}
}
return true;
}
}

Dependency Vulnerability Scanner

package com.titliel.shiftleft;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* Dependency vulnerability analyzer
*/
public class DependencyAnalyzer {
private final VulnerabilityDatabase vulnDatabase;
private final List<DependencyScanner> scanners;
public DependencyAnalyzer() {
this.vulnDatabase = new VulnerabilityDatabase();
this.scanners = Arrays.asList(
new MavenDependencyScanner(),
new GradleDependencyScanner()
);
}
public List<Vulnerability> analyzeDependencies(ProjectContext context) {
List<Vulnerability> vulnerabilities = new ArrayList<>();
for (DependencyScanner scanner : scanners) {
if (scanner.supportsProject(context)) {
List<Dependency> dependencies = scanner.scanDependencies(context);
vulnerabilities.addAll(analyzeDependenciesForVulnerabilities(dependencies));
}
}
return vulnerabilities;
}
private List<Vulnerability> analyzeDependenciesForVulnerabilities(List<Dependency> dependencies) {
List<Vulnerability> vulnerabilities = new ArrayList<>();
for (Dependency dependency : dependencies) {
List<Vulnerability> dependencyVulns = vulnDatabase.getVulnerabilities(dependency);
vulnerabilities.addAll(dependencyVulns);
}
return vulnerabilities;
}
}
/**
* Dependency representation
*/
class Dependency {
private final String groupId;
private final String artifactId;
private final String version;
private final String scope;
public Dependency(String groupId, String artifactId, String version, String scope) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
this.scope = scope;
}
// Getters
public String getGroupId() { return groupId; }
public String getArtifactId() { return artifactId; }
public String getVersion() { return version; }
public String getScope() { return scope; }
public String getCoordinates() {
return groupId + ":" + artifactId + ":" + version;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dependency that = (Dependency) o;
return Objects.equals(groupId, that.groupId) &&
Objects.equals(artifactId, that.artifactId) &&
Objects.equals(version, that.version);
}
@Override
public int hashCode() {
return Objects.hash(groupId, artifactId, version);
}
}
/**
* Dependency scanner interface
*/
interface DependencyScanner {
boolean supportsProject(ProjectContext context);
List<Dependency> scanDependencies(ProjectContext context);
}
/**
* Maven dependency scanner
*/
class MavenDependencyScanner implements DependencyScanner {
@Override
public boolean supportsProject(ProjectContext context) {
return Files.exists(context.getProjectDir().resolve("pom.xml"));
}
@Override
public List<Dependency> scanDependencies(ProjectContext context) {
List<Dependency> dependencies = new ArrayList<>();
try {
Path pomFile = context.getProjectDir().resolve("pom.xml");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(pomFile.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", "compile");
if (groupId != null && artifactId != null && version != null) {
dependencies.add(new Dependency(groupId, artifactId, version, scope));
}
}
} catch (Exception e) {
throw new RuntimeException("Failed to parse Maven dependencies", e);
}
return dependencies;
}
private String getElementText(Element parent, String tagName) {
return getElementText(parent, tagName, null);
}
private String getElementText(Element parent, String tagName, String defaultValue) {
NodeList nodes = parent.getElementsByTagName(tagName);
if (nodes.getLength() > 0) {
return nodes.item(0).getTextContent();
}
return defaultValue;
}
}
/**
* Gradle dependency scanner
*/
class GradleDependencyScanner implements DependencyScanner {
@Override
public boolean supportsProject(ProjectContext context) {
return Files.exists(context.getProjectDir().resolve("build.gradle")) ||
Files.exists(context.getProjectDir().resolve("build.gradle.kts"));
}
@Override
public List<Dependency> scanDependencies(ProjectContext context) {
// This is a simplified implementation
// In practice, you would need to parse Gradle files or use Gradle tooling API
List<Dependency> dependencies = new ArrayList<>();
try {
// Execute gradle dependencies command and parse output
ProcessBuilder processBuilder = new ProcessBuilder("gradle", "dependencies");
processBuilder.directory(context.getProjectDir().toFile());
Process process = processBuilder.start();
String output = readProcessOutput(process);
dependencies = parseGradleDependencies(output);
} catch (Exception e) {
// Fallback to basic parsing
dependencies = parseGradleFile(context.getProjectDir());
}
return dependencies;
}
private List<Dependency> parseGradleDependencies(String output) {
// Implement Gradle dependencies output parsing
return new ArrayList<>();
}
private List<Dependency> parseGradleFile(Path projectDir) {
// Basic Gradle file parsing
return new ArrayList<>();
}
private String readProcessOutput(Process process) throws IOException {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
StringBuilder output = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
return output.toString();
}
}
}
/**
* Mock vulnerability database
*/
class VulnerabilityDatabase {
private final Map<String, List<Vulnerability>> vulnerabilityMap;
public VulnerabilityDatabase() {
this.vulnerabilityMap = new HashMap<>();
initializeSampleData();
}
public List<Vulnerability> getVulnerabilities(Dependency dependency) {
String key = dependency.getGroupId() + ":" + dependency.getArtifactId();
return vulnerabilityMap.getOrDefault(key, new ArrayList<>());
}
private void initializeSampleData() {
// Sample vulnerability data
Vulnerability vuln1 = new Vulnerability();
vuln1.setId("CVE-2021-44228");
vuln1.setTitle("Log4Shell - Log4j RCE Vulnerability");
vuln1.setSeverity(SeverityLevel.CRITICAL);
vuln1.setCategory("dependency");
vuln1.setCweId("CWE-502");
vulnerabilityMap.put("org.apache.logging.log4j:log4j-core", Arrays.asList(vuln1));
}
}

Configuration Management

package com.titliel.shiftleft;
import java.io.*;
import java.util.*;
/**
* ShiftLeft configuration manager
*/
public class ShiftLeftConfig {
private static ShiftLeftConfig instance;
private final Properties properties;
private ShiftLeftConfig() {
this.properties = loadProperties();
}
public static synchronized ShiftLeftConfig getInstance() {
if (instance == null) {
instance = new ShiftLeftConfig();
}
return instance;
}
private Properties loadProperties() {
Properties props = new Properties();
// Load from system properties first
props.putAll(System.getProperties());
// Load from configuration file
try (InputStream input = getClass().getClassLoader()
.getResourceAsStream("shiftleft.properties")) {
if (input != null) {
props.load(input);
}
} catch (Exception e) {
// Use defaults
}
// Set defaults for missing properties
setDefaults(props);
return props;
}
private void setDefaults(Properties props) {
props.setProperty("shiftleft.executable", "shiftleft");
props.setProperty("shiftleft.thread.count", "4");
props.setProperty("shiftleft.min.severity", "LOW");
props.setProperty("shiftleft.dataflow.enabled", "true");
props.setProperty("shiftleft.taint.analysis.enabled", "true");
props.setProperty("shiftleft.report.html.enabled", "true");
props.setProperty("shiftleft.report.json.enabled", "true");
props.setProperty("shiftleft.report.sarif.enabled", "true");
props.setProperty("shiftleft.report.junit.enabled", "false");
}
// Getters
public String getShiftLeftExecutable() {
return properties.getProperty("shiftleft.executable", "shiftleft");
}
public int getThreadCount() {
return Integer.parseInt(properties.getProperty("shiftleft.thread.count", "4"));
}
public SeverityLevel getMinSeverity() {
return SeverityLevel.fromString(properties.getProperty("shiftleft.min.severity", "LOW"));
}
public boolean isDataFlowAnalysisEnabled() {
return Boolean.parseBoolean(properties.getProperty("shiftleft.dataflow.enabled", "true"));
}
public boolean isTaintAnalysisEnabled() {
return Boolean.parseBoolean(properties.getProperty("shiftleft.taint.analysis.enabled", "true"));
}
public boolean isHtmlReportEnabled() {
return Boolean.parseBoolean(properties.getProperty("shiftleft.report.html.enabled", "true"));
}
public boolean isJsonReportEnabled() {
return Boolean.parseBoolean(properties.getProperty("shiftleft.report.json.enabled", "true"));
}
public boolean isSarifReportEnabled() {
return Boolean.parseBoolean(properties.getProperty("shiftleft.report.sarif.enabled", "true"));
}
public boolean isJunitReportEnabled() {
return Boolean.parseBoolean(properties.getProperty("shiftleft.report.junit.enabled", "false"));
}
public void setProperty(String key, String value) {
properties.setProperty(key, value);
}
}

Usage Examples

package com.titliel.examples;
import com.titliel.shiftleft.TitlielShiftLeftScanner;
import com.titliel.shiftleft.ProjectContext;
import com.titliel.shiftleft.ScanResult;
import java.nio.file.Paths;
/**
* Example usage of Titliel ShiftLeft Scanner
*/
public class ShiftLeftScanExample {
public static void main(String[] args) {
// Create project context
ProjectContext context = new ProjectContext.Builder()
.setProjectDir(Paths.get("/path/to/your/project"))
.setApplicationName("my-spring-boot-app")
.setVersion("1.0.0")
.addExclude("**/generated/**")
.addProperty("java.version", "11")
.build();
// Create scanner
TitlielShiftLeftScanner scanner = new TitlielShiftLeftScanner();
try {
// Perform scan
ScanResult result = scanner.scanProject(context);
// Analyze results
analyzeScanResult(result);
} finally {
scanner.shutdown();
}
}
private static void analyzeScanResult(ScanResult result) {
System.out.println("=== ShiftLeft Scan Results ===");
System.out.println("Scan Duration: " + result.getScanDuration() + "ms");
System.out.println("Total Vulnerabilities: " + result.getTotalVulnerabilities());
System.out.println("Critical: " + result.getCriticalCount());
System.out.println("High: " + result.getHighCount());
System.out.println("Medium: " + result.getMediumCount());
System.out.println("Low: " + result.getLowCount());
if (!result.isScanSuccessful()) {
System.out.println("Scan completed with errors:");
result.getErrors().forEach(error -> 
System.out.println("  - " + error.getMessage()));
}
// Check for critical vulnerabilities
if (result.hasCriticalVulnerabilities()) {
System.out.println("🚨 CRITICAL VULNERABILITIES FOUND!");
result.getVulnerabilities().stream()
.filter(v -> v.getSeverity() == SeverityLevel.CRITICAL)
.forEach(v -> System.out.println("  - " + v.getTitle() + " in " + v.getFilePath()));
}
// Generate reports path
result.getReports().forEach((type, path) -> 
System.out.println("Report generated: " + type + " -> " + path));
}
}
/**
* CI/CD Integration Example
*/
class CICDIntegration {
public boolean performSecurityGate(ProjectContext context) {
TitlielShiftLeftScanner scanner = new TitlielShiftLeftScanner();
try {
ScanResult result = scanner.scanProject(context);
// Fail build if critical vulnerabilities found
if (result.hasCriticalVulnerabilities()) {
System.err.println("Build failed: Critical vulnerabilities detected");
return false;
}
// Fail build if high vulnerabilities exceed threshold
if (result.getHighCount() > 5) {
System.err.println("Build failed: Too many high severity vulnerabilities");
return false;
}
// Generate reports for artifact storage
result.getReports().forEach((type, path) -> 
System.out.println("Storing security report: " + path));
return true;
} finally {
scanner.shutdown();
}
}
public void performScheduledScan(ProjectContext context) {
TitlielShiftLeftScanner scanner = new TitlielShiftLeftScanner();
try {
ScanResult result = scanner.scanProject(context);
// Send results to security dashboard
sendToSecurityDashboard(result);
// Notify security team if critical issues found
if (result.hasCriticalVulnerabilities()) {
notifySecurityTeam(result);
}
} finally {
scanner.shutdown();
}
}
private void sendToSecurityDashboard(ScanResult result) {
// Integration with security dashboards
System.out.println("Sending scan results to security dashboard");
}
private void notifySecurityTeam(ScanResult result) {
// Send notifications via email, Slack, etc.
System.out.println("Notifying security team about critical vulnerabilities");
}
}

Maven Dependencies

<dependencies>
<!-- ShiftLeft CLI (assumed available in PATH) -->
<!-- JSON Processing -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230227</version>
</dependency>
<!-- XML Processing -->
<dependency>
<groupId>javax.xml.parsers</groupId>
<artifactId>jaxp-api</artifactId>
<version>1.4.5</version>
</dependency>
<!-- File Utilities -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>

Best Practices

1. Integration Strategy

  • Integrate early in development lifecycle
  • Use in CI/CD pipelines for automated scanning
  • Schedule regular scans for ongoing projects

2. Result Management

  • Implement severity-based gating
  • Track vulnerability trends over time
  • Prioritize remediation based on risk

3. Performance Optimization

  • Use appropriate thread counts
  • Cache scan results when possible
  • Exclude generated code and dependencies

4. Custom Rules

  • Develop organization-specific rules
  • Integrate with security policies
  • Regularly update custom rule sets

5. Reporting and Compliance

  • Generate multiple report formats
  • Integrate with security dashboards
  • Maintain audit trails for compliance

This Titliel ShiftLeft integration provides comprehensive SAST capabilities with advanced features like custom rule engines, dependency scanning, and sophisticated reporting for enterprise Java applications.

Leave a Reply

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


Macro Nepal Helper