ShiftLeft Scan in Java: Complete SAST Security Scanning Guide

Introduction to ShiftLeft Scan

ShiftLeft Scan is a Static Application Security Testing (SAST) tool that integrates security scanning into your CI/CD pipeline. It analyzes source code and dependencies to identify security vulnerabilities, secrets, and compliance issues early in the development lifecycle.

Key Features

  • Static Code Analysis: Identifies security vulnerabilities in source code
  • Dependency Scanning: Detects vulnerable third-party libraries
  • Secret Detection: Finds hardcoded secrets and credentials
  • Software Composition Analysis (SCA): Analyzes open-source dependencies
  • CI/CD Integration: Seamless integration with Jenkins, GitHub Actions, etc.
  • Custom Rules: Support for organization-specific security rules

Implementation Guide

Dependencies and Setup

Maven Configuration (pom.xml)

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>shiftleft-scan-demo</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- ShiftLeft Scan Version --> <shiftleft.scan.version>0.1.0</shiftleft.scan.version> <!-- Security Testing Dependencies --> <owasp.dependency.check.version>8.2.1</owasp.dependency.check.version> <spotbugs.version>4.7.3</spotbugs.version> <pmd.version>6.55.0</pmd.version> <checkstyle.version>10.12.0</checkstyle.version> </properties> <dependencies> <!-- Spring Boot for demo application --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>2.1.214</version> <scope>runtime</scope> </dependency> <!-- Security libraries --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-crypto</artifactId> <version>5.7.1</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.2.1</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.3</version> </dependency> <!-- XML Processing --> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency> </dependencies> <build> <plugins> <!-- Maven Compiler Plugin --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>11</source> <target>11</target> <compilerArgs> <arg>-Xlint:all</arg> <arg>-Werror</arg> </compilerArgs> </configuration> </plugin> <!-- ShiftLeft Scan Plugin --> <plugin> <groupId>com.shiftleft</groupId> <artifactId>shiftleft-scan-maven</artifactId> <version>${shiftleft.scan.version}</version> <executions> <execution> <goals> <goal>scan</goal> </goals> <phase>verify</phase> </execution> </executions> <configuration> <appName>${project.artifactId}</appName> <appType>java</appType> <failOnVulnerability>true</failOnVulnerability> <severityThreshold>HIGH</severityThreshold> <outputDirectory>${project.build.directory}/shiftleft-reports</outputDirectory> <includePatterns> <includePattern>**/*.java</includePattern> </includePatterns> <excludePatterns> <excludePattern>**/test/**</excludePattern> <excludePattern>**/generated/**</excludePattern> </excludePatterns> </configuration> </plugin> <!-- OWASP Dependency Check --> <plugin> <groupId>org.owasp</groupId> <artifactId>dependency-check-maven</artifactId> <version>${owasp.dependency.check.version}</version> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> <configuration> <failBuildOnAnyVulnerability>true</failBuildOnAnyVulnerability> <suppressionFile>${project.basedir}/security/dependency-check-suppressions.xml</suppressionFile> <format>HTML</format> <formats>HTML,JSON,XML</formats> </configuration> </plugin> <!-- SpotBugs for static analysis --> <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> <version>${spotbugs.version}</version> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> <configuration> <effort>Max</effort> <threshold>Low</threshold> <failOnError>true</failOnError> <excludeFilterFile>${project.basedir}/security/spotbugs-exclude.xml</excludeFilterFile> </configuration> </plugin> <!-- PMD for code quality --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> <version>${pmd.version}</version> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> <configuration> <failurePriority>3</failurePriority> <rulesets> <ruleset>${project.basedir}/security/pmd-ruleset.xml</ruleset> </rulesets> <printFailingErrors>true</printFailingErrors> </configuration> </plugin> <!-- Checkstyle for coding standards --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> <version>${checkstyle.version}</version> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> <configuration> <configLocation>google_checks.xml</configLocation> <failsOnError>true</failsOnError> <consoleOutput>true</consoleOutput> </configuration> </plugin> <!-- Surefire for unit tests --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M9</version> <configuration> <includes> <include>**/*Test.java</include> </includes> <excludes> <exclude>**/*IntegrationTest.java</exclude> </excludes> </configuration> </plugin> </plugins> </build> <reporting> <plugins> <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> <version>${spotbugs.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> <version>${pmd.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> <version>${checkstyle.version}</version> </plugin> </plugins> </reporting> </project>

Security Configuration Files

ShiftLeft Configuration (shiftleft.yml)

# ShiftLeft Scan Configuration version: 1 scan: # Application configuration app: name: "shiftleft-scan-demo" type: "java" version: "1.0.0" # Analysis configuration analysis: language: "java" build-tool: "maven" java-version: "11" # Security thresholds security: fail-on-severity: "HIGH" max-critical: 0 max-high: 5 max-medium: 10 max-low: 20 # Scan targets targets: source: directories: - "src/main/java" exclude: - "**/test/**" - "**/generated/**" dependencies: enabled: true include-optional: false secrets: enabled: true custom-patterns: - name: "AWS Access Key" pattern: "AKIA[0-9A-Z]{16}" - name: "Generic API Key" pattern: "(?i)(api[_-]?key|secret)[\\s]*[=:][\\s]*['\\\"]([0-9a-zA-Z]{32,})['\\\"]" # Reporting reporting: formats: - "json" - "html" - "sarif" output-directory: "target/shiftleft-reports" # CI/CD Integration ci: fail-on-vulnerability: true upload-results: true branch: "${GIT_BRANCH}" commit: "${GIT_COMMIT}" build-url: "${BUILD_URL}" # Custom rules custom-rules: - id: "CUSTOM-SQL-INJECTION" pattern: "Statement.executeQuery.*\\$.*" message: "Potential SQL Injection with string concatenation" severity: "HIGH" category: "sql-injection" - id: "CUSTOM-HARDCODED-SECRET" pattern: "(?i)password.*=.*['\\\"].{4,}['\\\"]" message: "Potential hardcoded password" severity: "HIGH" category: "hardcoded-secret"

OWASP Dependency Check Suppression (security/dependency-check-suppressions.xml)

<?xml version="1.0" encoding="UTF-8"?> <suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd"> <!-- Suppress false positives for Spring Framework --> <suppress> <notes><![CDATA[ False positive for Spring Framework CVE ]]></notes> <cve>CVE-2018-1258</cve> </suppress> <!-- Suppress false positives for Jackson --> <suppress> <notes><![CDATA[ False positive for Jackson CVE ]]></notes> <cve>CVE-2020-36518</cve> </suppress> <!-- Suppress specific dependencies --> <suppress> <notes><![CDATA[ Suppress vulnerabilities in test dependencies ]]></notes> <filePath regex="true">.*test.*\.jar$</filePath> </suppress> <!-- Suppress low severity vulnerabilities --> <suppress base="true"> <notes><![CDATA[ Suppress low severity vulnerabilities ]]></notes> <severity>LOW</severity> </suppress> </suppressions>

SpotBugs Exclusions (security/spotbugs-exclude.xml)

<?xml version="1.0" encoding="UTF-8"?> <FindBugsFilter> <!-- Exclude generated code --> <Match> <Class name="~.*\.R\$.*"/> </Match> <!-- Exclude test code --> <Match> <Class name="~.*\.*Test.*"/> </Match> <!-- Exclude specific false positives --> <Match> <Bug pattern="EI_EXPOSE_REP,EI_EXPOSE_REP2"/> <Class name="com.example.shiftleft.model.*"/> </Match> <!-- Exclude serialization issues in DTOs --> <Match> <Bug pattern="SE_NO_SERIALVERSIONID"/> <Class name="~.*\.dto\..*"/> </Match> <!-- Exclude specific methods --> <Match> <Class name="com.example.shiftleft.controller.UserController"/> <Method name="getUserPassword"/> <Bug pattern="HARD_CODE_PASSWORD"/> </Match> </FindBugsFilter>

PMD Ruleset (security/pmd-ruleset.xml)

<?xml version="1.0"?> <ruleset name="Custom Security Rules" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> <description>Custom PMD rules for security scanning</description> <!-- Import standard rules --> <rule ref="category/java/security.xml"> <exclude name="InsecureCryptoUsage"/> </rule> <rule ref="category/java/bestpractices.xml"> <exclude name="GuardLogStatement"/> </rule> <rule ref="category/java/errorprone.xml"> <exclude name="BeanMembersShouldSerialize"/> </rule> <!-- Custom security rules --> <rule name="AvoidSystemOutPrint" language="java" message="Avoid using System.out.print, use logger instead" class="net.sourceforge.pmd.lang.rule.XPathRule"> <description> System.out.print should be avoided in production code </description> <priority>3</priority> <properties> <property name="xpath"> <value> <![CDATA[ //PrimaryExpression[ PrimaryPrefix/Name[ @Image='System.out.print' or @Image='System.out.println' or @Image='System.err.print' or @Image='System.err.println' ] ] ]]> </value> </property> </properties> </rule> <rule name="AvoidHardCodedCredentials" language="java" message="Avoid hardcoded credentials" class="net.sourceforge.pmd.lang.rule.XPathRule"> <description> Hardcoded credentials should be avoided </description> <priority>1</priority> <properties> <property name="xpath"> <value> <![CDATA[ //Literal[ matches(@Image, '(?i).*password.*|.*secret.*|.*key.*') and string-length(@Image) > 10 ] ]]> </value> </property> </properties> </rule> </ruleset>

Sample Application Code for Scanning

Secure User Controller

package com.example.shiftleft.controller; import com.example.shiftleft.model.User; import com.example.shiftleft.service.UserService; import com.example.shiftleft.util.SecurityValidator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.List; /** * User controller with security best practices * This class demonstrates secure coding patterns */ @RestController @RequestMapping("/api/users") public class UserController { private static final Logger logger = LoggerFactory.getLogger(UserController.class); @Autowired private UserService userService; @Autowired private SecurityValidator securityValidator; /** * Get user by ID - Secure implementation */ @GetMapping("/{id}") public ResponseEntity<User> getUser(@PathVariable String id, @RequestHeader("Authorization") String authHeader) { // Validate input if (!securityValidator.isValidUserId(id)) { logger.warn("Invalid user ID provided: {}", id); return ResponseEntity.badRequest().build(); } // Validate authentication if (!securityValidator.isValidAuthHeader(authHeader)) { logger.warn("Invalid authentication header"); return ResponseEntity.status(401).build(); } try { User user = userService.findUserById(id); if (user == null) { return ResponseEntity.notFound().build(); } // Sanitize sensitive data before returning user.sanitizeSensitiveData(); return ResponseEntity.ok(user); } catch (Exception e) { logger.error("Error fetching user: {}", id, e); return ResponseEntity.internalServerError().build(); } } /** * Create user - Secure implementation */ @PostMapping public ResponseEntity<User> createUser(@Valid @RequestBody User user, @RequestHeader("Authorization") String authHeader) { // Validate authentication if (!securityValidator.isValidAuthHeader(authHeader)) { return ResponseEntity.status(401).build(); } // Validate user data if (!securityValidator.isValidUser(user)) { return ResponseEntity.badRequest().build(); } try { User createdUser = userService.createUser(user); createdUser.sanitizeSensitiveData(); logger.info("User created successfully: {}", createdUser.getId()); return ResponseEntity.ok(createdUser); } catch (Exception e) { logger.error("Error creating user", e); return ResponseEntity.internalServerError().build(); } } /** * Search users - Secure implementation with input validation */ @GetMapping("/search") public ResponseEntity<List<User>> searchUsers(@RequestParam String query, @RequestHeader("Authorization") String authHeader) { // Validate authentication if (!securityValidator.isValidAuthHeader(authHeader)) { return ResponseEntity.status(401).build(); } // Validate and sanitize search query String sanitizedQuery = securityValidator.sanitizeSearchQuery(query); if (sanitizedQuery == null) { return ResponseEntity.badRequest().build(); } try { List<User> users = userService.searchUsers(sanitizedQuery); users.forEach(User::sanitizeSensitiveData); return ResponseEntity.ok(users); } catch (Exception e) { logger.error("Error searching users with query: {}", sanitizedQuery, e); return ResponseEntity.internalServerError().build(); } } }

User Service with Security Measures

package com.example.shiftleft.service; import com.example.shiftleft.model.User; import com.example.shiftleft.repository.UserRepository; import com.example.shiftleft.util.PasswordHasher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.sql.SQLException; import java.util.List; import java.util.regex.Pattern; /** * User service with security best practices * Demonstrates secure coding patterns for ShiftLeft scanning */ @Service public class UserService { private static final Logger logger = LoggerFactory.getLogger(UserService.class); // SQL injection prevention pattern private static final Pattern SQL_INJECTION_PATTERN = Pattern.compile("([';]+|(--)+|(\\|\\|)+|(&&)+)", Pattern.CASE_INSENSITIVE); @Autowired private UserRepository userRepository; @Autowired private PasswordHasher passwordHasher; /** * Find user by ID - Secure implementation */ public User findUserById(String id) { // Validate input if (id == null || id.trim().isEmpty()) { throw new IllegalArgumentException("User ID cannot be null or empty"); } // Check for potential SQL injection if (containsSqlInjection(id)) { logger.warn("Potential SQL injection attempt detected in user ID: {}", id); throw new SecurityException("Invalid user ID format"); } try { return userRepository.findById(id) .orElse(null); } catch (DataAccessException e) { logger.error("Database error while fetching user: {}", id, e); throw new RuntimeException("Unable to fetch user", e); } } /** * Create user with secure password handling */ @Transactional public User createUser(User user) { // Validate user object if (user == null) { throw new IllegalArgumentException("User cannot be null"); } // Hash password before storage if (user.getPassword() != null) { String hashedPassword = passwordHasher.hashPassword(user.getPassword()); user.setPassword(hashedPassword); } try { User savedUser = userRepository.save(user); logger.info("User created with ID: {}", savedUser.getId()); return savedUser; } catch (DataAccessException e) { logger.error("Database error while creating user", e); throw new RuntimeException("Unable to create user", e); } } /** * Search users with input sanitization */ public List<User> searchUsers(String query) { // Validate and sanitize query if (query == null || query.trim().isEmpty()) { throw new IllegalArgumentException("Search query cannot be null or empty"); } String sanitizedQuery = sanitizeSearchQuery(query); if (sanitizedQuery == null) { throw new SecurityException("Invalid search query"); } try { return userRepository.findByUsernameContaining(sanitizedQuery); } catch (DataAccessException e) { logger.error("Database error while searching users with query: {}", sanitizedQuery, e); throw new RuntimeException("Unable to search users", e); } } /** * Authenticate user securely */ public boolean authenticateUser(String username, String password) { if (username == null || password == null) { return false; } try { User user = userRepository.findByUsername(username); if (user == null) { // Use constant time comparison to prevent timing attacks passwordHasher.dummyHashComparison(); return false; } return passwordHasher.verifyPassword(password, user.getPassword()); } catch (DataAccessException e) { logger.error("Database error during authentication for user: {}", username, e); return false; } } /** * Check for SQL injection patterns */ private boolean containsSqlInjection(String input) { return SQL_INJECTION_PATTERN.matcher(input).find(); } /** * Sanitize search query */ private String sanitizeSearchQuery(String query) { if (query == null) return null; // Remove potentially dangerous characters String sanitized = query.replaceAll("[';\"\\\\]", ""); // Limit length if (sanitized.length() > 100) { sanitized = sanitized.substring(0, 100); } return sanitized.trim(); } }

Security Utility Classes

package com.example.shiftleft.util; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.encrypt.Encryptors; import org.springframework.security.crypto.encrypt.TextEncryptor; import org.springframework.stereotype.Component; import java.security.SecureRandom; import java.util.Base64; import java.util.regex.Pattern; /** * Security utilities for password hashing and validation */ @Component public class PasswordHasher { private final BCryptPasswordEncoder bcryptEncoder; private final SecureRandom secureRandom; // Do NOT hardcode this in production - use environment variables private static final String ENCRYPTION_PASSWORD = System.getenv("ENCRYPTION_PASSWORD"); private static final String ENCRYPTION_SALT = System.getenv("ENCRYPTION_SALT"); public PasswordHasher() { this.bcryptEncoder = new BCryptPasswordEncoder(12); this.secureRandom = new SecureRandom(); } /** * Hash password using bcrypt */ public String hashPassword(String plainPassword) { if (plainPassword == null || plainPassword.trim().isEmpty()) { throw new IllegalArgumentException("Password cannot be null or empty"); } if (plainPassword.length() < 8) { throw new IllegalArgumentException("Password must be at least 8 characters long"); } return bcryptEncoder.encode(plainPassword); } /** * Verify password against hash */ public boolean verifyPassword(String plainPassword, String hashedPassword) { if (plainPassword == null || hashedPassword == null) { return false; } return bcryptEncoder.matches(plainPassword, hashedPassword); } /** * Dummy hash comparison to prevent timing attacks */ public void dummyHashComparison() { // Perform a dummy hash to prevent timing attacks String dummyPassword = generateRandomString(16); bcryptEncoder.encode(dummyPassword); } /** * Generate secure random string */ private String generateRandomString(int length) { byte[] bytes = new byte[length]; secureRandom.nextBytes(bytes); return Base64.getEncoder().encodeToString(bytes); } } /** * Security validator for input validation */ @Component public class SecurityValidator { private static final Pattern USER_ID_PATTERN = Pattern.compile("^[a-zA-Z0-9-_]{1,50}$"); private static final Pattern USERNAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_]{3,30}$"); private static final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Za-z0-9+_.-]+@(.+)$"); private static final Pattern AUTH_HEADER_PATTERN = Pattern.compile("^Bearer [a-zA-Z0-9-_]+\\.[a-zA-Z0-9-_]+\\.[a-zA-Z0-9-_]+$"); /** * Validate user ID format */ public boolean isValidUserId(String userId) { return userId != null && USER_ID_PATTERN.matcher(userId).matches(); } /** * Validate username format */ public boolean isValidUsername(String username) { return username != null && USERNAME_PATTERN.matcher(username).matches(); } /** * Validate email format */ public boolean isValidEmail(String email) { return email != null && EMAIL_PATTERN.matcher(email).matches(); } /** * Validate authentication header format */ public boolean isValidAuthHeader(String authHeader) { return authHeader != null && AUTH_HEADER_PATTERN.matcher(authHeader).matches(); } /** * Validate user object */ public boolean isValidUser(User user) { if (user == null) return false; return isValidUsername(user.getUsername()) && isValidEmail(user.getEmail()) && (user.getPassword() == null || user.getPassword().length() >= 8); } /** * Sanitize search query */ public String sanitizeSearchQuery(String query) { if (query == null) return null; // Remove potentially dangerous characters String sanitized = query.replaceAll("[';\"\\\\<>]", ""); // Limit length if (sanitized.length() > 100) { sanitized = sanitized.substring(0, 100); } return sanitized.trim(); } /** * Check for potential XSS in input */ public boolean containsXss(String input) { if (input == null) return false; Pattern xssPattern = Pattern.compile( "(<script|javascript:|onload=|onerror=|onclick=)", Pattern.CASE_INSENSITIVE ); return xssPattern.matcher(input).find(); } }

Data Models with Security Considerations

package com.example.shiftleft.model; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import javax.persistence.*; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import java.time.LocalDateTime; /** * User entity with security considerations */ @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.UUID) private String id; @NotBlank(message = "Username is required") @Size(min = 3, max = 30, message = "Username must be between 3 and 30 characters") @Column(unique = true, nullable = false) private String username; @NotBlank(message = "Email is required") @Email(message = "Email should be valid") @Column(unique = true, nullable = false) private String email; @NotBlank(message = "Password is required") @Size(min = 8, message = "Password must be at least 8 characters long") @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) // Never serialize password @Column(nullable = false) private String password; @Column(name = "created_at") private LocalDateTime createdAt; @Column(name = "updated_at") private LocalDateTime updatedAt; // Security-sensitive fields that should not be exposed @JsonIgnore @Column(name = "password_reset_token") private String passwordResetToken; @JsonIgnore @Column(name = "failed_login_attempts") private Integer failedLoginAttempts; // Constructors public User() { this.createdAt = LocalDateTime.now(); this.updatedAt = LocalDateTime.now(); this.failedLoginAttempts = 0; } public User(String username, String email, String password) { this(); this.username = username; this.email = email; this.password = password; } // Getters and setters public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public LocalDateTime getCreatedAt() { return createdAt; } public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } public LocalDateTime getUpdatedAt() { return updatedAt; } public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } @JsonIgnore public String getPasswordResetToken() { return passwordResetToken; } public void setPasswordResetToken(String passwordResetToken) { this.passwordResetToken = passwordResetToken; } @JsonIgnore public Integer getFailedLoginAttempts() { return failedLoginAttempts; } public void setFailedLoginAttempts(Integer failedLoginAttempts) { this.failedLoginAttempts = failedLoginAttempts; } /** * Sanitize sensitive data before serialization */ public void sanitizeSensitiveData() { this.password = null; this.passwordResetToken = null; } @PreUpdate public void preUpdate() { this.updatedAt = LocalDateTime.now(); } @Override public String toString() { return String.format("User{id='%s', username='%s', email='%s'}", id, username, email); } }

Security Vulnerabilities Examples (for testing)

package com.example.shiftleft.vulnerabilities; import java.sql.*; import java.util.regex.Pattern; /** * This class contains intentional security vulnerabilities * for testing ShiftLeft Scan detection capabilities * DO NOT USE IN PRODUCTION */ public class VulnerableCodeExamples { // ❌ VULNERABLE: Hardcoded credentials private static final String DB_PASSWORD = "mysecretpassword123"; private static final String API_KEY = "sk_live_1234567890abcdef"; private static final String AWS_ACCESS_KEY = "AKIAIOSFODNN7EXAMPLE"; /** * ❌ VULNERABLE: SQL Injection vulnerable method */ public static void vulnerableSqlInjection(String userInput) { try { Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/mydb", "user", DB_PASSWORD); // Vulnerable: direct string concatenation String query = "SELECT * FROM users WHERE username = '" + userInput + "'"; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(query); // SQL Injection vulnerability while (rs.next()) { System.out.println(rs.getString("username")); } } catch (SQLException e) { e.printStackTrace(); } } /** * ❌ VULNERABLE: Command Injection vulnerable method */ public static void vulnerableCommandInjection(String filename) { try { // Vulnerable: direct command execution Runtime.getRuntime().exec("ls -la " + filename); // Command Injection vulnerability } catch (Exception e) { e.printStackTrace(); } } /** * ❌ VULNERABLE: XSS vulnerable method */ public static String vulnerableXss(String userInput) { // Vulnerable: no input sanitization return "<div>" + userInput + "</div>"; // XSS vulnerability } /** * ❌ VULNERABLE: Insecure deserialization */ public static void vulnerableDeserialization(byte[] data) { try { // Vulnerable: no validation of serialized data java.io.ObjectInputStream ois = new java.io.ObjectInputStream( new java.io.ByteArrayInputStream(data)); Object obj = ois.readObject(); // Insecure deserialization vulnerability } catch (Exception e) { e.printStackTrace(); } } /** * ❌ VULNERABLE: Weak random number generation */ public static int vulnerableRandom() { // Vulnerable: using insecure random return new java.util.Random().nextInt(); // Weak randomness } /** * ✅ SECURE: SQL Injection protected method */ public static void secureSqlInjection(String userInput, Connection conn) { try { // Secure: using prepared statements String query = "SELECT * FROM users WHERE username = ?"; PreparedStatement stmt = conn.prepareStatement(query); stmt.setString(1, userInput); // Parameterized query ResultSet rs = stmt.executeQuery(); while (rs.next()) { System.out.println(rs.getString("username")); } } catch (SQLException e) { e.printStackTrace(); } } /** * ✅ SECURE: Command Injection protected method */ public static void secureCommandInjection(String filename) { try { // Secure: validate and sanitize input if (!isValidFilename(filename)) { throw new IllegalArgumentException("Invalid filename"); } String[] command = {"ls", "-la", filename}; Runtime.getRuntime().exec(command); // Safe command execution } catch (Exception e) { e.printStackTrace(); } } /** * ✅ SECURE: XSS protected method */ public static String secureXss(String userInput) { // Secure: sanitize input String sanitized = userInput.replaceAll("<", "&lt;") .replaceAll(">", "&gt;") .replaceAll("\"", "&quot;") .replaceAll("'", "&#x27;"); return "<div>" + sanitized + "</div>"; } private static boolean isValidFilename(String filename) { return Pattern.matches("^[a-zA-Z0-9._-]+$", filename); } }

CI/CD Integration

GitHub Actions Workflow (.github/workflows/shiftleft-scan.yml)

name: ShiftLeft Security Scan on: push: branches: [ main, develop ] pull_request: branches: [ main ] jobs: security-scan: name: ShiftLeft Security Scan runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up Java uses: actions/setup-java@v3 with: java-version: '11' distribution: 'temurin' cache: 'maven' - name: Cache Maven packages uses: actions/cache@v3 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - name: Run ShiftLeft Scan uses: shiftleftsecurity/scan-action@v2 with: output: reports fail-on-vulnerability: true severity-threshold: HIGH env: SHIFTLEFT_ACCESS_TOKEN: ${{ secrets.SHIFTLEFT_ACCESS_TOKEN }} - name: Run OWASP Dependency Check run: mvn dependency-check:check - name: Run SpotBugs run: mvn spotbugs:check - name: Run PMD run: mvn pmd:check - name: Run Checkstyle run: mvn checkstyle:check - name: Upload Security Reports uses: actions/upload-artifact@v3 with: name: security-reports path: | target/shiftleft-reports/ target/dependency-check-report.html target/spotbugs.xml target/pmd.xml target/checkstyle-result.xml - name: Security Scan Summary run: | echo "## Security Scan Results" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### ShiftLeft Scan" >> $GITHUB_STEP_SUMMARY echo "- ✅ Static Code Analysis Completed" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### OWASP Dependency Check" >> $GITHUB_STEP_SUMMARY echo "- ✅ Dependency Vulnerability Scan Completed" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Static Analysis Tools" >> $GITHUB_STEP_SUMMARY echo "- ✅ SpotBugs Analysis Completed" >> $GITHUB_STEP_SUMMARY echo "- ✅ PMD Analysis Completed" >> $GITHUB_STEP_SUMMARY echo "- ✅ Checkstyle Analysis Completed" >> $GITHUB_STEP_SUMMARY

Jenkins Pipeline (Jenkinsfile)

pipeline { agent any environment { SHIFTLEFT_ACCESS_TOKEN = credentials('shiftleft-access-token') JAVA_HOME = '/usr/lib/jvm/java-11-openjdk' } stages { stage('Checkout') { steps { checkout scm } } stage('Build') { steps { sh 'mvn clean compile -DskipTests' } } stage('Unit Tests') { steps { sh 'mvn test' } post { always { junit 'target/surefire-reports/*.xml' } } } stage('Security Scan') { parallel { stage('ShiftLeft Scan') { steps { sh ''' mvn com.shiftleft:shiftleft-scan-maven:scan \ -DappName=${JOB_NAME} \ -DfailOnVulnerability=true \ -DseverityThreshold=HIGH \ -DoutputDirectory=target/shiftleft-reports ''' } post { always { publishHTML([ allowMissing: false, alwaysLinkToLastBuild: true, keepAll: true, reportDir: 'target/shiftleft-reports', reportFiles: 'report.html', reportName: 'ShiftLeft Scan Report' ]) } } } stage('OWASP Dependency Check') { steps { sh 'mvn dependency-check:check' } post { always { publishHTML([ allowMissing: false, alwaysLinkToLastBuild: true, keepAll: true, reportDir: 'target', reportFiles: 'dependency-check-report.html', reportName: 'OWASP Dependency Check Report' ]) } } } stage('Static Analysis') { steps { sh ''' mvn spotbugs:check pmd:check checkstyle:check ''' } post { always { recordIssues( tools: [ spotBugs(pattern: 'target/spotbugs.xml'), pmdParser(pattern: 'target/pmd.xml'), checkStyle(pattern: 'target/checkstyle-result.xml') ] ) } } } } } stage('Integration Tests') { steps { sh 'mvn verify -DskipUnitTests' } post { always { junit 'target/failsafe-reports/*.xml' } } } } post { always { archiveArtifacts artifacts: 'target/*.jar', fingerprint: true cleanWs() } success { emailext ( subject: "SUCCESS: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'", body: "The build completed successfully.\n\nCheck console output at ${env.BUILD_URL}", to: "${env.BUILD_USER_EMAIL}" ) } failure { emailext ( subject: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'", body: "The build failed. Security vulnerabilities were detected.\n\nCheck console output at ${env.BUILD_URL}", to: "${env.BUILD_USER_EMAIL}" ) } } }

Security Test Cases

package com.example.shiftleft.security; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import static org.junit.jupiter.api.Assertions.*; /** * Security test cases for ShiftLeft scanning */ @SpringBootTest @ActiveProfiles("test") public class SecurityTests { @Test public void testSqlInjectionPrevention() { SecurityValidator validator = new SecurityValidator(); // Test valid inputs assertTrue(validator.isValidUserId("user123")); assertTrue(validator.isValidUserId("user-123")); assertTrue(validator.isValidUserId("user_123")); // Test SQL injection attempts assertFalse(validator.isValidUserId("user' OR '1'='1")); assertFalse(validator.isValidUserId("admin'; DROP TABLE users; --")); assertFalse(validator.isValidUserId("test\" OR \"1\"=\"1")); } @Test public void testXssPrevention() { SecurityValidator validator = new SecurityValidator(); // Test XSS attempts assertTrue(validator.containsXss("<script>alert('XSS')</script>")); assertTrue(validator.containsXss("javascript:alert('XSS')")); assertTrue(validator.containsXss("onload=\"maliciousCode()\"")); assertTrue(validator.containsXss("onerror=\"alert(1)\"")); // Test safe inputs assertFalse(validator.containsXss("Hello World")); assertFalse(validator.containsXss("user123")); assertFalse(validator.containsXss("[email protected]")); } @Test public void testInputSanitization() { SecurityValidator validator = new SecurityValidator(); // Test search query sanitization String maliciousQuery = "test'; DROP TABLE users; --"; String sanitized = validator.sanitizeSearchQuery(maliciousQuery); assertFalse(sanitized.contains("'")); assertFalse(sanitized.contains(";")); assertFalse(sanitized.contains("--")); } @Test public void testPasswordHashing() { PasswordHasher hasher = new PasswordHasher(); String password = "SecurePassword123!"; String hashed = hasher.hashPassword(password); assertNotNull(hashed); assertNotEquals(password, hashed); // Verify password assertTrue(hasher.verifyPassword(password, hashed)); assertFalse(hasher.verifyPassword("WrongPassword", hashed)); } @Test public void testAuthenticationHeaderValidation() { SecurityValidator validator = new SecurityValidator(); // Valid JWT token format assertTrue(validator.isValidAuthHeader( "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" )); // Invalid formats assertFalse(validator.isValidAuthHeader("Basic dXNlcjpwYXNz")); assertFalse(validator.isValidAuthHeader("Bearer invalid.token.format")); assertFalse(validator.isValidAuthHeader("")); assertFalse(validator.isValidAuthHeader(null)); } }

Configuration for Different Environments

application-security.yml

# Security configuration for different environments spring: profiles: default security: encryption: password: ${ENCRYPTION_PASSWORD:default-dev-password} salt: ${ENCRYPTION_SALT:default-dev-salt} shiftleft: scan: enabled: true fail-on-vulnerability: false severity-threshold: MEDIUM --- spring: profiles: test security: encryption: password: ${ENCRYPTION_PASSWORD:test-password} salt: ${ENCRYPTION_SALT:test-salt} shiftleft: scan: enabled: true fail-on-vulnerability: true severity-threshold: HIGH --- spring: profiles: production security: encryption: password: ${ENCRYPTION_PASSWORD} salt: ${ENCRYPTION_SALT} shiftleft: scan: enabled: true fail-on-vulnerability: true severity-threshold: HIGH

Best Practices

1. Secure Coding Guidelines

/** * Security best practices for Java development */ public class SecurityBestPractices { // ✅ Use environment variables for secrets private static final String DB_PASSWORD = System.getenv("DB_PASSWORD"); // ✅ Use secure random for cryptographic operations private final SecureRandom secureRandom = new SecureRandom(); // ✅ Use prepared statements for database operations public void secureDatabaseOperation(Connection conn, String userInput) { String query = "SELECT * FROM users WHERE username = ?"; try (PreparedStatement stmt = conn.prepareStatement(query)) { stmt.setString(1, userInput); ResultSet rs = stmt.executeQuery(); // Process results } catch (SQLException e) { // Log properly, don't expose stack traces logger.error("Database error occurred", e); } } // ✅ Validate and sanitize all inputs public String sanitizeInput(String input) { if (input == null) return null; // Remove potentially dangerous characters return input.replaceAll("[<>\"']", ""); } // ✅ Use proper password hashing public String hashPassword(String password) { return BCrypt.hashpw(password, BCrypt.gensalt(12)); } // ✅ Implement proper error handling public void handleError(Exception e) { // Log error for internal use logger.error("An error occurred", e); // Return generic error message to user throw new RuntimeException("An unexpected error occurred"); } }

2. Regular Security Scanning

#!/bin/bash # security-scan.sh echo "Starting comprehensive security scan..." # ShiftLeft Scan echo "Running ShiftLeft Scan..." mvn com.shiftleft:shiftleft-scan-maven:scan # OWASP Dependency Check echo "Running OWASP Dependency Check..." mvn dependency-check:check # Static Analysis echo "Running SpotBugs..." mvn spotbugs:check echo "Running PMD..." mvn pmd:check echo "Running Checkstyle..." mvn checkstyle:check # Generate report echo "Generating security report..." ./generate-security-report.sh echo "Security scan completed!"

Conclusion

This comprehensive ShiftLeft Scan implementation provides:

  • Complete SAST integration with Maven builds
  • Multiple security scanning tools (ShiftLeft, OWASP, SpotBugs, PMD, Checkstyle)
  • CI/CD pipeline integration with GitHub Actions and Jenkins
  • Security-focused code examples with both vulnerable and secure implementations
  • Custom security configurations for different environments
  • Comprehensive testing for security controls

Key benefits include early vulnerability detection, automated security testing in CI/CD, comprehensive coverage of security issues, and integration with development workflows.

Remember to:

  • Run security scans regularly in your CI/CD pipeline
  • Address high-severity vulnerabilities immediately
  • Keep security tools and dependencies updated
  • Educate developers on secure coding practices
  • Use environment variables for sensitive configuration
  • Review and update security rules regularly

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