Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/339Lr3BJ)
### How the tests work (and Docker requirement)

This project ships with an end‑to‑end CLI integration test suite that uses Testcontainers to spin up a temporary MySQL database.
Expand Down
247 changes: 243 additions & 4 deletions src/main/java/com/example/Main.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.example;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.*;
import java.util.Arrays;
import java.util.Scanner;

public class Main {

Expand Down Expand Up @@ -31,6 +30,246 @@ public void run() {
throw new RuntimeException(e);
}
//Todo: Starting point for your code
Scanner scanner = new Scanner(System.in);

System.out.println("Username:");
String username = scanner.nextLine();

System.out.println("Password:");
String password = scanner.nextLine();

boolean isValid = validateLogin(username, password, jdbcUrl, dbUser, dbPass);

if (!isValid) {
System.out.println("Invalid username or password");
// Testarna förväntar sig att man kan välja 0 för att avsluta
System.out.print("Press 0 to exit or any key to continue...");
String choice = scanner.nextLine();
if ("0".equals(choice)) {
return;
}
} else {
System.out.println("Login successful!");
}

// Huvudmeny
boolean running = true;
while (running) {
System.out.println("1) List moon missions");
System.out.println("2) Get a moon mission by mission_id");
System.out.println("3) Count missions for a given year");
System.out.println("4) Create an account");
System.out.println("5) Update an account password");
System.out.println("6) Delete an account");
System.out.println("0) Exit");

System.out.print("Enter choice: ");
String choice = scanner.nextLine();

switch (choice) {
case "1":
listMoonMissions(jdbcUrl, dbUser, dbPass);
break;
case "2":
System.out.print("Enter mission ID: ");
String missionId = scanner.nextLine();
getMissionById(jdbcUrl, dbUser, dbPass, missionId);
break;
case "3":
System.out.print("Enter year: ");
String year = scanner.nextLine();
countMissionsByYear(jdbcUrl, dbUser, dbPass, year);
break;
case "4":
createAccount(jdbcUrl, dbUser, dbPass, scanner);
break;
case "5":
updateAccountPassword(jdbcUrl, dbUser, dbPass, scanner);
break;
case "6":
deleteAccount(jdbcUrl, dbUser, dbPass, scanner);
break;
case "0":
System.out.println("Exiting...");
running = false;
break;
default:
System.out.println("Invalid choice");
}

if (running && !choice.equals("0")) {
System.out.println("\nPress Enter to continue...");
scanner.nextLine();
}
}
}
Comment on lines +33 to +105
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t allow main menu access after failed login — currently, a user can fail auth and still proceed into CRUD/query operations by choosing “any key to continue”.

