From 1af2dad3bd811ac2cf4230920d0f2942a39187e1 Mon Sep 17 00:00:00 2001 From: Jeff Dyke Date: Tue, 6 Aug 2019 18:29:09 -0400 Subject: [PATCH 1/7] need to change version, but logging is safe --- .../com/decodified/scalassh/Command.scala | 20 ++++++++++++++++--- .../scala/com/decodified/scalassh/SSH.scala | 4 ++-- .../com/decodified/scalassh/SshClient.scala | 11 +++++++--- version.sbt | 2 +- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/main/scala/com/decodified/scalassh/Command.scala b/src/main/scala/com/decodified/scalassh/Command.scala index 031046e..881b54b 100644 --- a/src/main/scala/com/decodified/scalassh/Command.scala +++ b/src/main/scala/com/decodified/scalassh/Command.scala @@ -18,11 +18,25 @@ package com.decodified.scalassh import java.io.{ByteArrayInputStream, File, FileInputStream, InputStream} import net.schmizz.sshj.connection.channel.direct.Session - -final case class Command(command: String, input: CommandInput = CommandInput.NoInput, timeout: Option[Int] = None) +trait Command { + val command: String + val input: CommandInput = CommandInput.NoInput + val timeout: Option[Int] = None + val safe: Boolean +} +case class SafeCommand(command: String, + override val input: CommandInput = CommandInput.NoInput, + override val timeout: Option[Int] = None, + safe: Boolean = true) + extends Command +case class UnsafeCommand(command: String, + override val input: CommandInput = CommandInput.NoInput, + override val timeout: Option[Int] = None, + safe: Boolean = false) + extends Command object Command { - implicit def fromString(cmd: String): Command = Command(cmd) + def apply(cmd: String, safe: Boolean): Command = if (safe) SafeCommand(cmd) else UnsafeCommand(cmd) } final case class CommandInput(inputStream: Option[InputStream]) diff --git a/src/main/scala/com/decodified/scalassh/SSH.scala b/src/main/scala/com/decodified/scalassh/SSH.scala index 9622263..08e87dd 100644 --- a/src/main/scala/com/decodified/scalassh/SSH.scala +++ b/src/main/scala/com/decodified/scalassh/SSH.scala @@ -16,7 +16,7 @@ package com.decodified.scalassh -import scala.util.control.{NoStackTrace, NonFatal} +import scala.util.control.NonFatal import scala.util.{Failure, Success, Try} object SSH { @@ -37,5 +37,5 @@ object SSH { implicit def fromAny[T](value: T) = Result(Success(value)) } - final case class Error(msg: String, cause: Throwable = null) extends RuntimeException(msg, cause) with NoStackTrace + final case class Error(msg: String, cause: Throwable = null) extends RuntimeException(msg, cause) } diff --git a/src/main/scala/com/decodified/scalassh/SshClient.scala b/src/main/scala/com/decodified/scalassh/SshClient.scala index 25fb4f6..b816694 100644 --- a/src/main/scala/com/decodified/scalassh/SshClient.scala +++ b/src/main/scala/com/decodified/scalassh/SshClient.scala @@ -26,7 +26,6 @@ import net.schmizz.sshj.SSHClient import net.schmizz.sshj.connection.channel.direct.Session import net.schmizz.sshj.userauth.keyprovider.KeyProvider import net.schmizz.sshj.userauth.method.AuthMethod - import scala.io.Source import scala.util.{Failure, Success, Try} import scala.util.control.NonFatal @@ -53,9 +52,10 @@ final class SshClient(val config: HostConfig) extends ScpTransferable { execWithSession(command, session) } } + def logCmd(command: Command): String = if (command.safe) command.command else "Sensitive data, not logged" def execWithSession(command: Command, session: Session): Try[CommandResult] = { - log.info("Executing SSH command on {}: \"{}\"", Seq(endpoint, command.command): _*) + log.info("Executing SSH command on {}: \"{}\"", Seq(endpoint, logCmd(command)): _*) protect("Could not execute SSH command on") { val channel = session.exec(command.command) command.input.inputStream.foreach(new StreamCopier().copy(_, channel.getOutputStream)) @@ -155,7 +155,12 @@ final class SshClient(val config: HostConfig) extends ScpTransferable { protected def protect[T](errorMsg: => String)(f: => T): Try[T] = try Success(f) - catch { case NonFatal(e) => Failure(SSH.Error(errorMsg + " " + endpoint, e)) } + catch { + case NonFatal(e) => + Failure( + SSH + .Error(errorMsg + " " + endpoint + " message: " + e.getMessage + e.getStackTrace.toList.map(_.toString), e)) + } } object SshClient { diff --git a/version.sbt b/version.sbt index 972f262..6a9dc4e 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.9.1-SNAPSHOT" +version in ThisBuild := "0.9.3-SNAPSHOT" From 7d920e431c1753da84dc84cf7a53dccb051f6f04 Mon Sep 17 00:00:00 2001 From: Jeff Dyke Date: Tue, 6 Aug 2019 18:31:46 -0400 Subject: [PATCH 2/7] Update gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 62dfe10..603fbd4 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ project/boot project/build/target project/plugins/lib_managed project/plugins/src_managed -project/plugins/target \ No newline at end of file +project/plugins/target +.idea From 9de1370b5ef508336a162d0cb04157a74c733a6f Mon Sep 17 00:00:00 2001 From: Jeff Dyke Date: Thu, 8 Aug 2019 10:57:08 -0400 Subject: [PATCH 3/7] bring back no stack trace, default to SafeCommand --- src/main/scala/com/decodified/scalassh/Command.scala | 1 + src/main/scala/com/decodified/scalassh/SSH.scala | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/decodified/scalassh/Command.scala b/src/main/scala/com/decodified/scalassh/Command.scala index 881b54b..8f1f78d 100644 --- a/src/main/scala/com/decodified/scalassh/Command.scala +++ b/src/main/scala/com/decodified/scalassh/Command.scala @@ -37,6 +37,7 @@ case class UnsafeCommand(command: String, object Command { def apply(cmd: String, safe: Boolean): Command = if (safe) SafeCommand(cmd) else UnsafeCommand(cmd) + def apply(cmd: String): Command = SafeCommand(cmd) } final case class CommandInput(inputStream: Option[InputStream]) diff --git a/src/main/scala/com/decodified/scalassh/SSH.scala b/src/main/scala/com/decodified/scalassh/SSH.scala index 08e87dd..9622263 100644 --- a/src/main/scala/com/decodified/scalassh/SSH.scala +++ b/src/main/scala/com/decodified/scalassh/SSH.scala @@ -16,7 +16,7 @@ package com.decodified.scalassh -import scala.util.control.NonFatal +import scala.util.control.{NoStackTrace, NonFatal} import scala.util.{Failure, Success, Try} object SSH { @@ -37,5 +37,5 @@ object SSH { implicit def fromAny[T](value: T) = Result(Success(value)) } - final case class Error(msg: String, cause: Throwable = null) extends RuntimeException(msg, cause) + final case class Error(msg: String, cause: Throwable = null) extends RuntimeException(msg, cause) with NoStackTrace } From e3bb81281c03665a14b30e3b6a12e2e3efacbc6d Mon Sep 17 00:00:00 2001 From: Jeff Dyke Date: Thu, 8 Aug 2019 11:06:43 -0400 Subject: [PATCH 4/7] add message to Error --- src/main/scala/com/decodified/scalassh/SshClient.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/scala/com/decodified/scalassh/SshClient.scala b/src/main/scala/com/decodified/scalassh/SshClient.scala index b816694..7d3d101 100644 --- a/src/main/scala/com/decodified/scalassh/SshClient.scala +++ b/src/main/scala/com/decodified/scalassh/SshClient.scala @@ -158,8 +158,7 @@ final class SshClient(val config: HostConfig) extends ScpTransferable { catch { case NonFatal(e) => Failure( - SSH - .Error(errorMsg + " " + endpoint + " message: " + e.getMessage + e.getStackTrace.toList.map(_.toString), e)) + SSH.Error(s"$errorMsg executing on $endpoint - message: ${e.getMessage}", e)) } } From 917426c949d0ad4d7d5650ca1f0195b2c604342f Mon Sep 17 00:00:00 2001 From: Jeff Dyke Date: Thu, 8 Aug 2019 11:08:21 -0400 Subject: [PATCH 5/7] revert version --- version.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.sbt b/version.sbt index 6a9dc4e..972f262 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.9.3-SNAPSHOT" +version in ThisBuild := "0.9.1-SNAPSHOT" From 65481b765151903d050379999c852680b0966736 Mon Sep 17 00:00:00 2001 From: Jeff Dyke Date: Thu, 8 Aug 2019 11:10:25 -0400 Subject: [PATCH 6/7] add message to Error --- src/main/scala/com/decodified/scalassh/SshClient.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/scala/com/decodified/scalassh/SshClient.scala b/src/main/scala/com/decodified/scalassh/SshClient.scala index 7d3d101..fea833b 100644 --- a/src/main/scala/com/decodified/scalassh/SshClient.scala +++ b/src/main/scala/com/decodified/scalassh/SshClient.scala @@ -157,8 +157,7 @@ final class SshClient(val config: HostConfig) extends ScpTransferable { try Success(f) catch { case NonFatal(e) => - Failure( - SSH.Error(s"$errorMsg executing on $endpoint - message: ${e.getMessage}", e)) + Failure(SSH.Error(s"$errorMsg executing on $endpoint - message: ${e.getMessage}", e)) } } From 1e7374d00363c6cd73f680f0a53005ca1d0dea76 Mon Sep 17 00:00:00 2001 From: Jeff Dyke Date: Mon, 12 Aug 2019 12:39:38 -0400 Subject: [PATCH 7/7] remove the overly verbose implementation and just add a flag to command --- .gitignore | 1 - .../com/decodified/scalassh/Command.scala | 24 +++++-------------- .../com/decodified/scalassh/SshClient.scala | 7 +++--- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 603fbd4..168f44c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,3 @@ project/build/target project/plugins/lib_managed project/plugins/src_managed project/plugins/target -.idea diff --git a/src/main/scala/com/decodified/scalassh/Command.scala b/src/main/scala/com/decodified/scalassh/Command.scala index 8f1f78d..fc1a9b4 100644 --- a/src/main/scala/com/decodified/scalassh/Command.scala +++ b/src/main/scala/com/decodified/scalassh/Command.scala @@ -18,26 +18,14 @@ package com.decodified.scalassh import java.io.{ByteArrayInputStream, File, FileInputStream, InputStream} import net.schmizz.sshj.connection.channel.direct.Session -trait Command { - val command: String - val input: CommandInput = CommandInput.NoInput - val timeout: Option[Int] = None - val safe: Boolean -} -case class SafeCommand(command: String, - override val input: CommandInput = CommandInput.NoInput, - override val timeout: Option[Int] = None, - safe: Boolean = true) - extends Command -case class UnsafeCommand(command: String, - override val input: CommandInput = CommandInput.NoInput, - override val timeout: Option[Int] = None, - safe: Boolean = false) - extends Command +final case class Command(command: String, + input: CommandInput = CommandInput.NoInput, + timeout: Option[Int] = None, + safeToLog: Boolean = true) object Command { - def apply(cmd: String, safe: Boolean): Command = if (safe) SafeCommand(cmd) else UnsafeCommand(cmd) - def apply(cmd: String): Command = SafeCommand(cmd) + def apply(cmd: String, safeToLog: Boolean): Command = new Command(cmd, safeToLog = safeToLog) + def apply(cmd: String): Command = new Command(cmd, safeToLog = true) } final case class CommandInput(inputStream: Option[InputStream]) diff --git a/src/main/scala/com/decodified/scalassh/SshClient.scala b/src/main/scala/com/decodified/scalassh/SshClient.scala index fea833b..812de27 100644 --- a/src/main/scala/com/decodified/scalassh/SshClient.scala +++ b/src/main/scala/com/decodified/scalassh/SshClient.scala @@ -35,7 +35,7 @@ final class SshClient(val config: HostConfig) extends ScpTransferable { lazy val endpoint = config.hostName + ':' + config.port lazy val authenticatedClient = connect(client).flatMap(authenticate) val client = createClient(config) - + val unsafeLogMsg = "Sensitive data, not logged" def exec(command: Command): Try[CommandResult] = authenticatedClient.flatMap { client => startSession(client).flatMap { session => @@ -52,10 +52,11 @@ final class SshClient(val config: HostConfig) extends ScpTransferable { execWithSession(command, session) } } - def logCmd(command: Command): String = if (command.safe) command.command else "Sensitive data, not logged" def execWithSession(command: Command, session: Session): Try[CommandResult] = { - log.info("Executing SSH command on {}: \"{}\"", Seq(endpoint, logCmd(command)): _*) + log.info( + "Executing SSH command on {}: \"{}\"", + Seq(endpoint, if (command.safeToLog) command.command else unsafeLogMsg): _*) protect("Could not execute SSH command on") { val channel = session.exec(command.command) command.input.inputStream.foreach(new StreamCopier().copy(_, channel.getOutputStream))