This project demonstrates how to use Semgrep, a static application security testing (SAST) tool, to detect and fix common security vulnerabilities in Java applications. The repository contains two branches showcasing vulnerable and secure implementations of the same functionality.
java-semgrep-demo/
├── src/main/java/com/example/demo/
│ ├── VulnerableApp.java (vulnerable branch)
│ └── SecureApp.java (fixed branch)
├── .semgrep.yml (Custom Semgrep rules)
├── build.gradle (Gradle build configuration)
├── run-semgrep.sh (Automated scanning script)
└── README.md (This file)
Contains intentionally vulnerable code with common security issues:
- CWE-798: Hardcoded credentials
- CWE-89: SQL Injection
- CWE-78: Command Injection
- CWE-22: Path Traversal
- CWE-327: Use of weak cryptographic algorithms (MD5, DES)
- CWE-330: Use of insufficiently random values
Contains the remediated version with security best practices:
- Environment variables for credentials
- Prepared statements for SQL queries
- Input validation and sanitization
- Path normalization and validation
- Strong cryptography (SHA-256, AES-256)
- SecureRandom for cryptographic operations
Vulnerable:
private static final String DB_PASSWORD = "password123";
private static final String SECRET_KEY = "MySecretKey12345";Secure:
private static final String DB_PASSWORD = System.getenv("DB_PASSWORD");
private static final String SECRET_KEY = System.getenv("SECRET_KEY");Vulnerable:
String query = "SELECT * FROM users WHERE username = '" + username +
"' AND password = '" + password + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);Secure:
String query = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = conn.prepareStatement(query);
pstmt.setString(1, username);
pstmt.setString(2, hashPassword(password));
ResultSet rs = pstmt.executeQuery();Vulnerable:
String command = "echo " + userInput;
Process process = Runtime.getRuntime().exec(command);Secure:
// Avoid shell execution entirely
String sanitized = userInput.replaceAll("[^a-zA-Z0-9\\s]", "");
System.out.println("Message: " + sanitized);Vulnerable:
File file = new File("data/" + filename);
BufferedReader reader = new BufferedReader(new FileReader(file));Secure:
Path basePath = Paths.get("data").toAbsolutePath().normalize();
Path filePath = basePath.resolve(filename).normalize();
if (!filePath.startsWith(basePath)) {
System.out.println("Access denied: Invalid file path");
return;
}Vulnerable:
MessageDigest md = MessageDigest.getInstance("MD5");
Cipher cipher = Cipher.getInstance("DES");Secure:
MessageDigest md = MessageDigest.getInstance("SHA-256");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");Vulnerable:
private static Random random = new Random();Secure:
private static SecureRandom secureRandom = new SecureRandom();- Java 11 or higher
- Gradle
- Git
- macOS/Linux (for shell script) or Windows with Git Bash
brew install semgreppip install semgrepOr using the official installer:
python3 -m pip install semgrepgit clone <repository-url>
cd java-semgrep-demoExecute the provided script to scan both branches:
./run-semgrep.shThis script will:
- Switch to the
vulnerablebranch and run Semgrep - Switch to the
fixedbranch and run Semgrep - Display the results showing vulnerabilities found vs fixed
# Switch to vulnerable branch
git checkout vulnerable
semgrep --config=.semgrep.yml src/
# Switch to fixed branch
git checkout fixed
semgrep --config=.semgrep.yml src/semgrep --config=auto src/semgrep --config=.semgrep.yml --severity ERROR src/./gradlew build# On vulnerable branch
git checkout vulnerable
./gradlew run
# On fixed branch
git checkout fixed
./gradlew runThe .semgrep.yml file contains custom rules for detecting:
- Hardcoded credentials
- SQL injection vulnerabilities
- Command injection vulnerabilities
- Path traversal vulnerabilities
- Weak cryptographic algorithms
- Insecure random number generation
Each rule includes:
- Pattern matching for vulnerable code
- Severity levels (ERROR/WARNING)
- CWE and OWASP references
- Helpful remediation messages
When scanning the vulnerable branch, Semgrep should detect:
- 3 hardcoded credentials (DB_USERNAME, DB_PASSWORD, SECRET_KEY)
- 1 SQL injection vulnerability
- 1 command injection vulnerability
- 1 path traversal vulnerability
- 1 MD5 usage warning
- 1 DES encryption error
- 1 weak random number generator warning
When scanning the fixed branch, Semgrep should find:
- No vulnerabilities (all issues remediated)
This demo helps you understand:
- Common Security Vulnerabilities: How they appear in code and their potential impact
- Secure Coding Practices: Proper techniques to prevent security issues
- Static Analysis Tools: How Semgrep can automatically detect vulnerabilities
- DevSecOps Integration: How to incorporate security scanning into development workflow
- OWASP Top 10: Real examples of top security risks
- Never hardcode credentials - Use environment variables or secure vaults
- Always use parameterized queries - Prevent SQL injection
- Validate and sanitize input - Prevent injection attacks
- Normalize and validate file paths - Prevent directory traversal
- Use strong cryptography - SHA-256+ for hashing, AES for encryption
- Use SecureRandom - For cryptographically secure random values
- Implement defense in depth - Multiple layers of security controls
You can integrate Semgrep into your CI/CD pipeline:
name: Semgrep Security Scan
on: [push, pull_request]
jobs:
semgrep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: returntocorp/semgrep-action@v1
with:
config: .semgrep.ymlsemgrep:
image: returntocorp/semgrep
script:
- semgrep --config=.semgrep.yml src/
only:
- merge_requests
- main- Semgrep Documentation
- OWASP Top 10
- CWE Database
- Java Security Best Practices
- Secure Coding Guidelines
This is a demonstration project for educational purposes. Feel free to:
- Add more vulnerability examples
- Improve the Semgrep rules
- Enhance the secure implementations
- Add unit tests
This project is for educational purposes. Use at your own risk.
The vulnerable code in this repository is intentionally insecure for demonstration purposes. Never use the vulnerable patterns in production code.