-        System.out.println("Username:");
-        String username = scanner.nextLine();
-
-        System.out.println("Password:");
-        String password = scanner.nextLine();
-
-        boolean isValid = validateLogin(username, password, jdbcUrl, dbUser, dbPass);
-
-        if (!isValid) {
-            System.out.println("Invalid username or password");
-            // Testarna förväntar sig att man kan välja 0 för att avsluta
-            System.out.print("Press 0 to exit or any key to continue...");
-            String choice = scanner.nextLine();
-            if ("0".equals(choice)) {
-                return;
-            }
-        } else {
-            System.out.println("Login successful!");
-        }
+        boolean isValid = false;
+        while (!isValid) {
+            System.out.println("Username:");
+            String username = scanner.nextLine();
+
+            System.out.println("Password:");
+            String password = scanner.nextLine();
+
+            isValid = validateLogin(username, password, jdbcUrl, dbUser, dbPass);
+            if (!isValid) {
+                System.out.println("Invalid username or password");
+                // Testarna förväntar sig att man kan välja 0 för att avsluta
+                System.out.print("Press 0 to exit or any key to try again...");
+                String choice = scanner.nextLine();
+                if ("0".equals(choice)) return;
+            }
+        }
+        System.out.println("Login successful!");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Scanner scanner = new Scanner(System.in);
System.out.println("Username:");
String username = scanner.nextLine();
System.out.println("Password:");
String password = scanner.nextLine();
boolean isValid = validateLogin(username, password, jdbcUrl, dbUser, dbPass);
if (!isValid) {
System.out.println("Invalid username or password");
// Testarna förväntar sig att man kan välja 0 för att avsluta
System.out.print("Press 0 to exit or any key to continue...");
String choice = scanner.nextLine();
if ("0".equals(choice)) {
return;
}
} else {
System.out.println("Login successful!");
}
// Huvudmeny
boolean running = true;
while (running) {
System.out.println("1) List moon missions");
System.out.println("2) Get a moon mission by mission_id");
System.out.println("3) Count missions for a given year");
System.out.println("4) Create an account");
System.out.println("5) Update an account password");
System.out.println("6) Delete an account");
System.out.println("0) Exit");
System.out.print("Enter choice: ");
String choice = scanner.nextLine();
switch (choice) {
case "1":
listMoonMissions(jdbcUrl, dbUser, dbPass);
break;
case "2":
System.out.print("Enter mission ID: ");
String missionId = scanner.nextLine();
getMissionById(jdbcUrl, dbUser, dbPass, missionId);
break;
case "3":
System.out.print("Enter year: ");
String year = scanner.nextLine();
countMissionsByYear(jdbcUrl, dbUser, dbPass, year);
break;
case "4":
createAccount(jdbcUrl, dbUser, dbPass, scanner);
break;
case "5":
updateAccountPassword(jdbcUrl, dbUser, dbPass, scanner);
break;
case "6":
deleteAccount(jdbcUrl, dbUser, dbPass, scanner);
break;
case "0":
System.out.println("Exiting...");
running = false;
break;
default:
System.out.println("Invalid choice");
}
if (running && !choice.equals("0")) {
System.out.println("\nPress Enter to continue...");
scanner.nextLine();
}
}
}
boolean isValid = false;
while (!isValid) {
System.out.println("Username:");
String username = scanner.nextLine();
System.out.println("Password:");
String password = scanner.nextLine();
isValid = validateLogin(username, password, jdbcUrl, dbUser, dbPass);
if (!isValid) {
System.out.println("Invalid username or password");
// Testarna förväntar sig att man kan välja 0 för att avsluta
System.out.print("Press 0 to exit or any key to try again...");
String choice = scanner.nextLine();
if ("0".equals(choice)) return;
}
}
System.out.println("Login successful!");
// Huvudmeny
boolean running = true;
while (running) {
System.out.println("1) List moon missions");
System.out.println("2) Get a moon mission by mission_id");
System.out.println("3) Count missions for a given year");
System.out.println("4) Create an account");
System.out.println("5) Update an account password");
System.out.println("6) Delete an account");
System.out.println("0) Exit");
System.out.print("Enter choice: ");
String choice = scanner.nextLine();
switch (choice) {
case "1":
listMoonMissions(jdbcUrl, dbUser, dbPass);
break;
case "2":
System.out.print("Enter mission ID: ");
String missionId = scanner.nextLine();
getMissionById(jdbcUrl, dbUser, dbPass, missionId);
break;
case "3":
System.out.print("Enter year: ");
String year = scanner.nextLine();
countMissionsByYear(jdbcUrl, dbUser, dbPass, year);
break;
case "4":
createAccount(jdbcUrl, dbUser, dbPass, scanner);
break;
case "5":
updateAccountPassword(jdbcUrl, dbUser, dbPass, scanner);
break;
case "6":
deleteAccount(jdbcUrl, dbUser, dbPass, scanner);
break;
case "0":
System.out.println("Exiting...");
running = false;
break;
default:
System.out.println("Invalid choice");
}
if (running && !choice.equals("0")) {
System.out.println("\nPress Enter to continue...");
scanner.nextLine();
}
}
}
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 33 to 105, the code allows
users who fail authentication to continue into the main menu by choosing "any
key to continue"; fix this by preventing access to the menu when isValid is
false — either return/exit immediately on failed login or loop back to re-prompt
for credentials, or move the entire main menu block inside the existing else
branch so it only runs when isValid is true; update the control flow accordingly
and remove the "continue to menu" prompt after a failed login.


private boolean validateLogin(String username, String password,
String jdbcUrl, String dbUser, String dbPass) {
String sql = "SELECT 1 FROM account WHERE name = ? AND password = ?";
try (Connection conn = DriverManager.getConnection(jdbcUrl, dbUser, dbPass);
PreparedStatement stmt = conn.prepareStatement(sql)) {

stmt.setString(1, username);
stmt.setString(2, password);

ResultSet rs = stmt.executeQuery();
return rs.next();

} catch (SQLException e) {
System.err.println("Login error: " + e.getMessage());
return false;
}
}
Comment on lines +107 to +124
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Close ResultSet explicitly (minor), and avoid printing raw SQL errors that may include sensitive info.

