Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
257 changes: 253 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

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);

try (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

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

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 = cap3(firstName) + cap3(lastName);

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

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,14 @@ private static String resolveConfig(String propertyKey, String envKey) {
}
return (v == null || v.trim().isEmpty()) ? null : v.trim();
}
}

private static String cap3(String s) {
if (s == null) return "";
String t = s.trim();
if (t.isEmpty()) return "";

String part = t.substring(0, Math.min(3, t.length()));
return part.substring(0, 1).toUpperCase() +
part.substring(1).toLowerCase();
}
}
Loading