From df265dea8b0ddf3ce073f5654229f41e6240e2cb Mon Sep 17 00:00:00 2001 From: Kostya Date: Tue, 18 Aug 2026 07:29:50 +0000 Subject: Harden console path input against crashes Menu.start() runs command.execute() with no surrounding try/catch, so an unchecked InvalidPathException from Path.of() on bad user input crashed the whole console loop. Centralize path parsing in InputProcessor.readPath(), which now validates and reprompts instead of throwing, and strips a surrounding pair of quotes so pasted/quoted paths still work. --- .../guess/ui/console/command/LoadFileMenuCommand.java | 3 +-- .../console/command/LoadMarketStateMenuCommand.java | 3 +-- .../console/command/SaveMarketStateMenuCommand.java | 3 +-- .../market/guess/ui/console/io/InputProcessor.java | 19 +++++++++++++++++-- 4 files changed, 20 insertions(+), 8 deletions(-) (limited to 'ui-console/src/main/java/market') diff --git a/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java index 11ec478..c5b3c77 100644 --- a/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java +++ b/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java @@ -1,6 +1,5 @@ package market.guess.ui.console.command; -import java.nio.file.Path; import market.guess.api.CatalogContext; import market.guess.ui.console.io.InputProcessor; @@ -26,7 +25,7 @@ public final class LoadFileMenuCommand implements MenuCommand { @Override public void execute() { var path = io.readPath("Pleae enter the full file path to the XML file: "); - var result = catalog.loadEvents(Path.of(path)); + var result = catalog.loadEvents(path); if (result.isSuccess()) { io.println( diff --git a/ui-console/src/main/java/market/guess/ui/console/command/LoadMarketStateMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/LoadMarketStateMenuCommand.java index 26d00c1..30acef3 100644 --- a/ui-console/src/main/java/market/guess/ui/console/command/LoadMarketStateMenuCommand.java +++ b/ui-console/src/main/java/market/guess/ui/console/command/LoadMarketStateMenuCommand.java @@ -1,6 +1,5 @@ package market.guess.ui.console.command; -import java.nio.file.Path; import market.guess.api.CatalogContext; import market.guess.ui.console.io.InputProcessor; @@ -27,7 +26,7 @@ public final class LoadMarketStateMenuCommand implements MenuCommand { @Override public void execute() { var path = io.readPath("Which state file to load? "); - var result = catalog.restoreState(Path.of(path)); + var result = catalog.restoreState(path); if (result.isSuccess()) { io.println("Restored %d event(s) from %s.".formatted(result.getData().eventsLoaded(), path)); diff --git a/ui-console/src/main/java/market/guess/ui/console/command/SaveMarketStateMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/SaveMarketStateMenuCommand.java index 77400d2..3c5d6b9 100644 --- a/ui-console/src/main/java/market/guess/ui/console/command/SaveMarketStateMenuCommand.java +++ b/ui-console/src/main/java/market/guess/ui/console/command/SaveMarketStateMenuCommand.java @@ -1,6 +1,5 @@ package market.guess.ui.console.command; -import java.nio.file.Path; import market.guess.api.CatalogContext; import market.guess.ui.console.io.InputProcessor; @@ -29,7 +28,7 @@ public final class SaveMarketStateMenuCommand implements MenuCommand { var path = io.readPath("Save to: "); try { - catalog.saveState(Path.of(path)); + catalog.saveState(path); io.println("Saved current state to %s.".formatted(path)); } catch (Exception e) { io.println("Failed to save: %s".formatted(e.getMessage())); diff --git a/ui-console/src/main/java/market/guess/ui/console/io/InputProcessor.java b/ui-console/src/main/java/market/guess/ui/console/io/InputProcessor.java index 7587768..512fdfe 100644 --- a/ui-console/src/main/java/market/guess/ui/console/io/InputProcessor.java +++ b/ui-console/src/main/java/market/guess/ui/console/io/InputProcessor.java @@ -1,5 +1,7 @@ package market.guess.ui.console.io; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; import java.util.List; import java.util.Scanner; import java.util.function.Function; @@ -95,7 +97,7 @@ public final class InputProcessor { } } - public String readPath(String prompt) { + public Path readPath(String prompt) { while (true) { var raw = readLine(prompt); @@ -104,8 +106,21 @@ public final class InputProcessor { continue; } - return raw; + try { + return Path.of(unquote(raw)); + } catch (InvalidPathException e) { + println("'%s' is not a valid file path.".formatted(raw)); + } + } + } + + private static String unquote(String raw) { + if (raw.length() >= 2 + && ((raw.charAt(0) == '"' && raw.charAt(raw.length() - 1) == '"') + || (raw.charAt(0) == '\'' && raw.charAt(raw.length() - 1) == '\''))) { + return raw.substring(1, raw.length() - 1); } + return raw; } public boolean readYesNo(String prompt) { -- cgit v1.2.3