From c691eaf3c9972857aba3d8019c6e50f62962121f Mon Sep 17 00:00:00 2001 From: Daniel McCoy Stephenson Date: Fri, 2 Oct 2026 01:35:23 -0600 Subject: [PATCH] Keep build() from throwing on an unconvertible plugins path File.toPath() ran outside the try in readServerWideConfig, so a path the file system cannot represent (a NUL in the name) let an InvalidPathException escape build(), which promises never to throw. It is now converted inside the try and treated like any other IO failure: logged at FINE, counted as enabled. Closes #7 Co-Authored-By: Claude Opus 5.5 --- .../stephenson/trace/TraceClient.java | 7 ++++-- .../stephenson/trace/TraceClientTest.java | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/main/java/software/stephenson/trace/TraceClient.java b/src/main/java/software/stephenson/trace/TraceClient.java index 42eaec4..26cad2a 100644 --- a/src/main/java/software/stephenson/trace/TraceClient.java +++ b/src/main/java/software/stephenson/trace/TraceClient.java @@ -286,8 +286,11 @@ static final class ServerWideConfig { * nor stop the host program. */ private ServerWideConfig readServerWideConfig(File pluginsDirectory) { - Path file = new File(pluginsDirectory, SERVER_WIDE_CONFIG_PATH).toPath(); + File location = new File(pluginsDirectory, SERVER_WIDE_CONFIG_PATH); try { + // Inside the try: toPath() throws InvalidPathException for a path + // the file system cannot represent, and build() never throws. + Path file = location.toPath(); if (!Files.exists(file)) { Files.createDirectories(file.getParent()); Files.write(file, SERVER_WIDE_CONFIG_CONTENT.getBytes(StandardCharsets.UTF_8)); @@ -295,7 +298,7 @@ private ServerWideConfig readServerWideConfig(File pluginsDirectory) { } return parseServerWideConfig(Files.readAllLines(file, StandardCharsets.UTF_8)); } catch (IOException | RuntimeException failure) { - log("could not read server-wide config " + file + ": " + failure); + log("could not read server-wide config " + location + ": " + failure); return ServerWideConfig.NONE; } } diff --git a/src/test/java/software/stephenson/trace/TraceClientTest.java b/src/test/java/software/stephenson/trace/TraceClientTest.java index 05866af..e3aa781 100644 --- a/src/test/java/software/stephenson/trace/TraceClientTest.java +++ b/src/test/java/software/stephenson/trace/TraceClientTest.java @@ -957,6 +957,29 @@ void serverWideConfig_ioFailureIsLoggedFineAndTreatedAsEnabled(@TempDir Path scr client.close(); } + @Test + void serverWideConfig_aPathThatCannotBeConvertedIsLoggedFineAndTreatedAsEnabled() throws Exception { + // Arrange + // A NUL in the name makes File.toPath() throw InvalidPathException -- + // a RuntimeException that build() must not let out. + File unconvertible = new File("plugins\u0000"); + RecordingHandler log = new RecordingHandler(); + Logger logger = Logger.getLogger("TraceClientTest.serverWideInvalidPath"); + logger.setLevel(Level.ALL); + logger.addHandler(log); + + // Act + TraceClient client = assertDoesNotThrow(() -> TraceClient.builder(baseUrl(), "MyPlugin", "1.2.3").key("k") + .serverWideConfig(unconvertible).logger(logger).build()); + + // Assert + assertTrue(client.isEnabled(), "a switch file that cannot be located must not turn reporting off"); + assertTrue(log.await(1, TimeUnit.SECONDS), "the failure should be mentioned at FINE"); + assertEquals(Level.FINE, log.records.get(0).getLevel()); + assertTrue(log.records.get(0).getMessage().contains("server-wide config"), log.records.get(0).getMessage()); + client.close(); + } + private static Path writeServerWideConfig(Path plugins, String content) throws java.io.IOException { Path file = plugins.resolve("trace").resolve("config.yml"); Files.createDirectories(file.getParent());