Skip to content
Open
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
277 changes: 254 additions & 23 deletions src/main/java/com/example/Main.java
Original file line number Diff line number Diff line change
@@ -1,45 +1,275 @@
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.InputMismatchException;
import java.util.Scanner;

public class Main {

static void main(String[] args) {
private final Scanner scanner = new Scanner(System.in);
private Connection connection;

public static void main(String[] args) {
if (isDevMode(args)) {
DevDatabaseInitializer.start();
}
new Main().run();
}

public void run() {
// Resolve DB settings with precedence: System properties -> Environment variables
String jdbcUrl = resolveConfig("APP_JDBC_URL", "APP_JDBC_URL");
String dbUser = resolveConfig("APP_DB_USER", "APP_DB_USER");
String dbPass = resolveConfig("APP_DB_PASS", "APP_DB_PASS");

if (jdbcUrl == null || dbUser == null || dbPass == null) {
throw new IllegalStateException(
"Missing DB configuration. Provide APP_JDBC_URL, APP_DB_USER, APP_DB_PASS " +
"as system properties (-Dkey=value) or environment variables.");
String JDBC_URL = resolveConfig("APP_JDBC_URL", "APP_JDBC_URL");
String DB_USER = resolveConfig("APP_DB_USER", "APP_DB_USER");
String DB_PASS = resolveConfig("APP_DB_PASS", "APP_DB_PASS");

if (JDBC_URL == null || DB_USER == null || DB_PASS == null) {
System.err.println("Database configuration environment variables not set.");
return;
}

try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) {
try {
connection = DriverManager.getConnection(JDBC_URL, DB_USER, DB_PASS);
System.out.println("Database connection established.");

if (login()) {
mainMenu();
} else {
handleInvalidLogin();
}
} catch (SQLException e) {
throw new RuntimeException(e);
System.err.println("Database error: " + e.getMessage());
} finally {
closeConnection();
}
//Todo: Starting point for your code
}

/**
* Determines if the application is running in development mode based on system properties,
* environment variables, or command-line arguments.
*
* @param args an array of command-line arguments
* @return {@code true} if the application is in development mode; {@code false} otherwise
*/
// --- Inloggning ---

private boolean login() throws SQLException {
System.out.print("Username: ");
String username = scanner.nextLine();
System.out.print("Password: ");
String password = scanner.nextLine();

String generatedUsername = username.substring(0, Math.min(username.length(), 3))
+ (username.length() > 3 ? username.substring(3, Math.min(username.length(), 6)) : "");
Comment on lines +55 to +56
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

Dead code: generatedUsername is never used.

The generatedUsername variable is computed but never referenced. This appears to be leftover code or an incomplete implementation. Remove it to avoid confusion.

-        String generatedUsername = username.substring(0, Math.min(username.length(), 3))
-                + (username.length() > 3 ? username.substring(3, Math.min(username.length(), 6)) : "");
-
📝 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
String generatedUsername = username.substring(0, Math.min(username.length(), 3))
+ (username.length() > 3 ? username.substring(3, Math.min(username.length(), 6)) : "");
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 55-56, the local variable
generatedUsername is computed but never used; remove the entire statement
computing generatedUsername (and any related unused imports or variables if
present) so there is no unused/dead code left, then compile to ensure no
references remain and run tests to confirm behavior unchanged.


String sql = "SELECT user_id FROM account WHERE name = ? AND password = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, username);
stmt.setString(2, password);
try (ResultSet rs = stmt.executeQuery()) {
return rs.next(); // True om användaren hittades
}
}
Comment on lines +58 to +65
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

Security: Passwords stored and compared in plaintext.

The login query compares passwords directly without hashing. Passwords should be hashed using a secure algorithm (e.g., bcrypt, Argon2) before storage and comparison. Plaintext password storage is a significant security vulnerability.

🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 58 to 65, the code compares
plaintext passwords directly in the SQL query; change the flow to query the
stored password hash by username only, then verify the supplied password against
that hash using a secure password-hashing library (e.g., bcrypt or Argon2)
rather than including password in the WHERE clause; ensure you use the library's
verify function (which handles salt and constant-time comparison), handle the
case of missing user gracefully, and update storage and registration code to
store hashed passwords (or plan a migration) so no plaintext passwords are
stored or compared.

}

private void handleInvalidLogin() {
System.out.println("Invalid username or password. Press 0 to exit.");
while (true) {
System.out.print("Choice: ");
String choice = scanner.nextLine().trim();
if ("0".equals(choice)) {
break;
}
}
}

// --- Menyhantering ---

private void mainMenu() {
int choice = -1;
while (choice != 0) {
printMenu();
try {
System.out.print("Choice: ");
choice = scanner.nextInt();
scanner.nextLine();

switch (choice) {
case 1:
listMoonMissions();
break;
case 2:
getMoonMissionById();
break;
case 3:
countMissionsByYear();
break;
case 4:
createAccount();
break;
case 5:
updateAccountPassword();
break;
case 6:
deleteAccount();
break;
case 0:
System.out.println("Exiting application.");
break;
default:
System.out.println("Invalid choice. Try again.");
}
} catch (InputMismatchException e) {
System.out.println("Invalid input. Please enter a number.");
scanner.nextLine();
} catch (SQLException e) {
System.err.println("Database operation failed: " + e.getMessage());
}
}
}

private void printMenu() {
System.out.println("\n--- Menu ---");
System.out.println("1) List moon missions (spacecraft names)");
System.out.println("2) Get a moon mission by mission_id (details)");
System.out.println("3) Count missions for a given year");
System.out.println("4) Create an account (first name, last name, ssn, password)");
System.out.println("5) Update an account password (user_id, new password)");
System.out.println("6) Delete an account (user_id)");
System.out.println("0) Exit");
System.out.println("------------");
}

// 1) List moon missions
private void listMoonMissions() throws SQLException {
String sql = "SELECT spacecraft FROM moon_mission";
try (Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
System.out.println("--- Moon Missions ---");
while (rs.next()) {
System.out.println(" - " + rs.getString("spacecraft"));
}
}
}

// 2) Get a moon mission by mission_id
private void getMoonMissionById() throws SQLException {
System.out.print("Enter mission_id: ");
long missionId = scanner.nextLong();
scanner.nextLine();

String sql = "SELECT * FROM moon_mission WHERE mission_id = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setLong(1, missionId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
System.out.println("--- Mission Details (ID: " + missionId + ") ---");
System.out.println("Spacecraft: " + rs.getString("spacecraft"));
System.out.println("Launch Date: " + rs.getDate("launch_date"));
System.out.println("Outcome: " + rs.getString("outcome"));
} else {
System.out.println("Mission with ID " + missionId + " not found.");
}
}
}
}

