diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7f24826 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,43 @@ +name: Java CI + +on: + push: + branches: + - main + - 'feature/**' + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Build with Maven + run: mvn clean verify + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-results + path: target/surefire-reports/ + + - name: Upload coverage report + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: target/site/jacoco/ + diff --git a/pom.xml b/pom.xml index 59ded44..8f2c632 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,11 @@ 1.10.19 test + + com.github.penggle + kaptcha + 2.3.2 + diff --git a/src/main/java/demo/security/servlet/CaptchaServlet.java b/src/main/java/demo/security/servlet/CaptchaServlet.java new file mode 100644 index 0000000..32e92de --- /dev/null +++ b/src/main/java/demo/security/servlet/CaptchaServlet.java @@ -0,0 +1,51 @@ +package demo.security.servlet; + +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; + +import javax.imageio.ImageIO; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.Properties; + +@WebServlet("/captcha") +public class CaptchaServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + // Configure CAPTCHA + Properties properties = new Properties(); + properties.setProperty("kaptcha.image.width", "200"); + properties.setProperty("kaptcha.image.height", "50"); + properties.setProperty("kaptcha.textproducer.char.length", "5"); + properties.setProperty("kaptcha.textproducer.font.names", "Arial"); + + DefaultKaptcha captchaProducer = new DefaultKaptcha(); + captchaProducer.setConfig(new Config(properties)); + + // Generate CAPTCHA + String captchaText = captchaProducer.createText(); + + // Store in session for validation + request.getSession().setAttribute("captcha", captchaText); + + // Create image + BufferedImage captchaImage = captchaProducer.createImage(captchaText); + + // Send image response + response.setContentType("image/jpeg"); + ServletOutputStream out = response.getOutputStream(); + ImageIO.write(captchaImage, "jpg", out); + out.flush(); + out.close(); + } +} diff --git a/src/main/java/demo/security/servlet/ContactFeedbackServlet.java b/src/main/java/demo/security/servlet/ContactFeedbackServlet.java new file mode 100644 index 0000000..abd615e --- /dev/null +++ b/src/main/java/demo/security/servlet/ContactFeedbackServlet.java @@ -0,0 +1,168 @@ +package demo.security.servlet; + +import org.apache.commons.codec.digest.DigestUtils; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.*; +import java.nio.file.Files; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +@WebServlet("/contact-feedback") +public class ContactFeedbackServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + private static final String FEEDBACK_DIR = "/tmp/feedback/"; + + @Override + public void init() throws ServletException { + super.init(); + // Ensure feedback directory exists + new File(FEEDBACK_DIR).mkdirs(); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + // Retrieve feedback by ID with SQL Injection vulnerability + String feedbackId = request.getParameter("id"); + if (feedbackId != null) { + try { + List feedbacks = getFeedbackById(feedbackId); + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.println(""); + out.println("

Feedback Results

"); + for (String feedback : feedbacks) { + // XSS vulnerability - no escaping + out.println("

" + feedback + "

"); + } + out.println(""); + out.close(); + } catch (Exception e) { + throw new ServletException(e); + } + } + + // View feedback file with Path Traversal vulnerability + String filename = request.getParameter("file"); + if (filename != null) { + // Path traversal vulnerability + File file = new File(FEEDBACK_DIR + filename); + if (file.exists()) { + response.setContentType("text/plain"); + Files.copy(file.toPath(), response.getOutputStream()); + } else { + response.sendError(404, "File not found"); + } + } + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + HttpSession session = request.getSession(); + + // Optional CAPTCHA validation + String captchaEnabled = request.getParameter("captcha_enabled"); + if ("true".equals(captchaEnabled)) { + String captchaInput = request.getParameter("captcha"); + String sessionCaptcha = (String) session.getAttribute("captcha"); + + // Weak comparison - timing attack vulnerability + if (!captchaInput.equals(sessionCaptcha)) { + response.sendError(400, "Invalid CAPTCHA"); + return; + } + } + + // Get form parameters + String name = request.getParameter("name"); + String email = request.getParameter("email"); + String subject = request.getParameter("subject"); + String message = request.getParameter("message"); + String priority = request.getParameter("priority"); + + // Weak random token generation + Random random = new Random(); + String token = String.valueOf(random.nextInt(1000000)); + + // Weak MD5 hashing for email + String emailHash = DigestUtils.md5Hex(email); + + // Store feedback in database with SQL Injection + try { + saveFeedback(name, email, subject, message, priority, token, emailHash); + } catch (Exception e) { + throw new ServletException("Error saving feedback", e); + } + + // Save feedback to file with predictable name + String filename = "feedback_" + token + ".txt"; + File feedbackFile = new File(FEEDBACK_DIR + filename); + + // Write sensitive data to file with weak permissions + try (PrintWriter fileWriter = new PrintWriter(new FileWriter(feedbackFile))) { + fileWriter.println("Name: " + name); + fileWriter.println("Email: " + email); + fileWriter.println("Subject: " + subject); + fileWriter.println("Message: " + message); + fileWriter.println("Priority: " + priority); + fileWriter.println("Token: " + token); + fileWriter.println("Email Hash: " + emailHash); + } + + // Log sensitive information + System.out.println("Feedback submitted by: " + email + " with token: " + token); + + // Redirect with token in URL + response.sendRedirect("/contact-feedback?success=true&token=" + token); + } + + private List getFeedbackById(String feedbackId) throws Exception { + // SQL Injection vulnerability + Connection connection = DriverManager.getConnection( + "myJDBCUrl", "myJDBCUser", "myJDBCPass"); + + String query = "SELECT * FROM feedback WHERE id = " + feedbackId; + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(query); + + List feedbacks = new ArrayList<>(); + while (resultSet.next()) { + String feedback = "Name: " + resultSet.getString("name") + + ", Subject: " + resultSet.getString("subject") + + ", Message: " + resultSet.getString("message"); + feedbacks.add(feedback); + } + + connection.close(); + return feedbacks; + } + + private void saveFeedback(String name, String email, String subject, + String message, String priority, String token, String emailHash) throws Exception { + // SQL Injection vulnerability + Connection connection = DriverManager.getConnection( + "myJDBCUrl", "myJDBCUser", "myJDBCPass"); + + String query = "INSERT INTO feedback (name, email, subject, message, priority, token, email_hash) " + + "VALUES ('" + name + "', '" + email + "', '" + subject + "', '" + + message + "', '" + priority + "', '" + token + "', '" + emailHash + "')"; + + Statement statement = connection.createStatement(); + statement.executeUpdate(query); + connection.close(); + } +} diff --git a/src/main/webapp/contact-feedback.jsp b/src/main/webapp/contact-feedback.jsp new file mode 100644 index 0000000..b106993 --- /dev/null +++ b/src/main/webapp/contact-feedback.jsp @@ -0,0 +1,205 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> + + + + + Contact Feedback Form + + + + +
+

Contact Feedback Form

+ + <% + String success = request.getParameter("success"); + String token = request.getParameter("token"); + if ("true".equals(success)) { + %> +
+ Thank you for your feedback! Your submission token is: <%= token %> +
+ <% } %> + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + + + + + +
+
+ +