From 73d01d69ddce942d9b4276d0e72c89d8373038f1 Mon Sep 17 00:00:00 2001 From: Gerben Kroes Date: Thu, 15 Feb 2024 15:43:47 +0100 Subject: [PATCH] SMHE-191: An external SIG analysis, recommended to make this not a public class. This class could be vulnerable to arbitrary command execution. The reason is that this class executes a command on the operating system. Signed-off-by: Gerben Kroes --- .../infra/networking/ping/CommandBuilder.java | 118 --------- .../networking/ping/CommandExecutor.java | 122 --------- .../shared/infra/networking/ping/Pinger.java | 236 +++++++++++++++++- .../networking/ping/CommandBuilderTest.java | 119 --------- .../infra/networking/ping/PingerTest.java | 128 +++++++++- 5 files changed, 348 insertions(+), 375 deletions(-) delete mode 100644 osgp/shared/shared/src/main/java/org/opensmartgridplatform/shared/infra/networking/ping/CommandBuilder.java delete mode 100644 osgp/shared/shared/src/main/java/org/opensmartgridplatform/shared/infra/networking/ping/CommandExecutor.java delete mode 100644 osgp/shared/shared/src/test/java/org/opensmartgridplatform/shared/infra/networking/ping/CommandBuilderTest.java diff --git a/osgp/shared/shared/src/main/java/org/opensmartgridplatform/shared/infra/networking/ping/CommandBuilder.java b/osgp/shared/shared/src/main/java/org/opensmartgridplatform/shared/infra/networking/ping/CommandBuilder.java deleted file mode 100644 index b861502a2b7..00000000000 --- a/osgp/shared/shared/src/main/java/org/opensmartgridplatform/shared/infra/networking/ping/CommandBuilder.java +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-FileCopyrightText: Copyright Contributors to the GXF project -// -// SPDX-License-Identifier: Apache-2.0 - -package org.opensmartgridplatform.shared.infra.networking.ping; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -public class CommandBuilder { - - public static final int MIN_SIZE = 0; - public static final int MAX_SIZE = 65500; - - private PingFlavor pingFlavor = PingFlavor.forCurrentOs(); - - private int count = 1; - - private int size = -1; - - private Duration timeout = null; - - private Boolean lookupNamesForHostAddresses = null; - - private String destination = "127.0.0.1"; - - public CommandBuilder withPingFlavor(final PingFlavor pingFlavor) { - this.pingFlavor = pingFlavor; - return this; - } - - public CommandBuilder withCount(final int count) { - if (count < 0) { - throw new IllegalArgumentException(); - } - this.count = count; - return this; - } - - public CommandBuilder withDefaultCount() { - this.count = 0; - return this; - } - - public CommandBuilder withSize(final int size) { - if (size != -1 && (size < MIN_SIZE || size > MAX_SIZE)) { - throw new IllegalArgumentException( - String.format("size out of range '%d <= value <= %d': %d", MIN_SIZE, MAX_SIZE, size)); - } - this.size = size; - return this; - } - - public CommandBuilder withDefaultSize() { - this.size = -1; - return this; - } - - public CommandBuilder withTimeout(final Duration timeout) { - if (timeout != null && (timeout.isZero() || timeout.isNegative())) { - throw new IllegalArgumentException("timeout must have a positive duration: " + timeout); - } - this.timeout = timeout; - return this; - } - - public CommandBuilder withDefaultTimeout() { - this.timeout = null; - return this; - } - - public CommandBuilder withLookupNamesForHostAddresses(final Boolean lookupNamesForHostAddresses) { - this.lookupNamesForHostAddresses = lookupNamesForHostAddresses; - return this; - } - - public CommandBuilder withDefaultLookupNamesForHostAddresses() { - this.lookupNamesForHostAddresses = null; - return this; - } - - public CommandBuilder withDestination(final String destination) { - this.destination = Objects.requireNonNull(destination, "destination"); - return this; - } - - public List commands() { - final List commands = new ArrayList<>(); - commands.add(this.pingFlavor.pingCommand()); - if (this.count > 0) { - commands.add(this.pingFlavor.countFlag(this.count)); - } - if (this.size > -1) { - commands.add(this.pingFlavor.sizeFlag(this.size)); - } - if (this.timeout != null && !this.timeout.isNegative() && !this.timeout.isZero()) { - commands.add(this.pingFlavor.timeoutFlag(this.timeout)); - } - if (this.lookupNamesForHostAddresses != null) { - final String flag = - this.pingFlavor.lookupNamesForHostAddressesFlag(this.lookupNamesForHostAddresses); - if (!flag.isEmpty()) { - commands.add(flag); - } - } - commands.add(this.destination); - return commands; - } - - @Override - public String toString() { - return String.format( - "CommandBuilder[flavor=%s, count=%d, size=%d, timeout=%s, lookupNames=%s]", - this.pingFlavor, this.count, this.size, this.timeout, this.lookupNamesForHostAddresses); - } -} diff --git a/osgp/shared/shared/src/main/java/org/opensmartgridplatform/shared/infra/networking/ping/CommandExecutor.java b/osgp/shared/shared/src/main/java/org/opensmartgridplatform/shared/infra/networking/ping/CommandExecutor.java deleted file mode 100644 index 81c78c44364..00000000000 --- a/osgp/shared/shared/src/main/java/org/opensmartgridplatform/shared/infra/networking/ping/CommandExecutor.java +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-FileCopyrightText: Copyright Contributors to the GXF project -// -// SPDX-License-Identifier: Apache-2.0 - -package org.opensmartgridplatform.shared.infra.networking.ping; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.SequenceInputStream; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class CommandExecutor { - - private static final Logger LOGGER = LoggerFactory.getLogger(CommandExecutor.class); - - private static final long AWAIT_TERMINATION_IN_SEC = 5; - - public List execute(final List commands, final Duration timeout) - throws IOException, TimeoutException, ExecutionException { - - final String commandLine = this.commandLine(commands); - final Process process = this.start(commands); - - final ExecutorService executorService = Executors.newSingleThreadExecutor(); - final Future> inputLines = - executorService.submit(() -> this.readLinesFromInput(process)); - try { - return inputLines.get(timeout.toMillis(), TimeUnit.MILLISECONDS); - } catch (final TimeoutException e) { - inputLines.cancel(true); - throw e; - } catch (final InterruptedException e) { - LOGGER.warn( - "Reading input lines from executed process was interrupted: \"{}\"", commandLine, e); - } finally { - this.shutdownAndAwaitTermination(executorService); - this.destroyProcess(process, commands); - } - return Collections.emptyList(); - } - - private void destroyProcess(final Process process, final List commands) { - process.destroy(); - if (process.isAlive()) { - LOGGER.debug("Destroy the process running \"{}\"", commands); - process.destroyForcibly(); - } else { - LOGGER.debug( - "Process running \"{}\" ended with exit value: {}", commands, process.exitValue()); - } - } - - void shutdownAndAwaitTermination(final ExecutorService executorService) { - executorService.shutdown(); - try { - if (!executorService.awaitTermination(AWAIT_TERMINATION_IN_SEC, TimeUnit.SECONDS)) { - executorService.shutdownNow(); - if (!executorService.awaitTermination(AWAIT_TERMINATION_IN_SEC, TimeUnit.SECONDS)) { - LOGGER.error("Pool did not terminate"); - } - } - } catch (final InterruptedException ex) { - executorService.shutdownNow(); - Thread.currentThread().interrupt(); - } - } - - private String commandLine(final List commands) { - return String.join(" ", commands); - } - - public List execute(final List commands) throws IOException { - final Process process = this.start(commands); - try { - return this.readLinesFromInput(process); - } finally { - if (process.isAlive()) { - final String commandLine = this.commandLine(commands); - LOGGER.debug("Destroy the process running \"{}\"", commandLine); - process.destroyForcibly(); - } else { - LOGGER.debug( - "Process running \"{}\" ended with exit value: {}", commands, process.exitValue()); - } - } - } - - private Process start(final List commands) throws IOException { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("About to execute: \"{}\"", this.commandLine(commands)); - } - return new ProcessBuilder(commands).start(); - } - - public List readLinesFromInput(final Process process) throws IOException { - final List lines = new ArrayList<>(); - try (final BufferedReader reader = - new BufferedReader( - new InputStreamReader( - new SequenceInputStream(process.getInputStream(), process.getErrorStream())))) { - String line = reader.readLine(); - while (line != null) { - LOGGER.debug("Input line: \"{}\"", line); - lines.add(line); - line = reader.readLine(); - } - } - return lines; - } -} diff --git a/osgp/shared/shared/src/main/java/org/opensmartgridplatform/shared/infra/networking/ping/Pinger.java b/osgp/shared/shared/src/main/java/org/opensmartgridplatform/shared/infra/networking/ping/Pinger.java index 3d2657ad206..74543d4a262 100644 --- a/osgp/shared/shared/src/main/java/org/opensmartgridplatform/shared/infra/networking/ping/Pinger.java +++ b/osgp/shared/shared/src/main/java/org/opensmartgridplatform/shared/infra/networking/ping/Pinger.java @@ -4,12 +4,22 @@ package org.opensmartgridplatform.shared.infra.networking.ping; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; +import java.io.SequenceInputStream; import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Predicate; import org.slf4j.Logger; @@ -19,7 +29,7 @@ public class Pinger { private static final Logger LOGGER = LoggerFactory.getLogger(Pinger.class); - private final CommandExecutor commandExecutor; + private final CommandExecutor commandExecutor = new CommandExecutor(); private final int count; private final int size; private final Duration timeout; @@ -31,20 +41,10 @@ public Pinger( final int size, final Duration timeout, final Boolean lookupNamesForHostAddresses) { - this(count, size, timeout, lookupNamesForHostAddresses, new CommandExecutor()); - } - - Pinger( - final int count, - final int size, - final Duration timeout, - final Boolean lookupNamesForHostAddresses, - final CommandExecutor commandExecutor) { this.count = count; this.size = size; this.timeout = timeout; this.lookupNamesForHostAddresses = lookupNamesForHostAddresses; - this.commandExecutor = commandExecutor; } public boolean ping(final String destination) { @@ -81,4 +81,218 @@ public boolean ping(final String destination) { } return false; } + + /* + * An external SIG analysis, recommended to make this not a public class. + * This class could be vulnerable to arbitrary command execution. + * The reason is that this class executes a command on the operating system. + */ + class CommandExecutor { + + private static final Logger LOGGER = LoggerFactory.getLogger(CommandExecutor.class); + + private static final long AWAIT_TERMINATION_IN_SEC = 5; + + public List execute(final List commands, final Duration timeout) + throws IOException, TimeoutException, ExecutionException { + + final String commandLine = this.commandLine(commands); + final Process process = this.start(commands); + + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + final Future> inputLines = + executorService.submit(() -> this.readLinesFromInput(process)); + try { + return inputLines.get(timeout.toMillis(), TimeUnit.MILLISECONDS); + } catch (final TimeoutException e) { + inputLines.cancel(true); + throw e; + } catch (final InterruptedException e) { + LOGGER.warn( + "Reading input lines from executed process was interrupted: \"{}\"", commandLine, e); + } finally { + this.shutdownAndAwaitTermination(executorService); + this.destroyProcess(process, commands); + } + return Collections.emptyList(); + } + + private void destroyProcess(final Process process, final List commands) { + process.destroy(); + if (process.isAlive()) { + LOGGER.debug("Destroy the process running \"{}\"", commands); + process.destroyForcibly(); + } else { + LOGGER.debug( + "Process running \"{}\" ended with exit value: {}", commands, process.exitValue()); + } + } + + void shutdownAndAwaitTermination(final ExecutorService executorService) { + executorService.shutdown(); + try { + if (!executorService.awaitTermination(AWAIT_TERMINATION_IN_SEC, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + if (!executorService.awaitTermination(AWAIT_TERMINATION_IN_SEC, TimeUnit.SECONDS)) { + LOGGER.error("Pool did not terminate"); + } + } + } catch (final InterruptedException ex) { + executorService.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + private String commandLine(final List commands) { + return String.join(" ", commands); + } + + public List execute(final List commands) throws IOException { + final Process process = this.start(commands); + try { + return this.readLinesFromInput(process); + } finally { + if (process.isAlive()) { + final String commandLine = this.commandLine(commands); + LOGGER.debug("Destroy the process running \"{}\"", commandLine); + process.destroyForcibly(); + } else { + LOGGER.debug( + "Process running \"{}\" ended with exit value: {}", commands, process.exitValue()); + } + } + } + + private Process start(final List commands) throws IOException { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("About to execute: \"{}\"", this.commandLine(commands)); + } + return new ProcessBuilder(commands).start(); + } + + public List readLinesFromInput(final Process process) throws IOException { + final List lines = new ArrayList<>(); + try (final BufferedReader reader = + new BufferedReader( + new InputStreamReader( + new SequenceInputStream(process.getInputStream(), process.getErrorStream())))) { + String line = reader.readLine(); + while (line != null) { + LOGGER.debug("Input line: \"{}\"", line); + lines.add(line); + line = reader.readLine(); + } + } + return lines; + } + } + + static class CommandBuilder { + + public static final int MIN_SIZE = 0; + public static final int MAX_SIZE = 65500; + + private PingFlavor pingFlavor = PingFlavor.forCurrentOs(); + + private int count = 1; + + private int size = -1; + + private Duration timeout = null; + + private Boolean lookupNamesForHostAddresses = null; + + private String destination = "127.0.0.1"; + + public CommandBuilder withPingFlavor(final PingFlavor pingFlavor) { + this.pingFlavor = pingFlavor; + return this; + } + + public CommandBuilder withCount(final int count) { + if (count < 0) { + throw new IllegalArgumentException(); + } + this.count = count; + return this; + } + + public CommandBuilder withDefaultCount() { + this.count = 0; + return this; + } + + public CommandBuilder withSize(final int size) { + if (size != -1 && (size < MIN_SIZE || size > MAX_SIZE)) { + throw new IllegalArgumentException( + String.format("size out of range '%d <= value <= %d': %d", MIN_SIZE, MAX_SIZE, size)); + } + this.size = size; + return this; + } + + public CommandBuilder withDefaultSize() { + this.size = -1; + return this; + } + + public CommandBuilder withTimeout(final Duration timeout) { + if (timeout != null && (timeout.isZero() || timeout.isNegative())) { + throw new IllegalArgumentException("timeout must have a positive duration: " + timeout); + } + this.timeout = timeout; + return this; + } + + public CommandBuilder withDefaultTimeout() { + this.timeout = null; + return this; + } + + public CommandBuilder withLookupNamesForHostAddresses( + final Boolean lookupNamesForHostAddresses) { + this.lookupNamesForHostAddresses = lookupNamesForHostAddresses; + return this; + } + + public CommandBuilder withDefaultLookupNamesForHostAddresses() { + this.lookupNamesForHostAddresses = null; + return this; + } + + public CommandBuilder withDestination(final String destination) { + this.destination = Objects.requireNonNull(destination, "destination"); + return this; + } + + public List commands() { + final List commands = new ArrayList<>(); + commands.add(this.pingFlavor.pingCommand()); + if (this.count > 0) { + commands.add(this.pingFlavor.countFlag(this.count)); + } + if (this.size > -1) { + commands.add(this.pingFlavor.sizeFlag(this.size)); + } + if (this.timeout != null && !this.timeout.isNegative() && !this.timeout.isZero()) { + commands.add(this.pingFlavor.timeoutFlag(this.timeout)); + } + if (this.lookupNamesForHostAddresses != null) { + final String flag = + this.pingFlavor.lookupNamesForHostAddressesFlag(this.lookupNamesForHostAddresses); + if (!flag.isEmpty()) { + commands.add(flag); + } + } + commands.add(this.destination); + return commands; + } + + @Override + public String toString() { + return String.format( + "CommandBuilder[flavor=%s, count=%d, size=%d, timeout=%s, lookupNames=%s]", + this.pingFlavor, this.count, this.size, this.timeout, this.lookupNamesForHostAddresses); + } + } } diff --git a/osgp/shared/shared/src/test/java/org/opensmartgridplatform/shared/infra/networking/ping/CommandBuilderTest.java b/osgp/shared/shared/src/test/java/org/opensmartgridplatform/shared/infra/networking/ping/CommandBuilderTest.java deleted file mode 100644 index a5e2447edb3..00000000000 --- a/osgp/shared/shared/src/test/java/org/opensmartgridplatform/shared/infra/networking/ping/CommandBuilderTest.java +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-FileCopyrightText: Copyright Contributors to the GXF project -// -// SPDX-License-Identifier: Apache-2.0 - -package org.opensmartgridplatform.shared.infra.networking.ping; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.params.provider.Arguments.arguments; - -import java.time.Duration; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Stream; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -class CommandBuilderTest { - - static Stream commandBuilderArgumentsProvider() { - return Stream.of( - arguments( - PingFlavor.LINUX, - 2, - 56, - Duration.ofSeconds(2), - false, - "lfenergy.org", - Arrays.asList("ping", "-c 2", "-s 56", "-W 2", "-n", "lfenergy.org")), - arguments( - PingFlavor.LINUX, - 2, - -1, - null, - true, - "192.168.12.27", - Arrays.asList("ping", "-c 2", "192.168.12.27")), - arguments( - PingFlavor.LINUX, - 0, - 0, - Duration.ofMillis(2718), - null, - "lfenergy.org", - Arrays.asList("ping", "-s 0", "-W 2", "lfenergy.org")), - arguments( - PingFlavor.MAC_OS, - 5, - 0, - null, - null, - "192.128.71.129", - Arrays.asList("ping", "-c 5", "-s 0", "192.128.71.129")), - arguments( - PingFlavor.MAC_OS, - 0, - -1, - Duration.ofSeconds(37), - true, - "127.0.0.1", - Arrays.asList("ping", "-t 37", "127.0.0.1")), - arguments( - PingFlavor.MAC_OS, - 5, - 24, - Duration.ofSeconds(37), - false, - "127.0.0.1", - Arrays.asList("ping", "-c 5", "-s 24", "-t 37", "-n", "127.0.0.1")), - arguments( - PingFlavor.WINDOWS, - 3, - 0, - Duration.ofMillis(3141), - true, - "opensmartgridplatform.org", - Arrays.asList("ping", "-n 3", "-l 0", "-w 3141", "-a", "opensmartgridplatform.org")), - arguments( - PingFlavor.WINDOWS, - 3, - 0, - null, - false, - "opensmartgridplatform.org", - Arrays.asList("ping", "-n 3", "-l 0", "opensmartgridplatform.org")), - arguments( - PingFlavor.WINDOWS, - 0, - -1, - Duration.ofMillis(1618), - null, - "192.168.1.81", - Arrays.asList("ping", "-w 1618", "192.168.1.81"))); - } - - @ParameterizedTest - @MethodSource("commandBuilderArgumentsProvider") - void commandBuilderGivesCorrectCommands( - final PingFlavor pingFlavor, - final int count, - final int size, - final Duration timeout, - final Boolean lookupNamesForHostAddresses, - final String destination, - final List expectedCommands) { - - final List actualCommands = - new CommandBuilder() - .withPingFlavor(pingFlavor) - .withCount(count) - .withSize(size) - .withTimeout(timeout) - .withLookupNamesForHostAddresses(lookupNamesForHostAddresses) - .withDestination(destination) - .commands(); - - assertThat(actualCommands).isEqualTo(expectedCommands); - } -} diff --git a/osgp/shared/shared/src/test/java/org/opensmartgridplatform/shared/infra/networking/ping/PingerTest.java b/osgp/shared/shared/src/test/java/org/opensmartgridplatform/shared/infra/networking/ping/PingerTest.java index 66e058007be..e5639b7a0e7 100644 --- a/osgp/shared/shared/src/test/java/org/opensmartgridplatform/shared/infra/networking/ping/PingerTest.java +++ b/osgp/shared/shared/src/test/java/org/opensmartgridplatform/shared/infra/networking/ping/PingerTest.java @@ -5,6 +5,7 @@ package org.opensmartgridplatform.shared.infra.networking.ping; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.when; @@ -12,12 +13,19 @@ import java.io.IOException; import java.time.Duration; import java.util.Arrays; +import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.opensmartgridplatform.shared.infra.networking.ping.Pinger.CommandExecutor; +import org.springframework.test.util.ReflectionTestUtils; @ExtendWith(MockitoExtension.class) class PingerTest { @@ -27,7 +35,7 @@ class PingerTest { @Test void pingReturnsTrueWhenReceivingPackages() throws Exception { this.whenPingCommandIsExecutedReceivingPackages(); - final Pinger pinger = new Pinger(1, 0, Duration.ofSeconds(1), false, this.commandExecutor); + final Pinger pinger = this.newPinger(1, 0, Duration.ofSeconds(1), false); final boolean pingResult = pinger.ping("localhost"); @@ -37,7 +45,7 @@ void pingReturnsTrueWhenReceivingPackages() throws Exception { @Test void pingReturnsFalseWhenNotReceivingPackages() throws Exception { this.whenPingCommandIsExecutedWithoutReceivingPackages(); - final Pinger pinger = new Pinger(100, 24, Duration.ofSeconds(15), true, this.commandExecutor); + final Pinger pinger = this.newPinger(100, 24, Duration.ofSeconds(15), true); final boolean pingResult = pinger.ping("192.168.5.3"); @@ -47,7 +55,7 @@ void pingReturnsFalseWhenNotReceivingPackages() throws Exception { @Test void pingReturnsFalseOnExceptionExecutingPing() throws Exception { this.whenAnExceptionIsThrownExecutingThePingCommand(); - final Pinger pinger = new Pinger(0, -1, Duration.ofSeconds(7), null, this.commandExecutor); + final Pinger pinger = this.newPinger(0, -1, Duration.ofSeconds(7), null); final boolean pingResult = pinger.ping("192.168.1.1"); @@ -57,7 +65,7 @@ void pingReturnsFalseOnExceptionExecutingPing() throws Exception { @Test void pingReturnsFalseOnExceptionProcessingPingInputBeforeTimeout() throws Exception { this.whenAnExceptionIsThrownProcessingInputFromThePingCommandBeforeTimeout(); - final Pinger pinger = new Pinger(2, 100, Duration.ofSeconds(8), false, this.commandExecutor); + final Pinger pinger = this.newPinger(2, 100, Duration.ofSeconds(8), false); final boolean pingResult = pinger.ping("192.168.1.1"); @@ -67,13 +75,23 @@ void pingReturnsFalseOnExceptionProcessingPingInputBeforeTimeout() throws Except @Test void pingReturnsFalseWhenPingResultIsNotAvailableBeforeTimeout() throws Exception { this.whenThePingCommandResultsAreNotObtainedBeforeTheTimeout(); - final Pinger pinger = new Pinger(5, 56, Duration.ofSeconds(12), null, this.commandExecutor); + final Pinger pinger = this.newPinger(5, 56, Duration.ofSeconds(12), null); final boolean pingResult = pinger.ping("192.168.2.3"); assertThat(pingResult).isFalse(); } + private Pinger newPinger( + final int count, + final int size, + final Duration timeout, + final Boolean lookupNamesForHostAddresses) { + final Pinger pinger = new Pinger(count, size, timeout, lookupNamesForHostAddresses); + ReflectionTestUtils.setField(pinger, "commandExecutor", this.commandExecutor); + return pinger; + } + private void whenPingCommandIsExecutedReceivingPackages() throws Exception { when(this.commandExecutor.execute(anyList(), any(Duration.class))) .thenReturn( @@ -112,4 +130,104 @@ private void whenThePingCommandResultsAreNotObtainedBeforeTheTimeout() throws Ex when(this.commandExecutor.execute(anyList(), any(Duration.class))) .thenThrow(TimeoutException.class); } + + static Stream commandBuilderArgumentsProvider() { + return Stream.of( + arguments( + PingFlavor.LINUX, + 2, + 56, + Duration.ofSeconds(2), + false, + "lfenergy.org", + Arrays.asList("ping", "-c 2", "-s 56", "-W 2", "-n", "lfenergy.org")), + arguments( + PingFlavor.LINUX, + 2, + -1, + null, + true, + "192.168.12.27", + Arrays.asList("ping", "-c 2", "192.168.12.27")), + arguments( + PingFlavor.LINUX, + 0, + 0, + Duration.ofMillis(2718), + null, + "lfenergy.org", + Arrays.asList("ping", "-s 0", "-W 2", "lfenergy.org")), + arguments( + PingFlavor.MAC_OS, + 5, + 0, + null, + null, + "192.128.71.129", + Arrays.asList("ping", "-c 5", "-s 0", "192.128.71.129")), + arguments( + PingFlavor.MAC_OS, + 0, + -1, + Duration.ofSeconds(37), + true, + "127.0.0.1", + Arrays.asList("ping", "-t 37", "127.0.0.1")), + arguments( + PingFlavor.MAC_OS, + 5, + 24, + Duration.ofSeconds(37), + false, + "127.0.0.1", + Arrays.asList("ping", "-c 5", "-s 24", "-t 37", "-n", "127.0.0.1")), + arguments( + PingFlavor.WINDOWS, + 3, + 0, + Duration.ofMillis(3141), + true, + "opensmartgridplatform.org", + Arrays.asList("ping", "-n 3", "-l 0", "-w 3141", "-a", "opensmartgridplatform.org")), + arguments( + PingFlavor.WINDOWS, + 3, + 0, + null, + false, + "opensmartgridplatform.org", + Arrays.asList("ping", "-n 3", "-l 0", "opensmartgridplatform.org")), + arguments( + PingFlavor.WINDOWS, + 0, + -1, + Duration.ofMillis(1618), + null, + "192.168.1.81", + Arrays.asList("ping", "-w 1618", "192.168.1.81"))); + } + + @ParameterizedTest + @MethodSource("commandBuilderArgumentsProvider") + void commandBuilderGivesCorrectCommands( + final PingFlavor pingFlavor, + final int count, + final int size, + final Duration timeout, + final Boolean lookupNamesForHostAddresses, + final String destination, + final List expectedCommands) { + + final List actualCommands = + new Pinger.CommandBuilder() + .withPingFlavor(pingFlavor) + .withCount(count) + .withSize(size) + .withTimeout(timeout) + .withLookupNamesForHostAddresses(lookupNamesForHostAddresses) + .withDestination(destination) + .commands(); + + assertThat(actualCommands).isEqualTo(expectedCommands); + } }