Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a suppressServer setting for high-security builds #3922

Merged
merged 3 commits into from
Feb 8, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions main-command/src/main/scala/sbt/BasicKeys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ object BasicKeys {
"The wire protocol for the server command.",
10000)

val suppressServer =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid double negatives, I would say we should keep the same name autoStartServer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! I've reworked it from false-defaulting suppressServer to true-defaulting autoStartServer.

AttributeKey[Boolean](
"suppressServer",
"Running the server will be suppressed if 'suppressServer is explicitly set to true.",
10000)

// Unlike other BasicKeys, this is not used directly as a setting key,
// and severLog / logLevel is used instead.
private[sbt] val serverLogLevel =
Expand Down
1 change: 1 addition & 0 deletions main/src/main/scala/sbt/Defaults.scala
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ object Defaults extends BuildCommon {
.getOrElse(GCUtil.defaultForceGarbageCollection),
minForcegcInterval :== GCUtil.defaultMinForcegcInterval,
interactionService :== CommandLineUIService,
suppressServer := false,
serverHost := "127.0.0.1",
serverPort := 5000 + (Hash
.toHex(Hash(appConfiguration.value.baseDirectory.toString))
Expand Down
1 change: 1 addition & 0 deletions main/src/main/scala/sbt/Keys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ object Keys {
// Command keys
val historyPath = SettingKey(BasicKeys.historyPath)
val shellPrompt = SettingKey(BasicKeys.shellPrompt)
val suppressServer = SettingKey(BasicKeys.suppressServer)
val serverPort = SettingKey(BasicKeys.serverPort)
val serverHost = SettingKey(BasicKeys.serverHost)
val serverAuthentication = SettingKey(BasicKeys.serverAuthentication)
Expand Down
3 changes: 3 additions & 0 deletions main/src/main/scala/sbt/Project.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Keys.{
sessionSettings,
shellPrompt,
templateResolverInfos,
suppressServer,
serverHost,
serverLog,
serverPort,
Expand Down Expand Up @@ -462,6 +463,7 @@ object Project extends ProjectExtra {
val prompt = get(shellPrompt)
val trs = (templateResolverInfos in Global get structure.data).toList.flatten
val watched = get(watch)
val suppressSvr: Option[Boolean] = get(suppressServer)
val host: Option[String] = get(serverHost)
val port: Option[Int] = get(serverPort)
val authentication: Option[Set[ServerAuthentication]] = get(serverAuthentication)
Expand All @@ -474,6 +476,7 @@ object Project extends ProjectExtra {
s.attributes
.setCond(Watched.Configuration, watched)
.put(historyPath.key, history)
.setCond(suppressServer.key, suppressSvr)
.setCond(serverPort.key, port)
.setCond(serverHost.key, host)
.setCond(serverAuthentication.key, authentication)
Expand Down
7 changes: 6 additions & 1 deletion main/src/main/scala/sbt/internal/CommandExchange.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import java.util.concurrent.atomic._
import scala.collection.mutable.ListBuffer
import scala.annotation.tailrec
import BasicKeys.{
suppressServer,
serverHost,
serverPort,
serverAuthentication,
Expand Down Expand Up @@ -87,7 +88,11 @@ private[sbt] final class CommandExchange {
consoleChannel = Some(x)
subscribe(x)
}
if (autoStartServer) runServer(s)
val suppress = (s get suppressServer) match {
case Some(bool) => bool
case None => false
}
if (autoStartServer && !suppress) runServer(s)
else s
}

Expand Down
23 changes: 23 additions & 0 deletions notes/1.1.1/suppressServer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
### Improvements

This pull request implements a Boolean setting called `suppressServer`, whose default value is `false'.

If a build or plugin explicitly sets it to `true`, the sbt-1.x server will not start up
(exactly as if `sbt.server.autostart` were set to start).

Users may manually override this setting by running the `startServer` command at the interactive prompt.

### Motivation

Projects often encounter private information, such as deployment credentials, private keys, etc.
For such projects, it may be preferable to reduce the potential attack surface than to enjoy the
interoperability offered by sbt's server. Projects that wish to make this tradeoff can set `suppressServer`
to `true` in their build. Security-sensitive plugins can define this setting as well, modifying the
default behavior in favor of security.

(My own motivation is that I am working on a [plugin for developing Ethereum applications](https://github.com/swaldman/sbt-ethereum)
with scala and sbt. It must work with extremely sensitive private keys.)

---

See also a [recent conversation on Stack Exchange](https://stackoverflow.com/questions/48591179/can-one-disable-the-sbt-1-x-server/48593906#48593906).