From b5ffe227e4e9544ecde21f2b2d2d2f05bd480bb8 Mon Sep 17 00:00:00 2001 From: sylvain Date: Wed, 26 Nov 2025 09:58:12 +0100 Subject: [PATCH 1/2] Add contact feedback form with security flows and CAPTCHA (Kaptcha, CVE-2019-16335) --- .settings/org.eclipse.jdt.core.prefs | 16 +-- pom.xml | 6 ++ .../security/servlet/CaptchaImageServlet.java | 37 +++++++ .../servlet/ContactFeedbackServlet.java | 57 +++++++++++ src/main/webapp/captcha.jsp | 9 ++ src/main/webapp/contact-feedback.jsp | 98 +++++++++++++++++++ src/main/webapp/index.jsp | 2 + 7 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 src/main/java/demo/security/servlet/CaptchaImageServlet.java create mode 100644 src/main/java/demo/security/servlet/ContactFeedbackServlet.java create mode 100644 src/main/webapp/captcha.jsp create mode 100644 src/main/webapp/contact-feedback.jsp diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index dcbe70c..7a2988b 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,20 +1,20 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore -org.eclipse.jdt.core.compiler.annotation.nonnull=javax.annotation.Nonnull -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=javax.annotation.ParametersAreNonnullByDefault -org.eclipse.jdt.core.compiler.annotation.nullable=javax.annotation.Nullable -org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 org.eclipse.jdt.core.compiler.compliance=17 org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error org.eclipse.jdt.core.compiler.problem.nullReference=warning -org.eclipse.jdt.core.compiler.problem.nullSpecViolation=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore -org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore -org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled org.eclipse.jdt.core.compiler.processAnnotations=enabled org.eclipse.jdt.core.compiler.release=disabled org.eclipse.jdt.core.compiler.source=17 diff --git a/pom.xml b/pom.xml index 59ded44..44f1b5e 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,12 @@ 1.10.19 test + + + com.github.penggle + kaptcha + 2.3.2 + diff --git a/src/main/java/demo/security/servlet/CaptchaImageServlet.java b/src/main/java/demo/security/servlet/CaptchaImageServlet.java new file mode 100644 index 0000000..d58ec3d --- /dev/null +++ b/src/main/java/demo/security/servlet/CaptchaImageServlet.java @@ -0,0 +1,37 @@ +package demo.security.servlet; + +import com.google.code.kaptcha.Producer; +import com.google.code.kaptcha.util.Config; +import javax.imageio.ImageIO; +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.awt.image.BufferedImage; +import java.io.IOException; +import java.util.Properties; + +@WebServlet("/captcha-image") +public class CaptchaImageServlet extends HttpServlet { + private Producer kaptchaProducer; + + @Override + public void init() throws ServletException { + Properties props = new Properties(); + props.put("kaptcha.textproducer.char.length", "5"); + Config config = new Config(props); + kaptchaProducer = config.getProducerImpl(); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String capText = kaptchaProducer.createText(); + HttpSession session = request.getSession(); + session.setAttribute("contactFeedbackCaptcha", capText); + BufferedImage bi = kaptchaProducer.createImage(capText); + response.setContentType("image/png"); + ImageIO.write(bi, "png", response.getOutputStream()); + } +} 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..fad53c1 --- /dev/null +++ b/src/main/java/demo/security/servlet/ContactFeedbackServlet.java @@ -0,0 +1,57 @@ +package demo.security.servlet; + +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 java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@WebServlet("/contact-feedback") +public class ContactFeedbackServlet extends HttpServlet { + private static final List> FEEDBACKS = new ArrayList<>(); + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String name = request.getParameter("name"); + String email = request.getParameter("email"); + String category = request.getParameter("category"); + String feedback = request.getParameter("feedback"); + String captcha = request.getParameter("captcha"); + String captchaExpected = (String) request.getSession().getAttribute("contactFeedbackCaptcha"); + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + if (captchaExpected == null || captcha == null || !captchaExpected.equalsIgnoreCase(captcha)) { + out.println("

CAPTCHA validation failed!

Back"); + return; + } + Map entry = new HashMap<>(); + entry.put("name", name); + entry.put("email", email); + entry.put("category", category); + entry.put("feedback", feedback); + FEEDBACKS.add(entry); + out.println("

Thank you for your feedback!

Back"); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String email = request.getParameter("email"); + String category = request.getParameter("category"); + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.println("

Feedback Results

"); + for (Map fb : FEEDBACKS) { + if ((email != null && !email.isEmpty() && email.equals(fb.get("email"))) || + (category != null && !category.isEmpty() && category.equals(fb.get("category")))) { + out.println("
Name: " + fb.get("name") + "
Email: " + fb.get("email") + "
Category: " + fb.get("category") + "
Feedback: " + fb.get("feedback") + "

"); + } + } + out.println("Back"); + } +} diff --git a/src/main/webapp/captcha.jsp b/src/main/webapp/captcha.jsp new file mode 100644 index 0000000..ebe0f70 --- /dev/null +++ b/src/main/webapp/captcha.jsp @@ -0,0 +1,9 @@ + +
+ + CAPTCHA + +
diff --git a/src/main/webapp/contact-feedback.jsp b/src/main/webapp/contact-feedback.jsp new file mode 100644 index 0000000..77e8843 --- /dev/null +++ b/src/main/webapp/contact-feedback.jsp @@ -0,0 +1,98 @@ + + + + Contact Feedback Form + + + +

Contact Feedback Form

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ <%@ include file="captcha.jsp" %> + +
+
+

Search Feedback

+

Search by Email

+
+
+ + +
+ +
+

Search by Category

+
+
+ + +
+ +
+
+ + diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp index 8b9a55b..3fdb813 100644 --- a/src/main/webapp/index.jsp +++ b/src/main/webapp/index.jsp @@ -5,5 +5,7 @@ Enter Name: +
+Contact Feedback Form \ No newline at end of file From 687d6848fccd38d765b844efe482d7538d27041e Mon Sep 17 00:00:00 2001 From: sylvain Date: Wed, 26 Nov 2025 10:04:34 +0100 Subject: [PATCH 2/2] Update GitHub Actions workflow to run on all feature branches --- .github/workflows/maven.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 30860ca..8a1ff0a 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -11,7 +11,8 @@ name: SonarQube Analyze on: push: branches: - - main # This will trigger the workflow only on pushes to the main branch + - main + - 'feature/**' # Run workflow on all feature branches pull_request: types: [opened, synchronize, reopened] workflow_dispatch: