mirror of
https://github.com/ysoftdevs/odc-analyzer.git
synced 2026-03-24 01:52:26 +01:00
Added new ODC scans for Java libraries. Those can scan even transitive dependencies and can be run before adding a new library to a project.
This commit is contained in:
114
app/controllers/LibraryAdvisor.scala
Normal file
114
app/controllers/LibraryAdvisor.scala
Normal file
@@ -0,0 +1,114 @@
|
||||
package controllers
|
||||
|
||||
import java.net.URL
|
||||
import javax.inject.Inject
|
||||
|
||||
import com.github.nscala_time.time.Imports._
|
||||
import com.ysoft.odc.SecureXml
|
||||
import modules.TemplateCustomization
|
||||
import org.joda.time.DateTime
|
||||
import play.api.Configuration
|
||||
import play.api.i18n.MessagesApi
|
||||
import play.api.mvc.{Action, AnyContent, Result}
|
||||
import play.twirl.api.Html
|
||||
import services.{DependencyNotFoundException, OdcDbService, OdcService, SingleLibraryScanResult}
|
||||
import views.html.DefaultRequest
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.Try
|
||||
|
||||
class LibraryAdvisor @Inject() (
|
||||
config: Configuration,
|
||||
odcServiceOption: Option[OdcService],
|
||||
odcDbService: OdcDbService,
|
||||
val messagesApi: MessagesApi,
|
||||
val env: AuthEnv,
|
||||
val templateCustomization: TemplateCustomization
|
||||
) (implicit ec: ExecutionContext) extends AuthenticatedController
|
||||
{
|
||||
|
||||
import secureRequestConversion._
|
||||
|
||||
private def withOdc(f: OdcService => Future[Result])(implicit defaultRequest: DefaultRequest) = {
|
||||
odcServiceOption.fold(Future.successful(InternalServerError(views.html.libraryAdvisor.notEnabled())))(odcService =>
|
||||
f(odcService)
|
||||
)
|
||||
}
|
||||
|
||||
private val InputParsers = Seq[(OdcService, String) => Option[Either[Future[SingleLibraryScanResult], String]]](
|
||||
(odcService, xmlString) => {
|
||||
val triedElem = Try {
|
||||
SecureXml.loadString(xmlString)
|
||||
}
|
||||
triedElem.toOption.map{ xml =>
|
||||
xml.label match {
|
||||
case "dependency" =>
|
||||
/*
|
||||
Maven POM, e.g.:
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.3.1</version>
|
||||
</dependency>
|
||||
*/
|
||||
val groupId = (xml \ "groupId").text
|
||||
val artifactId = (xml \ "artifactId").text
|
||||
val version = (xml \ "version").text
|
||||
Left(odcService.scanMaven(groupId, artifactId, version))
|
||||
case other =>
|
||||
Right(s"Unknown root XML element: $other")
|
||||
}
|
||||
}
|
||||
},
|
||||
(odcService, urlString) => {
|
||||
Try{new URL(urlString)}.toOption.map{url =>
|
||||
url.getHost match {
|
||||
case "www.mvnrepository.com" | "mvnrepository.com" =>
|
||||
// https://www.mvnrepository.com/artifact/ch.qos.logback/logback-classic/0.9.10
|
||||
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic/0.9.10
|
||||
url.getPath.split('/') match {
|
||||
case Array("", "artifact", groupId, artifactId, version) =>
|
||||
Left(odcService.scanMaven(groupId, artifactId, version))
|
||||
case _ =>
|
||||
Right("Unknown path for mvnrepository.com: Expected https://mvnrepository.com/artifact/<groupId>/<artifactId>/<version>")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def index(dependency: Option[String]): Action[AnyContent] = ReadAction.async{ implicit req =>
|
||||
withOdc{ odcService =>
|
||||
Future.successful(Ok(views.html.libraryAdvisor.scanLibrary(dependency, Seq(
|
||||
Html("<dependency>…</dependency> – Maven POM format"),
|
||||
Html("https://mvnrepository.com/artifact/<i>groupId</i>/<i>artifactId</i>/<i>version</i>")
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection TypeAnnotation
|
||||
def scan() = ReadAction.async(parse.json[String]){ implicit req =>
|
||||
withOdc{ odcService =>
|
||||
val now = DateTime.now()
|
||||
|
||||
val oldDataThreshold = 2.days
|
||||
val lastDbUpdateFuture = odcDbService.loadLastDbUpdate()
|
||||
val isOldFuture = lastDbUpdateFuture.map{ lastUpdate => now - oldDataThreshold > lastUpdate}
|
||||
|
||||
val response = InputParsers.toStream.map(_(odcService, req.body)).find(_.nonEmpty).flatten match{
|
||||
case None => Future.successful(Ok(views.html.libraryAdvisor.scanInputError("Unknown input format")))
|
||||
case Some(Right(message)) => Future.successful(Ok(views.html.libraryAdvisor.scanInputError(s"Unknown input format: $message")))
|
||||
case Some(Left(resFuture)) =>
|
||||
for{
|
||||
res <- resFuture
|
||||
isOld <- isOldFuture
|
||||
} yield Ok(views.html.libraryAdvisor.scanResults(isOld, res))
|
||||
}
|
||||
response.recover{
|
||||
case DependencyNotFoundException(dependency) =>
|
||||
NotFound(views.html.libraryAdvisor.notFound(dependency))
|
||||
}.map { _.withHeaders("Content-type" -> "text/plain; charset=utf-8")}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user