-
Notifications
You must be signed in to change notification settings - Fork 48
Add Contact Feedback Form with Security Testing #69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f47569f
a4d4d9c
a3b341d
1170328
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| package demo.security.servlet; | ||
|
|
||
| import demo.security.util.ContactFeedbackUtil; | ||
| import demo.security.util.ContactFeedbackException; | ||
|
|
||
| import javax.servlet.*; | ||
| import javax.servlet.http.*; | ||
| import javax.servlet.annotation.*; | ||
| import java.io.IOException; | ||
| import java.io.PrintWriter; | ||
| import java.sql.SQLException; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| @WebServlet("/contact-feedback") | ||
| public class ContactFeedbackServlet extends HttpServlet { | ||
|
|
||
| // Constants for string literals | ||
| private static final String FIELD_NAME = "name"; | ||
| private static final String FIELD_EMAIL = "email"; | ||
| private static final String FIELD_FEEDBACK = "feedback"; | ||
| private static final String FIELD_CATEGORY = "category"; | ||
| private static final String FIELD_ID = "id"; | ||
| private static final String HTML_START = "<html><body>"; | ||
| private static final String HTML_END = "</body></html>"; | ||
|
|
||
| @Override | ||
| protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { | ||
| String name = request.getParameter(FIELD_NAME); | ||
| String email = request.getParameter(FIELD_EMAIL); | ||
| String feedback = request.getParameter(FIELD_FEEDBACK); | ||
| String category = request.getParameter(FIELD_CATEGORY); | ||
|
|
||
| ContactFeedbackUtil util = createUtil(); | ||
|
|
||
| // Store feedback with SQL injection vulnerability | ||
| String feedbackId = storeFeedback(util, name, email, feedback, category); | ||
Check noticeCode scanning / SonarQube Exceptions should not be thrown from servlet methods Low
Handle the following exception that could be thrown by "storeFeedback": ServletException. See more on SonarQube
|
||
|
|
||
| // Retrieve and display feedback | ||
| List<Map<String, String>> feedbackList = getFeedbackByEmail(util, email); | ||
Check noticeCode scanning / SonarQube Exceptions should not be thrown from servlet methods Low
Handle the following exception that could be thrown by "getFeedbackByEmail": ServletException. See more on SonarQube
|
||
|
|
||
| renderFeedbackSubmissionResponse(response, feedbackId, feedbackList); | ||
Check noticeCode scanning / SonarQube Exceptions should not be thrown from servlet methods Low
Handle the following exception that could be thrown by "renderFeedbackSubmissionResponse": IOException. See more on SonarQube
|
||
| } | ||
|
|
||
| @Override | ||
| protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { | ||
| String searchEmail = request.getParameter(FIELD_EMAIL); | ||
| String searchCategory = request.getParameter(FIELD_CATEGORY); | ||
|
|
||
| ContactFeedbackUtil util = createUtil(); | ||
Check noticeCode scanning / SonarQube Exceptions should not be thrown from servlet methods Low
Handle the following exception that could be thrown by "createUtil": ServletException. See more on SonarQube
|
||
| List<Map<String, String>> feedbackList = getFeedbackList(util, searchEmail, searchCategory); | ||
Check noticeCode scanning / SonarQube Exceptions should not be thrown from servlet methods Low
Handle the following exception that could be thrown by "getFeedbackList": ServletException. See more on SonarQube
|
||
|
|
||
| renderFeedbackSearchResponse(response, feedbackList); | ||
Check noticeCode scanning / SonarQube Exceptions should not be thrown from servlet methods Low
Handle the following exception that could be thrown by "renderFeedbackSearchResponse": IOException. See more on SonarQube
|
||
| } | ||
|
|
||
| private ContactFeedbackUtil createUtil() throws ServletException { | ||
| try { | ||
| return new ContactFeedbackUtil(); | ||
| } catch (SQLException e) { | ||
| throw new ServletException("Database connection failed", e); | ||
| } | ||
| } | ||
|
|
||
| private String storeFeedback(ContactFeedbackUtil util, String name, String email, String feedback, String category) throws ServletException { | ||
| try { | ||
| return util.storeFeedback(name, email, feedback, category); | ||
| } catch (ContactFeedbackException e) { | ||
| throw new ServletException("Failed to store feedback", e); | ||
| } | ||
| } | ||
|
|
||
| private List<Map<String, String>> getFeedbackByEmail(ContactFeedbackUtil util, String email) throws ServletException { | ||
| try { | ||
| return util.getFeedbackByEmail(email); | ||
| } catch (ContactFeedbackException e) { | ||
| throw new ServletException("Failed to retrieve feedback", e); | ||
| } | ||
| } | ||
|
|
||
| private List<Map<String, String>> getFeedbackList(ContactFeedbackUtil util, String searchEmail, String searchCategory) throws ServletException { | ||
| try { | ||
| if (searchEmail != null && !searchEmail.isEmpty()) { | ||
| // SQL injection vulnerability in search | ||
| return util.getFeedbackByEmail(searchEmail); | ||
| } else if (searchCategory != null && !searchCategory.isEmpty()) { | ||
| return util.getFeedbackByCategory(searchCategory); | ||
| } else { | ||
| return util.getAllFeedback(); | ||
| } | ||
| } catch (ContactFeedbackException e) { | ||
| throw new ServletException("Failed to search feedback", e); | ||
| } | ||
| } | ||
|
|
||
| private void renderFeedbackSubmissionResponse(HttpServletResponse response, String feedbackId, List<Map<String, String>> feedbackList) throws IOException { | ||
| response.setContentType("text/html"); | ||
| PrintWriter out = response.getWriter(); | ||
|
|
||
| out.println(HTML_START); | ||
| out.println("<h2>Thank you for your feedback!</h2>"); | ||
| out.println("<p>Your feedback ID: " + feedbackId + "</p>"); | ||
| out.println("<h3>Your previous feedback:</h3>"); | ||
|
|
||
| renderFeedbackItems(out, feedbackList); | ||
|
|
||
| out.println(HTML_END); | ||
| out.close(); | ||
| } | ||
|
|
||
| private void renderFeedbackSearchResponse(HttpServletResponse response, List<Map<String, String>> feedbackList) throws IOException { | ||
| response.setContentType("text/html"); | ||
| PrintWriter out = response.getWriter(); | ||
|
|
||
| out.println(HTML_START); | ||
| out.println("<h2>Feedback Results</h2>"); | ||
|
|
||
| for (Map<String, String> fb : feedbackList) { | ||
| // XSS vulnerability | ||
| out.println("<div>"); | ||
| out.println("<p><b>ID:</b> " + fb.get(FIELD_ID) + "</p>"); | ||
| out.println("<p><b>Name:</b> " + fb.get(FIELD_NAME) + "</p>"); | ||
| out.println("<p><b>Email:</b> " + fb.get(FIELD_EMAIL) + "</p>"); | ||
| out.println("<p><b>Category:</b> " + fb.get(FIELD_CATEGORY) + "</p>"); | ||
| out.println("<p><b>Feedback:</b> " + fb.get(FIELD_FEEDBACK) + "</p>"); | ||
| out.println("</div><hr>"); | ||
| } | ||
|
|
||
| out.println(HTML_END); | ||
| out.close(); | ||
| } | ||
|
|
||
| private void renderFeedbackItems(PrintWriter out, List<Map<String, String>> feedbackList) { | ||
| for (Map<String, String> fb : feedbackList) { | ||
| // XSS vulnerability - directly outputting user input | ||
| out.println("<div>"); | ||
| out.println("<p><b>Name:</b> " + fb.get(FIELD_NAME) + "</p>"); | ||
| out.println("<p><b>Email:</b> " + fb.get(FIELD_EMAIL) + "</p>"); | ||
| out.println("<p><b>Category:</b> " + fb.get(FIELD_CATEGORY) + "</p>"); | ||
| out.println("<p><b>Feedback:</b> " + fb.get(FIELD_FEEDBACK) + "</p>"); | ||
| out.println("</div><hr>"); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package demo.security.util; | ||
|
|
||
| public class ContactFeedbackException extends Exception { | ||
|
|
||
| public ContactFeedbackException(String message) { | ||
| super(message); | ||
| } | ||
|
|
||
| public ContactFeedbackException(String message, Throwable cause) { | ||
| super(message, cause); | ||
| } | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| package demo.security.util; | ||
|
|
||
| import java.sql.*; | ||
| import java.util.ArrayList; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Random; | ||
|
|
||
| public class ContactFeedbackUtil { | ||
|
|
||
| // Constants for string literals | ||
| private static final String FIELD_ID = "id"; | ||
| private static final String FIELD_NAME = "name"; | ||
| private static final String FIELD_EMAIL = "email"; | ||
| private static final String FIELD_FEEDBACK = "feedback"; | ||
| private static final String FIELD_CATEGORY = "category"; | ||
|
|
||
| private Connection connection; | ||
| private final Random random; | ||
| private final String feedbackBasePath; | ||
|
|
||
| public ContactFeedbackUtil() throws SQLException { | ||
| connection = DriverManager.getConnection( | ||
| "myJDBCUrl", "myJDBCUser", "myJDBCPass"); | ||
Check failureCode scanning / SonarQube Credentials should not be hard-coded Critical
Revoke and change this password, as it is compromised. See more on SonarQube
|
||
| this.random = new Random(); | ||
| this.feedbackBasePath = System.getProperty("feedback.base.path", "/var/feedback/"); | ||
| } | ||
|
|
||
| // SQL Injection vulnerability - string concatenation | ||
| public String storeFeedback(String name, String email, String feedback, String category) throws ContactFeedbackException { | ||
| String feedbackId = generateFeedbackId(); | ||
|
|
||
| // SQL injection through string concatenation | ||
| String query = "INSERT INTO feedback (id, name, email, feedback, category) VALUES ('" | ||
| + feedbackId + "', '" | ||
| + name + "', '" | ||
| + email + "', '" | ||
| + feedback + "', '" | ||
| + category + "')"; | ||
|
|
||
| try (Statement statement = connection.createStatement()) { | ||
| statement.executeUpdate(query); | ||
Check failureCode scanning / SonarQube Database queries should not be vulnerable to injection attacks Critical
Change this code to not construct SQL queries directly from user-controlled data. See more on SonarQube
|
||
| } catch (SQLException e) { | ||
| throw new ContactFeedbackException("Failed to store feedback", e); | ||
| } | ||
|
|
||
| return feedbackId; | ||
| } | ||
|
|
||
| // SQL Injection vulnerability - string concatenation in WHERE clause | ||
| public List<Map<String, String>> getFeedbackByEmail(String email) throws ContactFeedbackException { | ||
| String query = "SELECT id, name, email, feedback, category FROM feedback WHERE email = '" + email + "'"; | ||
|
|
||
| try (Statement statement = connection.createStatement(); | ||
| ResultSet resultSet = statement.executeQuery(query)) { | ||
Check failureCode scanning / SonarQube Database queries should not be vulnerable to injection attacks Critical
Change this code to not construct SQL queries directly from user-controlled data. See more on SonarQube
|
||
|
|
||
| List<Map<String, String>> feedbackList = new ArrayList<>(); | ||
| while (resultSet.next()) { | ||
| Map<String, String> feedback = new HashMap<>(); | ||
| feedback.put(FIELD_ID, resultSet.getString(FIELD_ID)); | ||
| feedback.put(FIELD_NAME, resultSet.getString(FIELD_NAME)); | ||
| feedback.put(FIELD_EMAIL, resultSet.getString(FIELD_EMAIL)); | ||
| feedback.put(FIELD_FEEDBACK, resultSet.getString(FIELD_FEEDBACK)); | ||
| feedback.put(FIELD_CATEGORY, resultSet.getString(FIELD_CATEGORY)); | ||
| feedbackList.add(feedback); | ||
| } | ||
|
|
||
| return feedbackList; | ||
| } catch (SQLException e) { | ||
| throw new ContactFeedbackException("Failed to get feedback by email", e); | ||
| } | ||
| } | ||
|
|
||
| // SQL Injection vulnerability - string concatenation | ||
| public List<Map<String, String>> getFeedbackByCategory(String category) throws ContactFeedbackException { | ||
| String query = "SELECT id, name, email, feedback, category FROM feedback WHERE category = '" + category + "'"; | ||
|
|
||
| try (Statement statement = connection.createStatement(); | ||
| ResultSet resultSet = statement.executeQuery(query)) { | ||
Check failureCode scanning / SonarQube Database queries should not be vulnerable to injection attacks Critical
Change this code to not construct SQL queries directly from user-controlled data. See more on SonarQube
|
||
|
|
||
| List<Map<String, String>> feedbackList = new ArrayList<>(); | ||
| while (resultSet.next()) { | ||
| Map<String, String> feedback = new HashMap<>(); | ||
| feedback.put(FIELD_ID, resultSet.getString(FIELD_ID)); | ||
| feedback.put(FIELD_NAME, resultSet.getString(FIELD_NAME)); | ||
| feedback.put(FIELD_EMAIL, resultSet.getString(FIELD_EMAIL)); | ||
| feedback.put(FIELD_FEEDBACK, resultSet.getString(FIELD_FEEDBACK)); | ||
| feedback.put(FIELD_CATEGORY, resultSet.getString(FIELD_CATEGORY)); | ||
| feedbackList.add(feedback); | ||
| } | ||
|
|
||
| return feedbackList; | ||
| } catch (SQLException e) { | ||
| throw new ContactFeedbackException("Failed to get feedback by category", e); | ||
| } | ||
| } | ||
|
|
||
| public List<Map<String, String>> getAllFeedback() throws ContactFeedbackException { | ||
| String query = "SELECT id, name, email, feedback, category FROM feedback"; | ||
|
|
||
| try (Statement statement = connection.createStatement(); | ||
| ResultSet resultSet = statement.executeQuery(query)) { | ||
|
|
||
| List<Map<String, String>> feedbackList = new ArrayList<>(); | ||
| while (resultSet.next()) { | ||
| Map<String, String> feedback = new HashMap<>(); | ||
| feedback.put(FIELD_ID, resultSet.getString(FIELD_ID)); | ||
| feedback.put(FIELD_NAME, resultSet.getString(FIELD_NAME)); | ||
| feedback.put(FIELD_EMAIL, resultSet.getString(FIELD_EMAIL)); | ||
| feedback.put(FIELD_FEEDBACK, resultSet.getString(FIELD_FEEDBACK)); | ||
| feedback.put(FIELD_CATEGORY, resultSet.getString(FIELD_CATEGORY)); | ||
| feedbackList.add(feedback); | ||
| } | ||
|
|
||
| return feedbackList; | ||
| } catch (SQLException e) { | ||
| throw new ContactFeedbackException("Failed to get all feedback", e); | ||
| } | ||
| } | ||
|
|
||
| // Weak random number generation for ID - security vulnerability | ||
| private String generateFeedbackId() { | ||
| return "FB-" + random.nextInt(1000000); | ||
| } | ||
|
|
||
| // Path traversal vulnerability | ||
| public String readFeedbackFile(String fileName) throws ContactFeedbackException { | ||
| // No validation on fileName - path traversal vulnerability | ||
| String fullPath = feedbackBasePath + fileName; | ||
|
|
||
| java.io.File file = new java.io.File(fullPath); | ||
| try (java.util.Scanner scanner = new java.util.Scanner(file)) { | ||
| StringBuilder content = new StringBuilder(); | ||
| while (scanner.hasNextLine()) { | ||
| content.append(scanner.nextLine()).append("\n"); | ||
| } | ||
| return content.toString(); | ||
| } catch (java.io.FileNotFoundException e) { | ||
| throw new ContactFeedbackException("Failed to read feedback file", e); | ||
| } | ||
| } | ||
| } | ||
Check notice
Code scanning / SonarQube
Exceptions should not be thrown from servlet methods Low