-            ResultSet rs = stmt.executeQuery();
-            return rs.next();
+            try (ResultSet rs = stmt.executeQuery()) {
+                return rs.next();
+            }
 
         } catch (SQLException e) {
-            System.err.println("Login error: " + e.getMessage());
+            System.err.println("Login error");
             return false;
         }
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 107 to 123, the code doesn't
close the ResultSet explicitly and prints raw SQL error via System.err which may
leak sensitive info; change the try-with-resources to include the ResultSet (or
wrap the executeQuery call in its own try-with-resources) so rs is closed,
replace System.err prints with a proper logger call that emits a generic error
message (e.g., "Login failed") and send the SQLException details to a secure
debug/trace log rather than standard error or user-facing output, and ensure no
sensitive SQL or credential data is included in the logged messages.


private void listMoonMissions(String jdbcUrl, String dbUser, String dbPass) {
String sql = "SELECT spacecraft FROM moon_mission ORDER BY mission_id";
try (Connection conn = DriverManager.getConnection(jdbcUrl, dbUser, dbPass);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {

System.out.println("\n=== Moon Missions ===");
while (rs.next()) {
System.out.println(rs.getString("spacecraft"));
}

} catch (SQLException e) {
System.err.println("Error listing missions: " + e.getMessage());
}
}

private void getMissionById(String jdbcUrl, String dbUser, String dbPass, String missionId) {
String sql = "SELECT * FROM moon_mission WHERE mission_id = ?";
try (Connection conn = DriverManager.getConnection(jdbcUrl, dbUser, dbPass);
PreparedStatement stmt = conn.prepareStatement(sql)) {

stmt.setInt(1, Integer.parseInt(missionId));
ResultSet rs = stmt.executeQuery();

if (rs.next()) {
System.out.println("\n=== Mission Details ===");
System.out.println("Mission ID: " + rs.getInt("mission_id"));
System.out.println("Spacecraft: " + rs.getString("spacecraft"));
System.out.println("Launch Date: " + rs.getDate("launch_date"));
System.out.println("Carrier Rocket: " + rs.getString("carrier_rocket"));
System.out.println("Operator: " + rs.getString("operator"));
System.out.println("Mission Type: " + rs.getString("mission_type"));
System.out.println("Outcome: " + rs.getString("outcome"));
} else {
System.out.println("Mission not found");
}

} catch (SQLException | NumberFormatException e) {
System.err.println("Error: " + e.getMessage());
}
}
Comment on lines +142 to +166
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Close ResultSet in getMissionById (currently not in try-with-resources) — avoid leaking cursors/resources.

-            stmt.setInt(1, Integer.parseInt(missionId));
-            ResultSet rs = stmt.executeQuery();
-
-            if (rs.next()) {
+            stmt.setInt(1, Integer.parseInt(missionId));
+            try (ResultSet rs = stmt.executeQuery()) {
+                if (rs.next()) {
                     System.out.println("\n=== Mission Details ===");
                     System.out.println("Mission ID: " + rs.getInt("mission_id"));
                     System.out.println("Spacecraft: " + rs.getString("spacecraft"));
                     System.out.println("Launch Date: " + rs.getDate("launch_date"));
                     System.out.println("Carrier Rocket: " + rs.getString("carrier_rocket"));
                     System.out.println("Operator: " + rs.getString("operator"));
                     System.out.println("Mission Type: " + rs.getString("mission_type"));
                     System.out.println("Outcome: " + rs.getString("outcome"));
-            } else {
-                System.out.println("Mission not found");
+                } else {
+                    System.out.println("Mission not found");
+                }
             }
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 142 to 166, the ResultSet
returned by stmt.executeQuery() is not closed, risking cursor/resource leaks;
update the method to ensure the ResultSet is closed by using a
try-with-resources that includes the ResultSet (or nest a try-with-resources) so
the PreparedStatement and ResultSet are both created in try-with-resources
blocks (e.g., prepare the statement, then assign executeQuery() to a ResultSet
variable declared in a try-with-resources), and remove reliance on implicit
closing so all JDBC resources are properly closed even on exceptions.


private void countMissionsByYear(String jdbcUrl, String dbUser, String dbPass, String year) {
String sql = "SELECT COUNT(*) as count FROM moon_mission WHERE YEAR(launch_date) = ?";
try (Connection conn = DriverManager.getConnection(jdbcUrl, dbUser, dbPass);
PreparedStatement stmt = conn.prepareStatement(sql)) {

stmt.setInt(1, Integer.parseInt(year));
ResultSet rs = stmt.executeQuery();

if (rs.next()) {
int count = rs.getInt("count");
System.out.println("Number of missions in " + year + ": " + count);
}

} catch (SQLException | NumberFormatException e) {
System.err.println("Error: " + e.getMessage());
}
}

private void createAccount(String jdbcUrl, String dbUser, String dbPass, Scanner scanner) {
System.out.print("First name: ");
String firstName = scanner.nextLine();

System.out.print("Last name: ");
String lastName = scanner.nextLine();

System.out.print("SSN: ");
String ssn = scanner.nextLine();

System.out.print("Password: ");
String password = scanner.nextLine();

// Skapa username från förnamn + första 3 bokstäver av efternamn
String username = (firstName.substring(0, Math.min(3, firstName.length())) +
lastName.substring(0, Math.min(3, lastName.length()))).toLowerCase();

String sql = "INSERT INTO account (first_name, last_name, ssn, name, password) " +
"VALUES (?, ?, ?, ?, ?)";

try (Connection conn = DriverManager.getConnection(jdbcUrl, dbUser, dbPass);
PreparedStatement stmt = conn.prepareStatement(sql)) {

stmt.setString(1, firstName);
stmt.setString(2, lastName);
stmt.setString(3, ssn);
stmt.setString(4, username);
stmt.setString(5, password);

int rows = stmt.executeUpdate();
if (rows > 0) {
System.out.println("Account created successfully!");
System.out.println("Username: " + username);
}

} catch (SQLException e) {
System.err.println("Error creating account: " + e.getMessage());
}
}
Comment on lines +186 to +223
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, explore the project structure to understand build system and dependencies
find . -maxdepth 2 -type f \( -name "pom.xml" -o -name "build.gradle" -o -name "build.gradle.kts" \)

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 108


🏁 Script executed:

# Locate and read the dependency file to see what libraries are available
git ls-files | grep -E "(pom\.xml|build\.gradle|build\.gradle\.kts)" | head -5

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 106


🏁 Script executed:

# Read the pom.xml to check project dependencies
cat pom.xml

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 2791


🏁 Script executed:

# Search for password-related code and authentication logic
rg -i "password" src/main/java --type java -B 2 -A 2

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 6569


🏁 Script executed:

# Search for any hashing or encoding related code
rg -i "(hash|bcrypt|encrypt|encode)" src/main/java --type java

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 81


🏁 Script executed:

# Look for authentication or login methods
rg -i "(login|authenticate|verify|validate.*password)" src/main/java --type java -B 2 -A 5

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 2420


🏁 Script executed:

# Verify the exact lines of validateLogin method to understand password comparison
sed -n '155,180p' src/main/java/com/example/Main.java

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 1277


🏁 Script executed:

# Find the validateLogin method specifically
rg -n "private boolean validateLogin" src/main/java/com/example/Main.java -A 15

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 858


Plaintext password storage and validation is a critical security vulnerability — passwords are stored and compared in plaintext across createAccount (line 210), validateLogin (line 109), and updateAccountPassword. Add a password hashing library (e.g., jBCrypt) to dependencies and hash passwords before storing; validate logins by comparing hashed password using a constant-time comparison.

Similarly, SSN should not be stored in plaintext.


private void updateAccountPassword(String jdbcUrl, String dbUser, String dbPass, Scanner scanner) {
System.out.print("Enter user ID: ");
String userId = scanner.nextLine();

System.out.print("Enter new password: ");
String newPassword = scanner.nextLine();

String sql = "UPDATE account SET password = ? WHERE user_id = ?";

try (Connection conn = DriverManager.getConnection(jdbcUrl, dbUser, dbPass);
PreparedStatement stmt = conn.prepareStatement(sql)) {

stmt.setString(1, newPassword);
stmt.setInt(2, Integer.parseInt(userId));

int rows = stmt.executeUpdate();
if (rows > 0) {
System.out.println("Password updated successfully");
} else {
System.out.println("No account found with ID: " + userId);
}

} catch (SQLException | NumberFormatException e) {
System.err.println("Error: " + e.getMessage());
}
}

private void deleteAccount(String jdbcUrl, String dbUser, String dbPass, Scanner scanner) {
System.out.print("Enter user ID to delete: ");
String userId = scanner.nextLine();

String sql = "DELETE FROM account WHERE user_id = ?";

try (Connection conn = DriverManager.getConnection(jdbcUrl, dbUser, dbPass);
PreparedStatement stmt = conn.prepareStatement(sql)) {

stmt.setInt(1, Integer.parseInt(userId));

int rows = stmt.executeUpdate();
if (rows > 0) {
System.out.println("Account deleted successfully");
} else {
System.out.println("No account found with ID: " + userId);
}

} catch (SQLException | NumberFormatException e) {
System.err.println("Error: " + e.getMessage());
}
}

/**
Expand Down Expand Up @@ -59,4 +298,4 @@ private static String resolveConfig(String propertyKey, String envKey) {
}
return (v == null || v.trim().isEmpty()) ? null : v.trim();
}
}
}
Loading