// 3) Count missions for a given year
private void countMissionsByYear() throws SQLException {
System.out.print("Enter year: ");
int year = scanner.nextInt();
scanner.nextLine();

String sql = "SELECT COUNT(*) FROM moon_mission WHERE YEAR(launch_date) = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setInt(1, year);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
int count = rs.getInt(1);
System.out.println("Found " + count + " missions launched in " + year + ".");
}
}
}
}

// 4) Create an account
private void createAccount() throws SQLException {
System.out.print("Enter first name: ");
String firstName = scanner.nextLine();
System.out.print("Enter last name: ");
String lastName = scanner.nextLine();
System.out.print("Enter ssn: ");
String ssn = scanner.nextLine();
System.out.print("Enter password: ");
String password = scanner.nextLine();

String username = firstName.substring(0, Math.min(firstName.length(), 3))
+ lastName.substring(0, Math.min(lastName.length(), 3));

String sql = "INSERT INTO account (first_name, last_name, ssn, password, name) VALUES (?, ?, ?, ?, ?)";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, firstName);
stmt.setString(2, lastName);
stmt.setString(3, ssn);
stmt.setString(4, password);
Comment on lines +194 to +207
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

Compliance: SSN stored in plaintext.

The Social Security Number (SSN) is highly sensitive PII and is stored without encryption. Consider encrypting SSN at rest or storing only a hashed/masked version to comply with data protection regulations (GDPR, CCPA, etc.).

Additionally, the password is stored in plaintext here as well (same issue as login).

stmt.setString(5, username);
Comment on lines +199 to +208
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

Potential issue: No validation on username generation.

If firstName or lastName is empty or very short, the generated username could be empty or just a few characters. Additionally, there's no uniqueness check—if two users have similar names (e.g., "John Smith" and "Johnathan Smithson"), they'd get the same username, potentially causing a duplicate key constraint violation.

Consider adding input validation and handling potential SQLException for duplicate usernames:

if (firstName.isEmpty() || lastName.isEmpty()) {
    System.out.println("First name and last name cannot be empty.");
    return;
}
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 199 to 208, the username
construction does not validate firstName/lastName and can produce empty or
non-unique usernames; update the code to first validate that firstName and
lastName are non-empty and meet a minimum length, then generate a base username
safely (fallback to a default or include parts of other fields if too short),
check the database for existing usernames and, if a conflict exists, append a
numeric suffix (incrementing until unique) or use a transactional DB-side
uniqueness mechanism, and wrap the insert in error handling that catches
SQLExceptions for duplicate-key errors to either retry with a new username or
return a clear error to the caller.


int rowsAffected = stmt.executeUpdate();
if (rowsAffected > 0) {
System.out.println("Account created successfully. Username is " + username + ".");
} else {
System.out.println("Failed to create account.");
}
}
}

// 5) Update an account password
private void updateAccountPassword() throws SQLException {
System.out.print("Enter user_id to update: ");
long userId = scanner.nextLong();
scanner.nextLine();
System.out.print("Enter new password: ");
String newPassword = scanner.nextLine();

String sql = "UPDATE account SET password = ? WHERE user_id = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, newPassword);
stmt.setLong(2, userId);

int rowsAffected = stmt.executeUpdate();
if (rowsAffected > 0) {
System.out.println("Account password updated successfully for user_id " + userId + ".");
} else {
System.out.println("No account found with user_id " + userId + " to update.");
}
}
}

// 6) Delete an account
private void deleteAccount() throws SQLException {
System.out.print("Enter user_id to delete: ");
long userId = scanner.nextLong();
scanner.nextLine();

String sql = "DELETE FROM account WHERE user_id = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setLong(1, userId);

int rowsAffected = stmt.executeUpdate();
if (rowsAffected > 0) {
System.out.println("Account deleted successfully for user_id " + userId + ".");
} else {
System.out.println("No account found with user_id " + userId + " to delete.");
}
}
}
Comment on lines +220 to +258
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

Security: Missing authorization checks.

Any authenticated user can update or delete any account by specifying an arbitrary user_id. This is an authorization flaw. Consider restricting these operations to the currently logged-in user's account, or implementing role-based access control for administrative operations.



private void closeConnection() {
if (connection != null) {
try {
connection.close();
System.out.println("Database connection closed.");
} catch (SQLException e) {
System.err.println("Error closing connection: " + e.getMessage());
}
}
}


private static boolean isDevMode(String[] args) {
if (Boolean.getBoolean("devMode")) //Add VM option -DdevMode=true
return true;
Expand All @@ -59,4 +289,5 @@ private static String resolveConfig(String propertyKey, String envKey) {
}
return (v == null || v.trim().isEmpty()) ? null : v.trim();
}
}

}
Loading