diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..6c0df92 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,51 @@ +name: "CodeQL Analysis" + +on: + push: + branches: [ "main", "master" ] + pull_request: + branches: [ "main", "master" ] + schedule: + - cron: '15 2 * * 1' # Weekly on Mondays at 2:15 AM + +jobs: + analyze: + name: Analyze Java Code + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + queries: +security-and-quality + build-mode: none + dependency-caching: true + + # Autobuild attempts to build any compiled languages (Java, C#, Go, etc.) + # If this step fails, remove it and run the build manually instead + #- name: Autobuild + # uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7974262 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar + +# Compiled class files +*.class + +# Log files +*.log + +# IDE files +.idea/ +*.iws +*.iml +*.ipr +.vscode/ +.settings/ +.project +.classpath + +# OS generated files +.DS_Store +Thumbs.db + +# Temporary files +*.tmp +*.bak +*.swp +*~.nib + +# Package files +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar \ No newline at end of file diff --git a/README.md b/README.md index 3b81e20..ee621d5 100644 --- a/README.md +++ b/README.md @@ -1 +1,56 @@ -# coding-agent-example-java-codeql-autobuild \ No newline at end of file +# coding-agent-example-java-codeql-autobuild + +A demonstration Java application with intentional security vulnerabilities for CodeQL scanning. + +## Overview + +This repository contains a simple Java application built with Maven that includes several common security vulnerabilities designed to be detected by GitHub's CodeQL static analysis tool. + +## Application Structure + +- **Main Application**: `com.example.app.VulnerableApplication` - Entry point that demonstrates various vulnerabilities +- **Database Layer**: `com.example.database.UserDatabase` - Contains SQL injection vulnerabilities +- **Security Utils**: `com.example.security.CryptoUtils` - Contains weak cryptographic implementations +- **Web/File Handling**: `com.example.web.FileController` - Contains path traversal and command injection vulnerabilities +- **LDAP Authentication**: `com.example.ldap.LdapAuth` - Contains LDAP injection vulnerabilities + +## Intentional Vulnerabilities + +This application contains the following types of security vulnerabilities: + +1. **SQL Injection** - Direct string concatenation in SQL queries +2. **Command Injection** - Unsanitized user input passed to system commands +3. **Path Traversal** - File operations without path validation +4. **LDAP Injection** - Unescaped user input in LDAP filters +5. **Weak Cryptography** - Use of MD5 and weak random number generation +6. **Hard-coded Secrets** - Embedded credentials and encryption keys + +## CodeQL Analysis + +The repository includes a GitHub Actions workflow (`.github/workflows/codeql-analysis.yml`) that: + +- Runs CodeQL analysis on push and pull requests +- Uses the autobuild functionality for Java +- Includes security-and-quality queries for comprehensive coverage +- Runs weekly scheduled scans + +## Building and Running + +```bash +# Compile the application +mvn clean compile + +# Run tests +mvn test + +# Run the application (demonstrates vulnerabilities) +mvn exec:java -Dexec.mainClass="com.example.app.VulnerableApplication" +``` + +## Warning + +⚠️ **This application contains intentional security vulnerabilities and should never be deployed in a production environment.** It is designed solely for educational purposes and CodeQL demonstration. + +## License + +This project is for educational and demonstration purposes only. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8dd88b8 --- /dev/null +++ b/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + com.example + vulnerable-app + 1.0.0 + jar + + Vulnerable Java Application - Test2 + A simple Java application with intentional vulnerabilities for CodeQL scanning demonstration + + + 11 + 11 + UTF-8 + + + + + + + com.google.guava + guava + 32.1.1-jre + + + + org.slf4j + slf4j-api + 2.0.7 + + + + org.apache.commons + commons-lang3 + 3.12.0 + + + + + mysql + mysql-connector-java + 8.0.33 + + + + + org.springframework + spring-web + 5.3.21 + + + + + com.fasterxml.jackson.core + jackson-databind + 2.13.3 + + + + + junit + junit + 4.13.2 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 11 + 11 + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M7 + + + + diff --git a/src/main/java/com/example/app/VulnerableApplication.java b/src/main/java/com/example/app/VulnerableApplication.java new file mode 100644 index 0000000..f2570d0 --- /dev/null +++ b/src/main/java/com/example/app/VulnerableApplication.java @@ -0,0 +1,50 @@ +package com.example.app; + +import com.example.database.UserDatabase; +import com.example.security.CryptoUtils; +import com.example.web.FileController; +import com.example.ldap.LdapAuth; + +/** + * Main application class demonstrating various Java vulnerabilities + * that should be detected by CodeQL scanning. + */ +public class VulnerableApplication { + + public static void main(String[] args) { + System.out.println("Starting Vulnerable Application..."); + + // Demonstrate various vulnerable components + UserDatabase userDb = new UserDatabase(); + CryptoUtils crypto = new CryptoUtils(); + FileController fileController = new FileController(); + LdapAuth ldapAuth = new LdapAuth(); + + // Example usage that would trigger vulnerabilities + String userInput = args.length > 0 ? args[0] : "admin"; + String password = args.length > 1 ? args[1] : "password123"; + + // SQL Injection vulnerability + userDb.authenticateUser(userInput, password); + userDb.deleteUser(userInput); + + // Weak cryptography + String token = crypto.generateToken(); + System.out.println("Generated token: " + token); + + // Path traversal vulnerability + String filename = args.length > 2 ? args[2] : "../../etc/passwd"; + fileController.readFile(filename); + + // Command injection + String command = args.length > 3 ? args[3] : "ls -la"; + fileController.executeCommand(command); + fileController.executeSystemCommand(command); + + // LDAP injection + ldapAuth.authenticateUser(userInput, password); + ldapAuth.getUserInfo(userInput); + + System.out.println("Application completed."); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/database/UserDatabase.java b/src/main/java/com/example/database/UserDatabase.java new file mode 100644 index 0000000..a111788 --- /dev/null +++ b/src/main/java/com/example/database/UserDatabase.java @@ -0,0 +1,90 @@ +package com.example.database; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.Statement; + +/** + * Database class with intentional SQL injection vulnerabilities + * to demonstrate CodeQL detection capabilities. + */ +public class UserDatabase { + + private static final String DB_URL = "jdbc:mysql://localhost:3306/testdb"; + private static final String DB_USER = "root"; + private static final String DB_PASSWORD = "password"; + + /** + * VULNERABLE: SQL Injection vulnerability - user input directly concatenated + * This should trigger a high/critical CodeQL alert + */ + public boolean authenticateUser(String username, String password) { + try { + Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); + Statement stmt = conn.createStatement(); + + // VULNERABILITY: Direct string concatenation leads to SQL injection + String query = "SELECT * FROM users WHERE username = '" + username + + "' AND password = '" + password + "'"; + + System.out.println("Executing query: " + query); + ResultSet rs = stmt.executeQuery(query); + + boolean authenticated = rs.next(); + + rs.close(); + stmt.close(); + conn.close(); + + return authenticated; + + } catch (Exception e) { + System.err.println("Database error: " + e.getMessage()); + return false; + } + } + + /** + * VULNERABLE: Another SQL injection point + */ + public void updateUserProfile(String userId, String email, String fullName) { + try { + Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); + Statement stmt = conn.createStatement(); + + // VULNERABILITY: String concatenation in UPDATE statement + String updateQuery = "UPDATE users SET email = '" + email + + "', full_name = '" + fullName + + "' WHERE user_id = " + userId; + + stmt.executeUpdate(updateQuery); + + stmt.close(); + conn.close(); + + } catch (Exception e) { + System.err.println("Update failed: " + e.getMessage()); + } + } + + /** + * VULNERABLE: Dynamic query construction - another SQL injection pattern + */ + public void deleteUser(String userIdParam) { + try { + Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); + Statement stmt = conn.createStatement(); + + // VULNERABILITY: Direct concatenation in DELETE statement + String sql = "DELETE FROM users WHERE id = " + userIdParam; + stmt.executeUpdate(sql); + + stmt.close(); + conn.close(); + + } catch (Exception e) { + System.err.println("Delete failed: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/ldap/LdapAuth.java b/src/main/java/com/example/ldap/LdapAuth.java new file mode 100644 index 0000000..1acfa4d --- /dev/null +++ b/src/main/java/com/example/ldap/LdapAuth.java @@ -0,0 +1,82 @@ +package com.example.ldap; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingEnumeration; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchResult; +import java.util.Hashtable; + +/** + * LDAP authentication with intentional LDAP injection vulnerability + * to demonstrate CodeQL detection capabilities. + */ +public class LdapAuth { + + private static final String LDAP_URL = "ldap://localhost:389"; + private static final String BASE_DN = "dc=example,dc=com"; + + /** + * VULNERABLE: LDAP injection vulnerability - user input directly concatenated + * This should trigger a high/critical CodeQL alert + */ + public boolean authenticateUser(String username, String password) { + try { + Hashtable env = new Hashtable<>(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, LDAP_URL); + + DirContext ctx = new InitialDirContext(env); + + // VULNERABILITY: Direct concatenation allows LDAP injection + String filter = "(&(uid=" + username + ")(userPassword=" + password + "))"; + + System.out.println("LDAP filter: " + filter); + + NamingEnumeration results = ctx.search(BASE_DN, filter, null); + boolean authenticated = results.hasMore(); + + results.close(); + ctx.close(); + + return authenticated; + + } catch (Exception e) { + System.err.println("LDAP authentication failed: " + e.getMessage()); + return false; + } + } + + /** + * VULNERABLE: Another LDAP injection pattern + */ + public String getUserInfo(String userId) { + try { + Hashtable env = new Hashtable<>(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, LDAP_URL); + + DirContext ctx = new InitialDirContext(env); + + // VULNERABILITY: LDAP injection in search filter + String searchFilter = "(uid=" + userId + ")"; + NamingEnumeration results = ctx.search(BASE_DN, searchFilter, null); + + if (results.hasMore()) { + SearchResult result = results.next(); + Attributes attrs = result.getAttributes(); + return attrs.toString(); + } + + results.close(); + ctx.close(); + + } catch (Exception e) { + System.err.println("LDAP search failed: " + e.getMessage()); + } + + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/security/CryptoUtils.java b/src/main/java/com/example/security/CryptoUtils.java new file mode 100644 index 0000000..1bbdc04 --- /dev/null +++ b/src/main/java/com/example/security/CryptoUtils.java @@ -0,0 +1,77 @@ +package com.example.security; + +import java.util.Random; +import java.security.MessageDigest; + +/** + * Security utilities with intentional cryptographic vulnerabilities + * to demonstrate CodeQL detection capabilities. + */ +public class CryptoUtils { + + // VULNERABLE: Using weak random number generator + private static final Random random = new Random(); + + /** + * VULNERABLE: Uses weak random number generation for security tokens + * This should trigger a CodeQL alert for insecure randomness + */ + public String generateToken() { + StringBuilder token = new StringBuilder(); + + // VULNERABILITY: Using java.util.Random for security-sensitive operations + for (int i = 0; i < 32; i++) { + int randomChar = random.nextInt(36); + if (randomChar < 10) { + token.append((char) ('0' + randomChar)); + } else { + token.append((char) ('a' + randomChar - 10)); + } + } + + return token.toString(); + } + + /** + * VULNERABLE: Uses weak random for session IDs + */ + public String generateSessionId() { + // VULNERABILITY: Predictable session ID generation + long sessionId = System.currentTimeMillis() + random.nextInt(1000); + return Long.toString(sessionId); + } + + /** + * VULNERABLE: Weak hash function usage + */ + public String hashPassword(String password) { + try { + // VULNERABILITY: Using MD5 for password hashing (weak algorithm) + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] hash = md.digest(password.getBytes()); + + StringBuilder hexString = new StringBuilder(); + for (byte b : hash) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + + return hexString.toString(); + + } catch (Exception e) { + System.err.println("Hashing failed: " + e.getMessage()); + return password; // VULNERABILITY: Fallback to plaintext + } + } + + /** + * VULNERABLE: Hard-coded encryption key + */ + public String getEncryptionKey() { + // VULNERABILITY: Hard-coded secret key + return "MySecretKey123456789"; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/web/FileController.java b/src/main/java/com/example/web/FileController.java new file mode 100644 index 0000000..23853f4 --- /dev/null +++ b/src/main/java/com/example/web/FileController.java @@ -0,0 +1,122 @@ +package com.example.web; + +import java.io.File; +import java.io.FileInputStream; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.FileWriter; + +/** + * File controller with intentional path traversal vulnerabilities + * to demonstrate CodeQL detection capabilities. + */ +public class FileController { + + private static final String BASE_DIR = "/tmp/uploads/"; + + /** + * VULNERABLE: Path traversal vulnerability - no input validation + * This should trigger a high/critical CodeQL alert + */ + public String readFile(String filename) { + try { + // VULNERABILITY: Direct concatenation allows path traversal attacks + File file = new File(BASE_DIR + filename); + + System.out.println("Reading file: " + file.getAbsolutePath()); + + if (!file.exists()) { + System.out.println("File does not exist: " + filename); + return null; + } + + StringBuilder content = new StringBuilder(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(new FileInputStream(file)))) { + + String line; + while ((line = reader.readLine()) != null) { + content.append(line).append("\n"); + } + } + + return content.toString(); + + } catch (Exception e) { + System.err.println("Error reading file: " + e.getMessage()); + return null; + } + } + + /** + * VULNERABLE: Another path traversal vulnerability in file writing + */ + public boolean writeFile(String filename, String content) { + try { + // VULNERABILITY: No validation on filename parameter + File file = new File(BASE_DIR + filename); + + // Create parent directories if they don't exist + file.getParentFile().mkdirs(); + + try (FileWriter writer = new FileWriter(file)) { + writer.write(content); + } + + System.out.println("File written: " + file.getAbsolutePath()); + return true; + + } catch (Exception e) { + System.err.println("Error writing file: " + e.getMessage()); + return false; + } + } + + /** + * VULNERABLE: Command injection vulnerability + */ + public String executeCommand(String userCommand) { + try { + // VULNERABILITY: Direct execution of user input + Process process = Runtime.getRuntime().exec("sh -c " + userCommand); + + StringBuilder output = new StringBuilder(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()))) { + + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + } + } + + return output.toString(); + + } catch (Exception e) { + System.err.println("Command execution failed: " + e.getMessage()); + return null; + } + } + + /** + * VULNERABLE: Another command injection pattern using ProcessBuilder + */ + public String executeSystemCommand(String cmd) { + try { + // VULNERABILITY: ProcessBuilder with unsanitized input + ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", cmd); + Process process = pb.start(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + StringBuilder result = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + result.append(line).append("\n"); + } + + return result.toString(); + } catch (Exception e) { + return "Error: " + e.getMessage(); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/example/VulnerableApplicationTest.java b/src/test/java/com/example/VulnerableApplicationTest.java new file mode 100644 index 0000000..86750ac --- /dev/null +++ b/src/test/java/com/example/VulnerableApplicationTest.java @@ -0,0 +1,44 @@ +package com.example; + +import com.example.app.VulnerableApplication; +import com.example.database.UserDatabase; +import com.example.security.CryptoUtils; +import com.example.web.FileController; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Basic tests to verify the application compiles and runs + */ +public class VulnerableApplicationTest { + + @Test + public void testUserDatabaseCreation() { + UserDatabase db = new UserDatabase(); + assertNotNull("UserDatabase should be created", db); + } + + @Test + public void testCryptoUtilsCreation() { + CryptoUtils crypto = new CryptoUtils(); + assertNotNull("CryptoUtils should be created", crypto); + + String token = crypto.generateToken(); + assertNotNull("Token should be generated", token); + assertTrue("Token should have length", token.length() > 0); + } + + @Test + public void testFileControllerCreation() { + FileController controller = new FileController(); + assertNotNull("FileController should be created", controller); + } + + @Test + public void testHashPasswordReturnsValue() { + CryptoUtils crypto = new CryptoUtils(); + String hash = crypto.hashPassword("testpassword"); + assertNotNull("Hash should not be null", hash); + assertTrue("Hash should have content", hash.length() > 0); + } +} \ No newline at end of file