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