From 337716830f7f34c2bc828788505c6833df8a6bc0 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 28 Aug 2019 15:07:35 -0400 Subject: [PATCH] Deprecate HTTP resolvers (take 2) Ref https://github.com/sbt/sbt/issues/4905 Ref https://github.com/sbt/librarymanagement/pull/312 Based on the discussion in https://github.com/sbt/sbt/issues/4905, this will exempt localhost, and also allows per-resolver opt-in for HTTP. --- build.sbt | 3 + .../sbt/librarymanagement/Artifact.scala | 22 ++++-- .../librarymanagement/ArtifactFormats.scala | 4 +- .../librarymanagement/ChainedResolver.scala | 2 +- .../sbt/librarymanagement/MavenCache.scala | 3 +- .../sbt/librarymanagement/MavenRepo.scala | 23 ++++-- .../librarymanagement/MavenRepoFormats.scala | 4 +- .../librarymanagement/MavenRepository.scala | 6 ++ .../sbt/librarymanagement/Resolver.scala | 3 +- .../sbt/librarymanagement/URLRepository.scala | 21 +++-- .../URLRepositoryFormats.scala | 4 +- .../main/contraband/librarymanagement.json | 43 +++++++++-- .../sbt/librarymanagement/ArtifactExtra.scala | 8 +- .../DependencyBuilders.scala | 3 +- .../sbt/librarymanagement/ModuleIDExtra.scala | 52 ++++++++----- .../sbt/librarymanagement/ResolverExtra.scala | 77 +++++++++++++------ 16 files changed, 194 insertions(+), 84 deletions(-) diff --git a/build.sbt b/build.sbt index 2ed58e87..25e1d9ea 100644 --- a/build.sbt +++ b/build.sbt @@ -238,6 +238,9 @@ lazy val lmCore = (project in file("core")) exclude[DirectMissingMethodProblem]( "sbt.librarymanagement.ResolverFunctions.useSecureResolvers" ), + exclude[ReversedMissingMethodProblem]( + "sbt.librarymanagement.MavenRepository.allowInsecureProtocol" + ) ), ) .configure(addSbtIO, addSbtUtilLogging, addSbtUtilPosition, addSbtUtilCache) diff --git a/core/src/main/contraband-scala/sbt/librarymanagement/Artifact.scala b/core/src/main/contraband-scala/sbt/librarymanagement/Artifact.scala index d337cb36..9cae77dc 100644 --- a/core/src/main/contraband-scala/sbt/librarymanagement/Artifact.scala +++ b/core/src/main/contraband-scala/sbt/librarymanagement/Artifact.scala @@ -12,22 +12,24 @@ final class Artifact private ( val configurations: Vector[sbt.librarymanagement.ConfigRef], val url: Option[java.net.URL], val extraAttributes: Map[String, String], - val checksum: Option[sbt.librarymanagement.Checksum]) extends sbt.librarymanagement.ArtifactExtra with Serializable { - - private def this(name: String) = this(name, Artifact.DefaultType, Artifact.DefaultExtension, None, Vector.empty, None, Map.empty, None) + val checksum: Option[sbt.librarymanagement.Checksum], + val allowInsecureProtocol: Boolean) extends sbt.librarymanagement.ArtifactExtra with Serializable { + private[sbt] def validateProtocol(logger: sbt.util.Logger): Unit = Resolver.validateArtifact(this, logger) + private def this(name: String) = this(name, Artifact.DefaultType, Artifact.DefaultExtension, None, Vector.empty, None, Map.empty, None, false) + private def this(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Vector[sbt.librarymanagement.ConfigRef], url: Option[java.net.URL], extraAttributes: Map[String, String], checksum: Option[sbt.librarymanagement.Checksum]) = this(name, `type`, extension, classifier, configurations, url, extraAttributes, checksum, false) override def equals(o: Any): Boolean = o match { - case x: Artifact => (this.name == x.name) && (this.`type` == x.`type`) && (this.extension == x.extension) && (this.classifier == x.classifier) && (this.configurations == x.configurations) && (this.url == x.url) && (this.extraAttributes == x.extraAttributes) && (this.checksum == x.checksum) + case x: Artifact => (this.name == x.name) && (this.`type` == x.`type`) && (this.extension == x.extension) && (this.classifier == x.classifier) && (this.configurations == x.configurations) && (this.url == x.url) && (this.extraAttributes == x.extraAttributes) && (this.checksum == x.checksum) && (this.allowInsecureProtocol == x.allowInsecureProtocol) case _ => false } override def hashCode: Int = { - 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.librarymanagement.Artifact".##) + name.##) + `type`.##) + extension.##) + classifier.##) + configurations.##) + url.##) + extraAttributes.##) + checksum.##) + 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.librarymanagement.Artifact".##) + name.##) + `type`.##) + extension.##) + classifier.##) + configurations.##) + url.##) + extraAttributes.##) + checksum.##) + allowInsecureProtocol.##) } override def toString: String = { - "Artifact(" + name + ", " + `type` + ", " + extension + ", " + classifier + ", " + configurations + ", " + url + ", " + extraAttributes + ", " + checksum + ")" + "Artifact(" + name + ", " + `type` + ", " + extension + ", " + classifier + ", " + configurations + ", " + url + ", " + extraAttributes + ", " + checksum + ", " + allowInsecureProtocol + ")" } - private[this] def copy(name: String = name, `type`: String = `type`, extension: String = extension, classifier: Option[String] = classifier, configurations: Vector[sbt.librarymanagement.ConfigRef] = configurations, url: Option[java.net.URL] = url, extraAttributes: Map[String, String] = extraAttributes, checksum: Option[sbt.librarymanagement.Checksum] = checksum): Artifact = { - new Artifact(name, `type`, extension, classifier, configurations, url, extraAttributes, checksum) + private[this] def copy(name: String = name, `type`: String = `type`, extension: String = extension, classifier: Option[String] = classifier, configurations: Vector[sbt.librarymanagement.ConfigRef] = configurations, url: Option[java.net.URL] = url, extraAttributes: Map[String, String] = extraAttributes, checksum: Option[sbt.librarymanagement.Checksum] = checksum, allowInsecureProtocol: Boolean = allowInsecureProtocol): Artifact = { + new Artifact(name, `type`, extension, classifier, configurations, url, extraAttributes, checksum, allowInsecureProtocol) } def withName(name: String): Artifact = { copy(name = name) @@ -53,9 +55,13 @@ final class Artifact private ( def withChecksum(checksum: Option[sbt.librarymanagement.Checksum]): Artifact = { copy(checksum = checksum) } + def withAllowInsecureProtocol(allowInsecureProtocol: Boolean): Artifact = { + copy(allowInsecureProtocol = allowInsecureProtocol) + } } object Artifact extends sbt.librarymanagement.ArtifactFunctions { def apply(name: String): Artifact = new Artifact(name) def apply(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Vector[sbt.librarymanagement.ConfigRef], url: Option[java.net.URL], extraAttributes: Map[String, String], checksum: Option[sbt.librarymanagement.Checksum]): Artifact = new Artifact(name, `type`, extension, classifier, configurations, url, extraAttributes, checksum) + def apply(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Vector[sbt.librarymanagement.ConfigRef], url: Option[java.net.URL], extraAttributes: Map[String, String], checksum: Option[sbt.librarymanagement.Checksum], allowInsecureProtocol: Boolean): Artifact = new Artifact(name, `type`, extension, classifier, configurations, url, extraAttributes, checksum, allowInsecureProtocol) } diff --git a/core/src/main/contraband-scala/sbt/librarymanagement/ArtifactFormats.scala b/core/src/main/contraband-scala/sbt/librarymanagement/ArtifactFormats.scala index 4dfc42b5..2c9b2a02 100644 --- a/core/src/main/contraband-scala/sbt/librarymanagement/ArtifactFormats.scala +++ b/core/src/main/contraband-scala/sbt/librarymanagement/ArtifactFormats.scala @@ -19,8 +19,9 @@ implicit lazy val ArtifactFormat: JsonFormat[sbt.librarymanagement.Artifact] = n val url = unbuilder.readField[Option[java.net.URL]]("url") val extraAttributes = unbuilder.readField[Map[String, String]]("extraAttributes") val checksum = unbuilder.readField[Option[sbt.librarymanagement.Checksum]]("checksum") + val allowInsecureProtocol = unbuilder.readField[Boolean]("allowInsecureProtocol") unbuilder.endObject() - sbt.librarymanagement.Artifact(name, `type`, extension, classifier, configurations, url, extraAttributes, checksum) + sbt.librarymanagement.Artifact(name, `type`, extension, classifier, configurations, url, extraAttributes, checksum, allowInsecureProtocol) case None => deserializationError("Expected JsObject but found None") } @@ -35,6 +36,7 @@ implicit lazy val ArtifactFormat: JsonFormat[sbt.librarymanagement.Artifact] = n builder.addField("url", obj.url) builder.addField("extraAttributes", obj.extraAttributes) builder.addField("checksum", obj.checksum) + builder.addField("allowInsecureProtocol", obj.allowInsecureProtocol) builder.endObject() } } diff --git a/core/src/main/contraband-scala/sbt/librarymanagement/ChainedResolver.scala b/core/src/main/contraband-scala/sbt/librarymanagement/ChainedResolver.scala index d98e6d1c..fa950e86 100644 --- a/core/src/main/contraband-scala/sbt/librarymanagement/ChainedResolver.scala +++ b/core/src/main/contraband-scala/sbt/librarymanagement/ChainedResolver.scala @@ -7,7 +7,7 @@ package sbt.librarymanagement final class ChainedResolver private ( name: String, val resolvers: Vector[sbt.librarymanagement.Resolver]) extends sbt.librarymanagement.Resolver(name) with Serializable { - + private[sbt] override def validateProtocol(logger: sbt.util.Logger): Unit = resolvers.foreach(_.validateProtocol(logger)) override def equals(o: Any): Boolean = o match { diff --git a/core/src/main/contraband-scala/sbt/librarymanagement/MavenCache.scala b/core/src/main/contraband-scala/sbt/librarymanagement/MavenCache.scala index b1501e3d..1ed824c7 100644 --- a/core/src/main/contraband-scala/sbt/librarymanagement/MavenCache.scala +++ b/core/src/main/contraband-scala/sbt/librarymanagement/MavenCache.scala @@ -14,7 +14,8 @@ final class MavenCache private ( localIfFile: Boolean, val rootFile: java.io.File) extends sbt.librarymanagement.MavenRepository(name, root, localIfFile) with Serializable { def this(name: String, rootFile: java.io.File) = this(name, rootFile.toURI.toURL.toString, true, rootFile) - def isCache: Boolean = true + override def isCache: Boolean = true + override def allowInsecureProtocol: Boolean = false private def this(name: String, root: String, rootFile: java.io.File) = this(name, root, true, rootFile) override def equals(o: Any): Boolean = o match { diff --git a/core/src/main/contraband-scala/sbt/librarymanagement/MavenRepo.scala b/core/src/main/contraband-scala/sbt/librarymanagement/MavenRepo.scala index b01c6d8a..a23e1a22 100644 --- a/core/src/main/contraband-scala/sbt/librarymanagement/MavenRepo.scala +++ b/core/src/main/contraband-scala/sbt/librarymanagement/MavenRepo.scala @@ -4,25 +4,30 @@ // DO NOT EDIT MANUALLY package sbt.librarymanagement +/** This is the internal implementation of actual Maven Repository (as opposed to a file cache). */ final class MavenRepo private ( name: String, root: String, - localIfFile: Boolean) extends sbt.librarymanagement.MavenRepository(name, root, localIfFile) with Serializable { - def isCache: Boolean = false - private def this(name: String, root: String) = this(name, root, true) + localIfFile: Boolean, + val _allowInsecureProtocol: Boolean) extends sbt.librarymanagement.MavenRepository(name, root, localIfFile) with Serializable { + override def isCache: Boolean = false + override def allowInsecureProtocol: Boolean = _allowInsecureProtocol + private[sbt] override def validateProtocol(logger: sbt.util.Logger): Unit = Resolver.validateMavenRepo(this, logger) + private def this(name: String, root: String) = this(name, root, true, false) + private def this(name: String, root: String, localIfFile: Boolean) = this(name, root, localIfFile, false) override def equals(o: Any): Boolean = o match { - case x: MavenRepo => (this.name == x.name) && (this.root == x.root) && (this.localIfFile == x.localIfFile) + case x: MavenRepo => (this.name == x.name) && (this.root == x.root) && (this.localIfFile == x.localIfFile) && (this._allowInsecureProtocol == x._allowInsecureProtocol) case _ => false } override def hashCode: Int = { - 37 * (37 * (37 * (37 * (17 + "sbt.librarymanagement.MavenRepo".##) + name.##) + root.##) + localIfFile.##) + 37 * (37 * (37 * (37 * (37 * (17 + "sbt.librarymanagement.MavenRepo".##) + name.##) + root.##) + localIfFile.##) + _allowInsecureProtocol.##) } override def toString: String = { s"$name: $root" } - private[this] def copy(name: String = name, root: String = root, localIfFile: Boolean = localIfFile): MavenRepo = { - new MavenRepo(name, root, localIfFile) + private[this] def copy(name: String = name, root: String = root, localIfFile: Boolean = localIfFile, _allowInsecureProtocol: Boolean = _allowInsecureProtocol): MavenRepo = { + new MavenRepo(name, root, localIfFile, _allowInsecureProtocol) } def withName(name: String): MavenRepo = { copy(name = name) @@ -33,9 +38,13 @@ final class MavenRepo private ( def withLocalIfFile(localIfFile: Boolean): MavenRepo = { copy(localIfFile = localIfFile) } + def with_allowInsecureProtocol(_allowInsecureProtocol: Boolean): MavenRepo = { + copy(_allowInsecureProtocol = _allowInsecureProtocol) + } } object MavenRepo { def apply(name: String, root: String): MavenRepo = new MavenRepo(name, root) def apply(name: String, root: String, localIfFile: Boolean): MavenRepo = new MavenRepo(name, root, localIfFile) + def apply(name: String, root: String, localIfFile: Boolean, _allowInsecureProtocol: Boolean): MavenRepo = new MavenRepo(name, root, localIfFile, _allowInsecureProtocol) } diff --git a/core/src/main/contraband-scala/sbt/librarymanagement/MavenRepoFormats.scala b/core/src/main/contraband-scala/sbt/librarymanagement/MavenRepoFormats.scala index 235de964..e4c97998 100644 --- a/core/src/main/contraband-scala/sbt/librarymanagement/MavenRepoFormats.scala +++ b/core/src/main/contraband-scala/sbt/librarymanagement/MavenRepoFormats.scala @@ -14,8 +14,9 @@ implicit lazy val MavenRepoFormat: JsonFormat[sbt.librarymanagement.MavenRepo] = val name = unbuilder.readField[String]("name") val root = unbuilder.readField[String]("root") val localIfFile = unbuilder.readField[Boolean]("localIfFile") + val _allowInsecureProtocol = unbuilder.readField[Boolean]("_allowInsecureProtocol") unbuilder.endObject() - sbt.librarymanagement.MavenRepo(name, root, localIfFile) + sbt.librarymanagement.MavenRepo(name, root, localIfFile, _allowInsecureProtocol) case None => deserializationError("Expected JsObject but found None") } @@ -25,6 +26,7 @@ implicit lazy val MavenRepoFormat: JsonFormat[sbt.librarymanagement.MavenRepo] = builder.addField("name", obj.name) builder.addField("root", obj.root) builder.addField("localIfFile", obj.localIfFile) + builder.addField("_allowInsecureProtocol", obj._allowInsecureProtocol) builder.endObject() } } diff --git a/core/src/main/contraband-scala/sbt/librarymanagement/MavenRepository.scala b/core/src/main/contraband-scala/sbt/librarymanagement/MavenRepository.scala index f1bbb608..3f891ef1 100644 --- a/core/src/main/contraband-scala/sbt/librarymanagement/MavenRepository.scala +++ b/core/src/main/contraband-scala/sbt/librarymanagement/MavenRepository.scala @@ -10,6 +10,12 @@ abstract class MavenRepository( val root: String, val localIfFile: Boolean) extends sbt.librarymanagement.Resolver(name) with Serializable { def isCache: Boolean + def allowInsecureProtocol: Boolean + def withAllowInsecureProtocol(allowInsecureProtocol: Boolean): MavenRepository = + this match { + case x: MavenRepo => x.with_allowInsecureProtocol(allowInsecureProtocol) + case x: MavenCache => x + } def this(name: String, root: String) = this(name, root, true) diff --git a/core/src/main/contraband-scala/sbt/librarymanagement/Resolver.scala b/core/src/main/contraband-scala/sbt/librarymanagement/Resolver.scala index c6caf7ea..448b0433 100644 --- a/core/src/main/contraband-scala/sbt/librarymanagement/Resolver.scala +++ b/core/src/main/contraband-scala/sbt/librarymanagement/Resolver.scala @@ -6,7 +6,8 @@ package sbt.librarymanagement abstract class Resolver( val name: String) extends Serializable { - + /** check for HTTP */ + private[sbt] def validateProtocol(logger: sbt.util.Logger): Unit = () diff --git a/core/src/main/contraband-scala/sbt/librarymanagement/URLRepository.scala b/core/src/main/contraband-scala/sbt/librarymanagement/URLRepository.scala index fc39dda7..9f0efa52 100644 --- a/core/src/main/contraband-scala/sbt/librarymanagement/URLRepository.scala +++ b/core/src/main/contraband-scala/sbt/librarymanagement/URLRepository.scala @@ -6,22 +6,23 @@ package sbt.librarymanagement final class URLRepository private ( name: String, - patterns: sbt.librarymanagement.Patterns) extends sbt.librarymanagement.PatternsBasedRepository(name, patterns) with Serializable { - Resolver.validatePatterns(patterns) - + patterns: sbt.librarymanagement.Patterns, + val allowInsecureProtocol: Boolean) extends sbt.librarymanagement.PatternsBasedRepository(name, patterns) with Serializable { + private[sbt] override def validateProtocol(logger: sbt.util.Logger): Unit = Resolver.validateURLRepository(this, logger) + private def this(name: String, patterns: sbt.librarymanagement.Patterns) = this(name, patterns, false) override def equals(o: Any): Boolean = o match { - case x: URLRepository => (this.name == x.name) && (this.patterns == x.patterns) + case x: URLRepository => (this.name == x.name) && (this.patterns == x.patterns) && (this.allowInsecureProtocol == x.allowInsecureProtocol) case _ => false } override def hashCode: Int = { - 37 * (37 * (37 * (17 + "sbt.librarymanagement.URLRepository".##) + name.##) + patterns.##) + 37 * (37 * (37 * (37 * (17 + "sbt.librarymanagement.URLRepository".##) + name.##) + patterns.##) + allowInsecureProtocol.##) } override def toString: String = { - "URLRepository(" + name + ", " + patterns + ")" + "URLRepository(" + name + ", " + patterns + ", " + allowInsecureProtocol + ")" } - private[this] def copy(name: String = name, patterns: sbt.librarymanagement.Patterns = patterns): URLRepository = { - new URLRepository(name, patterns) + private[this] def copy(name: String = name, patterns: sbt.librarymanagement.Patterns = patterns, allowInsecureProtocol: Boolean = allowInsecureProtocol): URLRepository = { + new URLRepository(name, patterns, allowInsecureProtocol) } def withName(name: String): URLRepository = { copy(name = name) @@ -29,8 +30,12 @@ final class URLRepository private ( def withPatterns(patterns: sbt.librarymanagement.Patterns): URLRepository = { copy(patterns = patterns) } + def withAllowInsecureProtocol(allowInsecureProtocol: Boolean): URLRepository = { + copy(allowInsecureProtocol = allowInsecureProtocol) + } } object URLRepository { def apply(name: String, patterns: sbt.librarymanagement.Patterns): URLRepository = new URLRepository(name, patterns) + def apply(name: String, patterns: sbt.librarymanagement.Patterns, allowInsecureProtocol: Boolean): URLRepository = new URLRepository(name, patterns, allowInsecureProtocol) } diff --git a/core/src/main/contraband-scala/sbt/librarymanagement/URLRepositoryFormats.scala b/core/src/main/contraband-scala/sbt/librarymanagement/URLRepositoryFormats.scala index dbf9e9cf..4ab1b23c 100644 --- a/core/src/main/contraband-scala/sbt/librarymanagement/URLRepositoryFormats.scala +++ b/core/src/main/contraband-scala/sbt/librarymanagement/URLRepositoryFormats.scala @@ -13,8 +13,9 @@ implicit lazy val URLRepositoryFormat: JsonFormat[sbt.librarymanagement.URLRepos unbuilder.beginObject(js) val name = unbuilder.readField[String]("name") val patterns = unbuilder.readField[sbt.librarymanagement.Patterns]("patterns") + val allowInsecureProtocol = unbuilder.readField[Boolean]("allowInsecureProtocol") unbuilder.endObject() - sbt.librarymanagement.URLRepository(name, patterns) + sbt.librarymanagement.URLRepository(name, patterns, allowInsecureProtocol) case None => deserializationError("Expected JsObject but found None") } @@ -23,6 +24,7 @@ implicit lazy val URLRepositoryFormat: JsonFormat[sbt.librarymanagement.URLRepos builder.beginObject() builder.addField("name", obj.name) builder.addField("patterns", obj.patterns) + builder.addField("allowInsecureProtocol", obj.allowInsecureProtocol) builder.endObject() } } diff --git a/core/src/main/contraband/librarymanagement.json b/core/src/main/contraband/librarymanagement.json index 2fc2c6fb..67d65547 100644 --- a/core/src/main/contraband/librarymanagement.json +++ b/core/src/main/contraband/librarymanagement.json @@ -143,7 +143,11 @@ { "name": "configurations", "type": "sbt.librarymanagement.ConfigRef*", "default": "Vector.empty", "since": "0.0.1" }, { "name": "url", "type": "Option[java.net.URL]", "default": "None", "since": "0.0.1" }, { "name": "extraAttributes", "type": "Map[String, String]", "default": "Map.empty", "since": "0.0.1" }, - { "name": "checksum", "type": "Option[sbt.librarymanagement.Checksum]", "default": "None", "since": "0.0.1" } + { "name": "checksum", "type": "Option[sbt.librarymanagement.Checksum]", "default": "None", "since": "0.0.1" }, + { "name": "allowInsecureProtocol", "type": "Boolean", "default": "false", "since": "1.3.0" } + ], + "extra": [ + "private[sbt] def validateProtocol(logger: sbt.util.Logger): Unit = Resolver.validateArtifact(this, logger)" ], "parentsCompanion": "sbt.librarymanagement.ArtifactFunctions" }, @@ -557,6 +561,10 @@ "fields": [ { "name": "name", "type": "String" } ], + "extra": [ + "/** check for HTTP */", + "private[sbt] def validateProtocol(logger: sbt.util.Logger): Unit = ()" + ], "types": [ { "name": "ChainedResolver", @@ -565,6 +573,9 @@ "type": "record", "fields": [ { "name": "resolvers", "type": "sbt.librarymanagement.Resolver*" } + ], + "extra": [ + "private[sbt] override def validateProtocol(logger: sbt.util.Logger): Unit = resolvers.foreach(_.validateProtocol(logger))" ] }, { @@ -583,7 +594,17 @@ "namespace": "sbt.librarymanagement", "target": "Scala", "type": "record", - "extra": "def isCache: Boolean = false", + "doc": [ + "This is the internal implementation of actual Maven Repository (as opposed to a file cache)." + ], + "fields": [ + { "name": "_allowInsecureProtocol", "type": "Boolean", "default": "false", "since": "1.3.0" } + ], + "extra": [ + "override def isCache: Boolean = false", + "override def allowInsecureProtocol: Boolean = _allowInsecureProtocol", + "private[sbt] override def validateProtocol(logger: sbt.util.Logger): Unit = Resolver.validateMavenRepo(this, logger)" + ], "toString": "s\"$name: $root\"" }, { @@ -600,13 +621,22 @@ ], "extra": [ "def this(name: String, rootFile: java.io.File) = this(name, rootFile.toURI.toURL.toString, true, rootFile)", - "def isCache: Boolean = true" + "override def isCache: Boolean = true", + "override def allowInsecureProtocol: Boolean = false" ], "toString": "s\"cache:$name: ${rootFile.getAbsolutePath}\"", "extraCompanion": "def apply(name: String, rootFile: java.io.File): MavenCache = new MavenCache(name, rootFile)" } ], - "extra": "def isCache: Boolean", + "extra": [ + "def isCache: Boolean", + "def allowInsecureProtocol: Boolean", + "def withAllowInsecureProtocol(allowInsecureProtocol: Boolean): MavenRepository =", + " this match {", + " case x: MavenRepo => x.with_allowInsecureProtocol(allowInsecureProtocol)", + " case x: MavenCache => x", + " }" + ], "parentsCompanion": "sbt.librarymanagement.MavenRepositoryFunctions" }, { @@ -642,8 +672,11 @@ "namespace": "sbt.librarymanagement", "target": "Scala", "type": "record", + "fields": [ + { "name": "allowInsecureProtocol", "type": "boolean", "default": "false", "since": "1.3.0" } + ], "extra": [ - "Resolver.validatePatterns(patterns)" + "private[sbt] override def validateProtocol(logger: sbt.util.Logger): Unit = Resolver.validateURLRepository(this, logger)" ] }, { diff --git a/core/src/main/scala/sbt/librarymanagement/ArtifactExtra.scala b/core/src/main/scala/sbt/librarymanagement/ArtifactExtra.scala index f90b9e29..63a992d2 100644 --- a/core/src/main/scala/sbt/librarymanagement/ArtifactExtra.scala +++ b/core/src/main/scala/sbt/librarymanagement/ArtifactExtra.scala @@ -26,14 +26,18 @@ private[librarymanagement] abstract class ArtifactFunctions { Artifact(name, `type`, extension, None, Vector.empty, None) def apply(name: String, `type`: String, extension: String, classifier: String): Artifact = Artifact(name, `type`, extension, Some(classifier), Vector.empty, None) - def apply(name: String, url: URL): Artifact = + def apply(name: String, url: URL): Artifact = Artifact(name, url, false) + def apply(name: String, url: URL, allowInsecureProtocol: Boolean): Artifact = Artifact( name, extract(url, DefaultType), extract(url, DefaultExtension), None, Vector.empty, - Some(url) + Some(url), + Map.empty, + None, + allowInsecureProtocol ) private final val empty = Map.empty[String, String] diff --git a/core/src/main/scala/sbt/librarymanagement/DependencyBuilders.scala b/core/src/main/scala/sbt/librarymanagement/DependencyBuilders.scala index a9c9d86f..99e64106 100755 --- a/core/src/main/scala/sbt/librarymanagement/DependencyBuilders.scala +++ b/core/src/main/scala/sbt/librarymanagement/DependencyBuilders.scala @@ -67,9 +67,8 @@ object DependencyBuilders { } final class RepositoryName private[sbt] (name: String) { - def at(location: String) = { + def at(location: String): MavenRepository = { nonEmpty(location, "Repository location") - Resolver.validateUrlString(location) MavenRepository(name, location) } } diff --git a/core/src/main/scala/sbt/librarymanagement/ModuleIDExtra.scala b/core/src/main/scala/sbt/librarymanagement/ModuleIDExtra.scala index d8c6ca6f..40db9def 100644 --- a/core/src/main/scala/sbt/librarymanagement/ModuleIDExtra.scala +++ b/core/src/main/scala/sbt/librarymanagement/ModuleIDExtra.scala @@ -8,6 +8,7 @@ import java.net.URL import sbt.internal.librarymanagement.mavenint.SbtPomExtraProperties import scala.collection.mutable.ListBuffer import sbt.librarymanagement.syntax._ +import sbt.util.Logger private[librarymanagement] abstract class ModuleIDExtra { def organization: String @@ -75,10 +76,10 @@ private[librarymanagement] abstract class ModuleIDExtra { // () required for chaining /** Do not follow dependencies of this module. Synonym for `intransitive`.*/ - def notTransitive() = intransitive() + def notTransitive(): ModuleID = intransitive() /** Do not follow dependencies of this module. Synonym for `notTransitive`.*/ - def intransitive() = withIsTransitive(false) + def intransitive(): ModuleID = withIsTransitive(false) /** * Marks this dependency as "changing". Ivy will always check if the metadata has changed and then if the artifact has changed, @@ -86,49 +87,58 @@ private[librarymanagement] abstract class ModuleIDExtra { * * See the "Changes in artifacts" section of https://ant.apache.org/ivy/history/trunk/concept.html for full details. */ - def changing() = withIsChanging(true) + def changing(): ModuleID = withIsChanging(true) /** * Indicates that conflict resolution should only select this module's revision. * This prevents a newer revision from being pulled in by a transitive dependency, for example. */ - def force() = withIsForce(true) + def force(): ModuleID = withIsForce(true) + + private[sbt] def validateProtocol(logger: Logger): Unit = { + explicitArtifacts foreach { _.validateProtocol(logger) } + } /** * Specifies a URL from which the main artifact for this dependency can be downloaded. * This value is only consulted if the module is not found in a repository. * It is not included in published metadata. */ - def from(url: String) = { - Resolver.validateUrlString(url) - artifacts(Artifact(name, new URL(url))) - } + def from(url: String): ModuleID = from(url, false) + + /** + * Specifies a URL from which the main artifact for this dependency can be downloaded. + * This value is only consulted if the module is not found in a repository. + * It is not included in published metadata. + */ + def from(url: String, allowInsecureProtocol: Boolean): ModuleID = + artifacts(Artifact(name, new URL(url), allowInsecureProtocol)) /** Adds a dependency on the artifact for this module with classifier `c`. */ - def classifier(c: String) = artifacts(Artifact(name, c)) + def classifier(c: String): ModuleID = artifacts(Artifact(name, c)) /** * Declares the explicit artifacts for this module. If this ModuleID represents a dependency, * these artifact definitions override the information in the dependency's published metadata. */ - def artifacts(newArtifacts: Artifact*) = + def artifacts(newArtifacts: Artifact*): ModuleID = withExplicitArtifacts(newArtifacts.toVector ++ explicitArtifacts) /** * Applies the provided exclusions to dependencies of this module. Note that only exclusions that specify * both the exact organization and name and nothing else will be included in a pom.xml. */ - def excludeAll(rules: ExclusionRule*) = withExclusions(exclusions ++ rules) + def excludeAll(rules: ExclusionRule*): ModuleID = withExclusions(exclusions ++ rules) /** Excludes the dependency with organization `org` and `name` from being introduced by this dependency during resolution. */ - def exclude(org: String, name: String) = + def exclude(org: String, name: String): ModuleID = excludeAll(ExclusionRule().withOrganization(org).withName(name)) /** * Adds extra attributes for this module. All keys are prefixed with `e:` if they are not already so prefixed. * This information will only be published in an ivy.xml and not in a pom.xml. */ - def extra(attributes: (String, String)*) = + def extra(attributes: (String, String)*): ModuleID = withExtraAttributes(extraAttributes ++ ModuleID.checkE(attributes)) /** @@ -136,30 +146,30 @@ private[librarymanagement] abstract class ModuleIDExtra { * for performance and correctness. This method adds a dependency on this module's artifact with the "sources" * classifier. If you want to also depend on the main artifact, be sure to also call `jar()` or use `withSources()` instead. */ - def sources() = artifacts(Artifact.sources(name)) + def sources(): ModuleID = artifacts(Artifact.sources(name)) /** * Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred * for performance and correctness. This method adds a dependency on this module's artifact with the "javadoc" * classifier. If you want to also depend on the main artifact, be sure to also call `jar()` or use `withJavadoc()` instead. */ - def javadoc() = artifacts(Artifact.javadoc(name)) + def javadoc(): ModuleID = artifacts(Artifact.javadoc(name)) - def pomOnly() = artifacts(Artifact.pom(name)) + def pomOnly(): ModuleID = artifacts(Artifact.pom(name)) /** * Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred * for performance and correctness. This method adds a dependency on this module's artifact with the "sources" * classifier. If there is not already an explicit dependency on the main artifact, this adds one. */ - def withSources() = jarIfEmpty.sources() + def withSources(): ModuleID = jarIfEmpty.sources() /** * Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred * for performance and correctness. This method adds a dependency on this module's artifact with the "javadoc" * classifier. If there is not already an explicit dependency on the main artifact, this adds one. */ - def withJavadoc() = jarIfEmpty.javadoc() + def withJavadoc(): ModuleID = jarIfEmpty.javadoc() private def jarIfEmpty = if (explicitArtifacts.isEmpty) jar() else this @@ -167,14 +177,14 @@ private[librarymanagement] abstract class ModuleIDExtra { * Declares a dependency on the main artifact. This is implied by default unless artifacts are explicitly declared, such * as when adding a dependency on an artifact with a classifier. */ - def jar() = artifacts(Artifact(name)) + def jar(): ModuleID = artifacts(Artifact(name)) /** * Sets the Ivy branch of this module. */ - def branch(branchName: String) = withBranchName(Some(branchName)) + def branch(branchName: String): ModuleID = withBranchName(Some(branchName)) - def branch(branchName: Option[String]) = withBranchName(branchName) + def branch(branchName: Option[String]): ModuleID = withBranchName(branchName) } private[librarymanagement] abstract class ModuleIDFunctions { diff --git a/core/src/main/scala/sbt/librarymanagement/ResolverExtra.scala b/core/src/main/scala/sbt/librarymanagement/ResolverExtra.scala index d4516699..33944a26 100644 --- a/core/src/main/scala/sbt/librarymanagement/ResolverExtra.scala +++ b/core/src/main/scala/sbt/librarymanagement/ResolverExtra.scala @@ -7,7 +7,7 @@ import java.io.{ IOException, File } import java.net.URL import scala.xml.XML import org.xml.sax.SAXParseException -import sbt.util.{ Level, LogExchange } +import sbt.util.Logger final class RawRepository(val resolver: AnyRef, name: String) extends Resolver(name) { override def toString = "Raw(" + resolver.toString + ")" @@ -404,33 +404,60 @@ private[librarymanagement] abstract class ResolverFunctions { Patterns().withIvyPatterns(pList).withArtifactPatterns(pList).withIsMavenCompatible(false) } - lazy val log = { - val log0 = LogExchange.logger("sbt.librarymanagement.ResolverExtra") - LogExchange.bindLoggerAppenders( - "sbt.librarymanagement.ResolverExtra", - List(LogExchange.buildAsyncStdout -> Level.Info) - ) - log0 + private[sbt] def warnHttp(value: String, suggestion: String, logger: Logger): Unit = { + logger.warn(s"insecure HTTP request is deprecated '$value'; switch to HTTPS$suggestion") } - private[sbt] def warnHttp(value: String): Unit = { - log.warn(s"insecure HTTP request is deprecated '$value'; switch to HTTPS") + private[sbt] def isInsecureUrl(str: String): Boolean = { + // don't try to parse str as URL because it could contain $variable from Ivy pattern + str.startsWith("http:") && + !(str.startsWith("http://localhost/") + || str.startsWith("http://localhost:") + || str.startsWith("http://127.0.0.1/") + || str.startsWith("http://127.0.0.1:")) } - private[sbt] def validatePatterns(patterns: Patterns): Unit = { - val ivy = patterns.ivyPatterns.headOption match { - case Some(x) => x.startsWith("http:") - case _ => false - } - val art = patterns.artifactPatterns.headOption match { - case Some(x) => x.startsWith("http:") - case _ => false - } - if (ivy || art) { - warnHttp(patterns.toString) + private[sbt] def validateURLRepository(repo: URLRepository, logger: Logger): Unit = { + if (repo.allowInsecureProtocol) () + else { + val patterns = repo.patterns + val ivy = patterns.ivyPatterns.headOption match { + case Some(x) => isInsecureUrl(x) + case _ => false + } + val art = patterns.artifactPatterns.headOption match { + case Some(x) => isInsecureUrl(x) + case _ => false + } + if (ivy || art) { + warnHttp( + patterns.toString, + s""" or opt-in as Resolver.url("${repo.name}", url(...)).withAllowInsecureProtocol(true)""", + logger + ) + } } } - private[sbt] def validateUrlString(url: String): Unit = { - if (url.startsWith("http:")) { - warnHttp(url) + + private[sbt] def validateMavenRepo(repo: MavenRepo, logger: Logger): Unit = + if (repo.allowInsecureProtocol) () + else if (isInsecureUrl(repo.root)) { + warnHttp( + repo.root, + s""" or opt-in as ("${repo.name}" at "${repo.root}").withAllowInsecureProtocol(true)""", + logger + ) + } + + private[sbt] def validateArtifact(art: Artifact, logger: Logger): Unit = + if (art.allowInsecureProtocol) () + else { + art.url foreach { url => + if (isInsecureUrl(url.toString)) { + warnHttp( + art.toString, + " or opt-in using from(url(...), allowInsecureProtocol = true) on ModuleID or .withAllowInsecureProtocol(true) on Artifact", + logger + ) + } + } } - } }