diff --git a/src/library/assistant/database/DatabaseHandler.java b/src/library/assistant/database/DatabaseHandler.java index e9c4f53..ed2407e 100644 --- a/src/library/assistant/database/DatabaseHandler.java +++ b/src/library/assistant/database/DatabaseHandler.java @@ -59,7 +59,7 @@ private static void inflateDB() { System.out.println("Already loaded tables " + loadedTables); DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); - Document doc = dBuilder.parse(DatabaseHandler.class.getClass().getResourceAsStream("/resources/database/tables.xml")); + Document doc = dBuilder.parse(DatabaseHandler.class.getResourceAsStream("/resources/database/tables.xml")); NodeList nList = doc.getElementsByTagName("table-entry"); for (int i = 0; i < nList.getLength(); i++) { Node nNode = nList.item(i); @@ -89,7 +89,7 @@ private static void createConnection() { conn = DriverManager.getConnection(DB_URL); } catch (Exception e) { - JOptionPane.showMessageDialog(null, "Cant load database", "Database Error", JOptionPane.ERROR_MESSAGE); + LOGGER.log(Level.ERROR, "Cant load database", e); System.exit(0); } } @@ -123,6 +123,22 @@ public ResultSet execQuery(String query) { return result; } + public ResultSet execQuery(String query, Object... params) { + ResultSet result; + try { + PreparedStatement pstmt = conn.prepareStatement(query); + for (int i = 0; i < params.length; i++) { + pstmt.setObject(i + 1, params[i]); + } + result = pstmt.executeQuery(); + } + catch (SQLException ex) { + System.out.println("Exception at execQuery:dataHandler" + ex.getLocalizedMessage()); + return null; + } + return result; + } + public boolean execAction(String qu) { try { stmt = conn.createStatement(); @@ -130,7 +146,7 @@ public boolean execAction(String qu) { return true; } catch (SQLException ex) { - JOptionPane.showMessageDialog(null, "Error:" + ex.getMessage(), "Error Occured", JOptionPane.ERROR_MESSAGE); + LOGGER.log(Level.ERROR, "Error: " + ex.getMessage(), ex); System.out.println("Exception at execQuery:dataHandler" + ex.getLocalizedMessage()); return false; } @@ -138,6 +154,21 @@ public boolean execAction(String qu) { } } + public boolean execAction(String qu, Object... params) { + try (PreparedStatement pstmt = conn.prepareStatement(qu)) { + for (int i = 0; i < params.length; i++) { + pstmt.setObject(i + 1, params[i]); + } + pstmt.execute(); + return true; + } + catch (SQLException ex) { + LOGGER.log(Level.ERROR, "Error: " + ex.getMessage(), ex); + System.out.println("Exception at execQuery:dataHandler" + ex.getLocalizedMessage()); + return false; + } + } + public boolean deleteBook(Book book) { try { String deleteStatement = "DELETE FROM BOOK WHERE ID = ?"; @@ -240,10 +271,6 @@ public boolean updateMember(MemberListController.Member member) { return false; } - public static void main(String[] args) throws Exception { - DatabaseHandler.getInstance(); - } - public ObservableList getBookGraphStatistics() { ObservableList data = FXCollections.observableArrayList(); try { diff --git a/src/library/assistant/ui/main/MainController.java b/src/library/assistant/ui/main/MainController.java index 50b0fff..783d984 100644 --- a/src/library/assistant/ui/main/MainController.java +++ b/src/library/assistant/ui/main/MainController.java @@ -187,8 +187,8 @@ private void loadMemberInfo(ActionEvent event) { enableDisableGraph(false); String id = memberIDInput.getText(); - String qu = "SELECT * FROM MEMBER WHERE id = '" + id + "'"; - ResultSet rs = databaseHandler.execQuery(qu); + String qu = "SELECT * FROM MEMBER WHERE id = ?"; + ResultSet rs = databaseHandler.execQuery(qu, id); Boolean flag = false; try { while (rs.next()) { @@ -236,13 +236,10 @@ private void loadIssueOperation(ActionEvent event) { JFXButton yesButton = new JFXButton("YES"); yesButton.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event1) -> { - String str = "INSERT INTO ISSUE(memberID,bookID) VALUES (" - + "'" + memberID + "'," - + "'" + bookID + "')"; - String str2 = "UPDATE BOOK SET isAvail = false WHERE id = '" + bookID + "'"; - System.out.println(str + " and " + str2); + String str = "INSERT INTO ISSUE(memberID,bookID) VALUES (?, ?)"; + String str2 = "UPDATE BOOK SET isAvail = false WHERE id = ?"; - if (databaseHandler.execAction(str) && databaseHandler.execAction(str2)) { + if (databaseHandler.execAction(str, memberID, bookID) && databaseHandler.execAction(str2, bookID)) { JFXButton button = new JFXButton("Done!"); button.setOnAction((actionEvent) -> { bookIDInput.requestFocus(); @@ -281,8 +278,8 @@ private void loadBookInfo2(ActionEvent event) { + "ON ISSUE.memberID=MEMBER.ID\n" + "LEFT JOIN BOOK\n" + "ON ISSUE.bookID=BOOK.ID\n" - + "WHERE ISSUE.bookID='" + id + "'"; - ResultSet rs = databaseHandler.execQuery(myQuery); + + "WHERE ISSUE.bookID=?"; + ResultSet rs = databaseHandler.execQuery(myQuery, id); if (rs.next()) { memberNameHolder.setText(rs.getString("name")); memberContactHolder.setText(rs.getString("mobile")); @@ -330,10 +327,10 @@ private void loadSubmissionOp(ActionEvent event) { JFXButton yesButton = new JFXButton("YES, Please"); yesButton.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent ev) -> { String id = bookID.getText(); - String ac1 = "DELETE FROM ISSUE WHERE BOOKID = '" + id + "'"; - String ac2 = "UPDATE BOOK SET ISAVAIL = TRUE WHERE ID = '" + id + "'"; + String ac1 = "DELETE FROM ISSUE WHERE BOOKID = ?"; + String ac2 = "UPDATE BOOK SET ISAVAIL = TRUE WHERE ID = ?"; - if (databaseHandler.execAction(ac1) && databaseHandler.execAction(ac2)) { + if (databaseHandler.execAction(ac1, id) && databaseHandler.execAction(ac2, id)) { JFXButton btn = new JFXButton("Done!"); btn.setOnAction((actionEvent) -> { bookID.requestFocus(); @@ -364,9 +361,8 @@ private void loadRenewOp(ActionEvent event) { } JFXButton yesButton = new JFXButton("YES, Please"); yesButton.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event1) -> { - String ac = "UPDATE ISSUE SET issueTime = CURRENT_TIMESTAMP, renew_count = renew_count+1 WHERE BOOKID = '" + bookID.getText() + "'"; - System.out.println(ac); - if (databaseHandler.execAction(ac)) { + String ac = "UPDATE ISSUE SET issueTime = CURRENT_TIMESTAMP, renew_count = renew_count+1 WHERE BOOKID = ?"; + if (databaseHandler.execAction(ac, bookID.getText())) { JFXButton btn = new JFXButton("Alright!"); AlertMaker.showMaterialDialog(rootPane, rootAnchorPane, Arrays.asList(btn), "Book Has Been Renewed", null); disableEnableControls(false); diff --git a/test/library/assistant/database/DatabaseHandlerExecTest.java b/test/library/assistant/database/DatabaseHandlerExecTest.java new file mode 100644 index 0000000..9823717 --- /dev/null +++ b/test/library/assistant/database/DatabaseHandlerExecTest.java @@ -0,0 +1,52 @@ +package library.assistant.database; + +import java.sql.ResultSet; +import org.junit.Test; +import static org.junit.Assert.*; + +public class DatabaseHandlerExecTest { + + @Test + public void testExecActionWithParameters() throws Exception { + DatabaseHandler handler = DatabaseHandler.getInstance(); + + // Ensure table exists + handler.execAction("CREATE TABLE TEST_TABLE (ID INT PRIMARY KEY, NAME VARCHAR(255))"); + + // Clear table + handler.execAction("DELETE FROM TEST_TABLE WHERE 1=1"); + + // Insert parameterized + boolean result = handler.execAction("INSERT INTO TEST_TABLE (ID, NAME) VALUES (?, ?)", 1, "TestName"); + assertTrue("Insert should succeed", result); + + // Select parameterized + ResultSet rs = handler.execQuery("SELECT NAME FROM TEST_TABLE WHERE ID = ?", 1); + assertNotNull("ResultSet should not be null", rs); + assertTrue("ResultSet should have result", rs.next()); + assertEquals("Name should match", "TestName", rs.getString("NAME")); + + // SQL Injection attempt in Delete + // First insert another row + handler.execAction("INSERT INTO TEST_TABLE (ID, NAME) VALUES (?, ?)", 2, "OtherName"); + + // Verify we have 2 rows + rs = handler.execQuery("SELECT COUNT(*) FROM TEST_TABLE"); + rs.next(); + assertEquals("Should have 2 rows", 2, rs.getInt(1)); + + // Attempt injection deletion + String payload = "' OR '1'='1"; + // If this was raw string concatenation: DELETE FROM TEST_TABLE WHERE NAME = '' OR '1'='1' + // With parameters, it looks for NAME equal to literal "' OR '1'='1" + handler.execAction("DELETE FROM TEST_TABLE WHERE NAME = ?", payload); + + // Verify we still have 2 rows (because no name matches the payload literal) + rs = handler.execQuery("SELECT COUNT(*) FROM TEST_TABLE"); + rs.next(); + assertEquals("Should still have 2 rows if injection failed", 2, rs.getInt(1)); + + // Clean up + handler.execAction("DROP TABLE TEST_TABLE"); + } +} diff --git a/test/stubs/javafx/collections/FXCollections.java b/test/stubs/javafx/collections/FXCollections.java new file mode 100644 index 0000000..47641db --- /dev/null +++ b/test/stubs/javafx/collections/FXCollections.java @@ -0,0 +1,7 @@ +package javafx.collections; + +public class FXCollections { + public static ObservableList observableArrayList() { + return new ObservableArrayList(); + } +} diff --git a/test/stubs/javafx/collections/ObservableArrayList.java b/test/stubs/javafx/collections/ObservableArrayList.java new file mode 100644 index 0000000..e032621 --- /dev/null +++ b/test/stubs/javafx/collections/ObservableArrayList.java @@ -0,0 +1,6 @@ +package javafx.collections; + +import java.util.ArrayList; + +public class ObservableArrayList extends ArrayList implements ObservableList { +} diff --git a/test/stubs/javafx/collections/ObservableList.java b/test/stubs/javafx/collections/ObservableList.java new file mode 100644 index 0000000..008b638 --- /dev/null +++ b/test/stubs/javafx/collections/ObservableList.java @@ -0,0 +1,6 @@ +package javafx.collections; + +import java.util.List; + +public interface ObservableList extends List { +} diff --git a/test/stubs/javafx/scene/chart/PieChart.java b/test/stubs/javafx/scene/chart/PieChart.java new file mode 100644 index 0000000..ee5078b --- /dev/null +++ b/test/stubs/javafx/scene/chart/PieChart.java @@ -0,0 +1,7 @@ +package javafx.scene.chart; + +public class PieChart { + public static class Data { + public Data(String name, double value) {} + } +} diff --git a/test/stubs/library/assistant/ui/listbook/BookListController.java b/test/stubs/library/assistant/ui/listbook/BookListController.java new file mode 100644 index 0000000..92bd9bb --- /dev/null +++ b/test/stubs/library/assistant/ui/listbook/BookListController.java @@ -0,0 +1,22 @@ +package library.assistant.ui.listbook; + +public class BookListController { + public static class Book { + private String id; + private String title; + private String author; + private String publisher; + + public Book(String id, String title, String author, String publisher) { + this.id = id; + this.title = title; + this.author = author; + this.publisher = publisher; + } + + public String getId() { return id; } + public String getTitle() { return title; } + public String getAuthor() { return author; } + public String getPublisher() { return publisher; } + } +}