diff --git a/src/library/assistant/database/DatabaseHandler.java b/src/library/assistant/database/DatabaseHandler.java index e9c4f53..616372b 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); @@ -123,6 +123,38 @@ 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]); + } + pstmt.closeOnCompletion(); + result = pstmt.executeQuery(); + } + catch (SQLException ex) { + System.out.println("Exception at execQuery:dataHandler" + ex.getLocalizedMessage()); + return null; + } + return result; + } + + public boolean execAction(String query, Object... params) { + try (PreparedStatement pstmt = conn.prepareStatement(query)) { + for (int i = 0; i < params.length; i++) { + pstmt.setObject(i + 1, params[i]); + } + pstmt.execute(); + return true; + } + catch (SQLException ex) { + JOptionPane.showMessageDialog(null, "Error:" + ex.getMessage(), "Error Occured", JOptionPane.ERROR_MESSAGE); + System.out.println("Exception at execQuery:dataHandler" + ex.getLocalizedMessage()); + return false; + } + } + public boolean execAction(String qu) { try { stmt = conn.createStatement(); diff --git a/src/library/assistant/ui/main/MainController.java b/src/library/assistant/ui/main/MainController.java index 50b0fff..0accef6 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,11 @@ 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 + "'"; + String str = "INSERT INTO ISSUE(memberID,bookID) VALUES (?, ?)"; + String str2 = "UPDATE BOOK SET isAvail = false WHERE id = ?"; System.out.println(str + " and " + str2); - 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 +279,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 +328,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 +362,9 @@ 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() + "'"; + String ac = "UPDATE ISSUE SET issueTime = CURRENT_TIMESTAMP, renew_count = renew_count+1 WHERE BOOKID = ?"; System.out.println(ac); - if (databaseHandler.execAction(ac)) { + 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..1dbf8b6 --- /dev/null +++ b/test/library/assistant/database/DatabaseHandlerExecTest.java @@ -0,0 +1,68 @@ +package library.assistant.database; + +import java.sql.ResultSet; +import java.sql.SQLException; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.BeforeClass; + +public class DatabaseHandlerExecTest { + + private static DatabaseHandler handler; + + @BeforeClass + public static void setUpClass() { + handler = DatabaseHandler.getInstance(); + } + + @Test + public void testExecActionWithParams() { + String bookID = "TestBook_" + System.currentTimeMillis(); + String title = "Test Title"; + String author = "Test Author"; + String publisher = "Test Publisher"; + Boolean isAvail = true; + + String insertQuery = "INSERT INTO BOOK(id,title,author,publisher,isAvail) VALUES(?,?,?,?,?)"; + boolean result = handler.execAction(insertQuery, bookID, title, author, publisher, isAvail); + assertTrue("Insert should succeed", result); + + String selectQuery = "SELECT * FROM BOOK WHERE id = ?"; + ResultSet rs = handler.execQuery(selectQuery, bookID); + try { + assertTrue("Should find the book", rs.next()); + assertEquals(title, rs.getString("title")); + } catch (SQLException e) { + fail(e.getMessage()); + } + + handler.execAction("DELETE FROM BOOK WHERE id = ?", bookID); + } + + @Test + public void testSqlInjectionPrevention() { + String bookID = "InjBook_" + System.currentTimeMillis(); + String title = "Inj Title"; + String author = "Inj Author"; + String publisher = "Inj Publisher"; + Boolean isAvail = true; + + handler.execAction("INSERT INTO BOOK(id,title,author,publisher,isAvail) VALUES(?,?,?,?,?)", + bookID, title, author, publisher, isAvail); + + String payload = "' OR '1'='1"; + String deleteQuery = "DELETE FROM BOOK WHERE id = ?"; + + // Attempt deletion with payload + handler.execAction(deleteQuery, payload); + + ResultSet rs = handler.execQuery("SELECT * FROM BOOK WHERE id = ?", bookID); + try { + assertTrue("Book should still exist because deletion payload was treated as literal", rs.next()); + } catch (SQLException e) { + fail(e.getMessage()); + } + + handler.execAction("DELETE FROM BOOK WHERE id = ?", bookID); + } +} diff --git a/test/stubs/javafx/collections/FXCollections.java b/test/stubs/javafx/collections/FXCollections.java new file mode 100644 index 0000000..c602b27 --- /dev/null +++ b/test/stubs/javafx/collections/FXCollections.java @@ -0,0 +1,12 @@ +package javafx.collections; + +import java.util.ArrayList; + +public class FXCollections { + public static ObservableList observableArrayList() { + return new ObservableArrayListStub<>(); + } + + private static class ObservableArrayListStub 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..38f6290 --- /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 title, String id, String author, String publisher, Boolean isAvail) { + this.title = title; + this.id = id; + 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; } + } +}