mirror of
https://github.com/ysoftdevs/odc-analyzer.git
synced 2026-03-18 07:14:26 +01:00
Added notice when there are some failed projects. This required some refactorings and fixes, that are also included in this commit.
This commit is contained in:
@@ -2,22 +2,27 @@ package com.ysoft.odc.statistics
|
|||||||
|
|
||||||
import controllers.ReportInfo
|
import controllers.ReportInfo
|
||||||
|
|
||||||
final class FailedProjects(val failedProjectsSet: Set[String]){
|
final class FailedProjects(val failedProjectsSet: Set[ReportInfo]){
|
||||||
|
|
||||||
|
val failedProjectIdsSet = failedProjectsSet.map(_.projectId)
|
||||||
|
|
||||||
|
def nonEmpty: Boolean = failedProjectsSet.nonEmpty
|
||||||
|
|
||||||
def isFailed(projectFullId: String): Boolean = {
|
def isFailed(projectFullId: String): Boolean = {
|
||||||
val projectBareId = projectFullId.takeWhile(_ != '/')
|
val projectBareId = projectFullId.takeWhile(_ != '/')
|
||||||
failedProjectsSet contains projectBareId
|
failedProjectIdsSet contains projectBareId
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object FailedProjects {
|
object FailedProjects {
|
||||||
def combineFails(failedReportDownloads: Map[String, Throwable], parsingFailures: Map[ReportInfo, Throwable]): FailedProjects = {
|
def combineFails(failedReportDownloads: Map[ReportInfo, Throwable], parsingFailures: Map[ReportInfo, Throwable]): FailedProjects = {
|
||||||
/*
|
/*
|
||||||
Fail can happen at multiple places:
|
Fail can happen at multiple places:
|
||||||
1. Build cannot be downloaded (auth error, connection error, …) or is failed (failedReportDownloads)
|
1. Build cannot be downloaded (auth error, connection error, …) or is failed (failedReportDownloads)
|
||||||
2. Build is successful and can be downloaded, but it cannot be parsed (parsingFailures)
|
2. Build is successful and can be downloaded, but it cannot be parsed (parsingFailures)
|
||||||
*/
|
*/
|
||||||
val failedProjectsSet = failedReportDownloads.keySet ++ parsingFailures.keySet.map(_.projectId)
|
val failedProjectsSet = failedReportDownloads.keySet ++ parsingFailures.keySet
|
||||||
new FailedProjects(failedProjectsSet)
|
new FailedProjects(failedProjectsSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,9 @@ case class LibDepStatistics(libraries: Set[(Int, Library)], dependencies: Set[Gr
|
|||||||
|
|
||||||
object LibDepStatistics{
|
object LibDepStatistics{
|
||||||
private def computeWeaknessesFrequency(vulnerabilities: Set[Vulnerability]) = vulnerabilities.toSeq.map(_.cweOption).groupBy(identity).mapValues(_.size).map(identity).withDefaultValue(0)
|
private def computeWeaknessesFrequency(vulnerabilities: Set[Vulnerability]) = vulnerabilities.toSeq.map(_.cweOption).groupBy(identity).mapValues(_.size).map(identity).withDefaultValue(0)
|
||||||
def apply(libraries: Set[(Int, Library)], dependencies: Set[GroupedDependency], failedReportDownloads: Map[String, Throwable], parsedReports: Result): LibDepStatistics = LibDepStatistics(
|
def apply(libraries: Set[(Int, Library)], dependencies: Set[GroupedDependency], parsedReports: Result): LibDepStatistics = LibDepStatistics(
|
||||||
libraries = libraries,
|
libraries = libraries,
|
||||||
dependencies = dependencies,
|
dependencies = dependencies,
|
||||||
failedProjects = FailedProjects.combineFails(
|
failedProjects = parsedReports.failedProjects
|
||||||
failedReportDownloads = failedReportDownloads,
|
|
||||||
parsingFailures = parsedReports.failedAnalysises
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,17 +26,19 @@ private final case class ProjectFilter(project: ReportInfo) extends Filter{
|
|||||||
override def descriptionText: String = s"project ${friendlyProjectNameString(project)}"
|
override def descriptionText: String = s"project ${friendlyProjectNameString(project)}"
|
||||||
override def subReports(r: Result): Option[Result] = {
|
override def subReports(r: Result): Option[Result] = {
|
||||||
@inline def reportInfo = project
|
@inline def reportInfo = project
|
||||||
def f[T](m: Map[ReportInfo, T]): Map[String, T] = (
|
def filter[T](m: Map[ReportInfo, T]): Map[ReportInfo, T] = (
|
||||||
if(reportInfo.subprojectNameOption.isEmpty) m.filter(_._1.projectId == project.projectId) else m.get(reportInfo).fold(Map.empty[ReportInfo, T])(x => Map(reportInfo -> x))
|
if(reportInfo.subprojectNameOption.isEmpty) m.filter(_._1.projectId == project.projectId)
|
||||||
).map{case (k, v) => k.fullId -> v}
|
else m.get(reportInfo).fold(Map.empty[ReportInfo, T])(x => Map(reportInfo -> x))
|
||||||
val newFlatReports = f(r.flatReports)
|
)
|
||||||
val newFailedAnalysises = f(r.failedAnalysises)
|
val newFlatReports = filter(r.flatReports)
|
||||||
if(newFlatReports.isEmpty && newFailedAnalysises.isEmpty) None
|
val newFailedAnalysises = filter(r.failedAnalysises)
|
||||||
|
val newFailedReportDownloads = filter(r.failedReportDownloads)
|
||||||
|
if(newFlatReports.isEmpty && newFailedAnalysises.isEmpty && newFailedReportDownloads.isEmpty) None
|
||||||
else Some(Result(
|
else Some(Result(
|
||||||
bareFlatReports = newFlatReports,
|
bareFlatReports = newFlatReports,
|
||||||
bareFailedAnalysises = newFailedAnalysises,
|
bareFailedAnalysises = newFailedAnalysises,
|
||||||
projects = r.projects,
|
projectsReportInfo = r.projectsReportInfo,
|
||||||
failedReportDownloads = r.failedReportDownloads // TODO: consider filtering of failedReportDownloads
|
failedReportDownloads = newFailedReportDownloads
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
override def selector = Some(s"project:${project.fullId}")
|
override def selector = Some(s"project:${project.fullId}")
|
||||||
@@ -45,27 +47,33 @@ private final case class TeamFilter(team: Team) extends Filter{
|
|||||||
override def filters: Boolean = true
|
override def filters: Boolean = true
|
||||||
override def subReports(r: Result): Option[Result] = {
|
override def subReports(r: Result): Option[Result] = {
|
||||||
val Wildcard = """^(.*): \*$""".r
|
val Wildcard = """^(.*): \*$""".r
|
||||||
val reportInfoByFriendlyProjectNameMap = r.projectsReportInfo.ungroupedReportsInfo.map(ri => friendlyProjectNameString(ri) -> ri).toSeq.groupBy(_._1).mapValues{
|
@inline def toMapStrict[K, V](l: Traversable[(K, V)]) = l.toSeq.groupBy(_._1).mapValues{ // without toSeq, the pattern matching might fail
|
||||||
case Seq((_, ri)) => ri
|
case Seq((_, v)) => v
|
||||||
case other => sys.error("some duplicate value: "+other)
|
case other => sys.error("some duplicate value: "+other)
|
||||||
}.map(identity)
|
}.map(identity)
|
||||||
|
val reportInfoByFriendlyProjectNameMap = toMapStrict(r.projectsReportInfo.ungroupedReportsInfo.map(ri => friendlyProjectNameString(ri) -> ri))
|
||||||
val ProjectName = """^(.*): (.*)$""".r
|
val ProjectName = """^(.*): (.*)$""".r
|
||||||
val failedProjectsFriendlyNames = r.failedProjects.failedProjectsSet.map(r.projectsReportInfo.parseUnfriendlyName).map(_.projectName)
|
val failedProjectsFriendlyNames = r.failedProjects.failedProjectsSet.map(_.projectName)
|
||||||
println(failedProjectsFriendlyNames)
|
Logger.error("failedProjectsFriendlyNames: "+failedProjectsFriendlyNames)
|
||||||
val rootProjectReports = reportInfoByFriendlyProjectNameMap.collect{ case (ProjectName(rootProject, subproject), v) =>
|
val rootProjectReports = reportInfoByFriendlyProjectNameMap.map{
|
||||||
(rootProject, v)
|
case (ProjectName(rootProject, _subproject), v) => (rootProject, v)
|
||||||
|
case value @ (rootProject, v) => value
|
||||||
}.groupBy(_._1).mapValues(_.values).withDefault(name =>
|
}.groupBy(_._1).mapValues(_.values).withDefault(name =>
|
||||||
if(failedProjectsFriendlyNames contains name) Seq()
|
if(failedProjectsFriendlyNames contains name) Seq()
|
||||||
else sys.error("Unknown project: "+name)
|
else sys.error("Unknown project: "+name)
|
||||||
)
|
)
|
||||||
def reportInfoByFriendlyProjectName(fpn: String) = reportInfoByFriendlyProjectNameMap.get(fpn).map(Set(_)).getOrElse(rootProjectReports(fpn.takeWhile(_ != ':')))
|
def reportInfoByFriendlyProjectName(fpn: String) = fpn match{
|
||||||
|
case Wildcard(rfpn) => rootProjectReports(rfpn)
|
||||||
|
case _ => Set(reportInfoByFriendlyProjectNameMap(fpn))
|
||||||
|
}
|
||||||
val reportInfos = team.projectNames.flatMap(reportInfoByFriendlyProjectName)
|
val reportInfos = team.projectNames.flatMap(reportInfoByFriendlyProjectName)
|
||||||
def submap[T](m: Map[String, T]) = reportInfos.toSeq.flatMap(ri => m.get(ri.fullId).map(ri.fullId -> _) ).toMap
|
def submap[T](m: Map[ReportInfo, T]) = reportInfos.toSeq.flatMap(ri => m.get(ri).map(ri -> _) ).toMap
|
||||||
|
def submapBare[T](m: Map[ReportInfo, T]): Map[ReportInfo, T] = reportInfos.toSeq.flatMap(ri => m.get(ri.bare.ensuring{x => println(x.fullId); true}).map(ri -> _) ).toMap
|
||||||
Some(Result(
|
Some(Result(
|
||||||
bareFlatReports = submap(r.bareFlatReports),
|
bareFlatReports = submap(r.bareFlatReports),
|
||||||
bareFailedAnalysises = submap(r.bareFailedAnalysises),
|
bareFailedAnalysises = submapBare(r.bareFailedAnalysises),
|
||||||
projects = r.projects,
|
projectsReportInfo = r.projectsReportInfo,
|
||||||
failedReportDownloads = r.failedReportDownloads // TODO: consider filtering of failedReportDownloads
|
failedReportDownloads = submapBare(r.failedReportDownloads)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
override def descriptionHtml: Html = views.html.filters.team(team.id)
|
override def descriptionHtml: Html = views.html.filters.team(team.id)
|
||||||
@@ -89,10 +97,11 @@ private final case class BadFilter(pattern: String) extends Filter{
|
|||||||
|
|
||||||
object DependencyCheckReportsParser{
|
object DependencyCheckReportsParser{
|
||||||
final case class ResultWithSelection(result: Result, projectsWithSelection: ProjectsWithSelection)
|
final case class ResultWithSelection(result: Result, projectsWithSelection: ProjectsWithSelection)
|
||||||
final case class Result(bareFlatReports: Map[String, Analysis], bareFailedAnalysises: Map[String, Throwable], projects: Projects /*TODO: maybe rename to rootProjects*/, failedReportDownloads: Map[String, Throwable]){
|
final case class Result(bareFlatReports: Map[ReportInfo, Analysis], bareFailedAnalysises: Map[ReportInfo, Throwable], projectsReportInfo: ProjectsWithReports/*TODO: maybe rename to rootProjects*/, failedReportDownloads: Map[ReportInfo, Throwable]){
|
||||||
lazy val projectsReportInfo = new ProjectsWithReports(projects, bareFlatReports.keySet ++ bareFailedAnalysises.keySet ++ failedReportDownloads.keySet) // TODO: consider renaming to projectsWithReports
|
//lazy val projectsReportInfo = new ProjectsWithReports(projects, (bareFlatReports.keySet ++ bareFailedAnalysises.keySet ++ failedReportDownloads.keySet).map(_.fullId)) // TODO: consider renaming to projectsWithReports
|
||||||
lazy val flatReports: Map[ReportInfo, Analysis] = bareFlatReports.map{case (k, v) => projectsReportInfo.reportIdToReportInfo(k) -> v}
|
@inline def flatReports: Map[ReportInfo, Analysis] = bareFlatReports // TODO: unify
|
||||||
lazy val failedAnalysises: Map[ReportInfo, Throwable] = bareFailedAnalysises.map{case (k, v) => projectsReportInfo.reportIdToReportInfo(k) -> v}
|
@inline def projects = projectsReportInfo.projects
|
||||||
|
@inline def failedAnalysises: Map[ReportInfo, Throwable] = bareFailedAnalysises // TODO: unify
|
||||||
lazy val failedProjects = FailedProjects.combineFails(parsingFailures = failedAnalysises, failedReportDownloads = failedReportDownloads)
|
lazy val failedProjects = FailedProjects.combineFails(parsingFailures = failedAnalysises, failedReportDownloads = failedReportDownloads)
|
||||||
lazy val allDependencies = flatReports.toSeq.flatMap(r => r._2.dependencies.map(_ -> r._1))
|
lazy val allDependencies = flatReports.toSeq.flatMap(r => r._2.dependencies.map(_ -> r._1))
|
||||||
lazy val groupedDependencies = allDependencies.groupBy(_._1.hashes).values.map(GroupedDependency(_)).toSeq
|
lazy val groupedDependencies = allDependencies.groupBy(_._1.hashes).values.map(GroupedDependency(_)).toSeq
|
||||||
@@ -124,7 +133,7 @@ object DependencyCheckReportsParser{
|
|||||||
|
|
||||||
final class DependencyCheckReportsParser @Inject() (cache: CacheApi, projects: Projects) {
|
final class DependencyCheckReportsParser @Inject() (cache: CacheApi, projects: Projects) {
|
||||||
|
|
||||||
def parseReports(successfulResults: Map[String, (Build, ArtifactItem, ArtifactFile)], failedReportDownloads: Map[String, Throwable]) = {
|
def parseReports(successfulResults: Map[String, (Build, ArtifactItem, ArtifactFile)], failedReportDownloads: Map[String, Throwable]): Result = {
|
||||||
val rid = math.random.toString // for logging
|
val rid = math.random.toString // for logging
|
||||||
@volatile var parseFailedForSomeAnalysis = false
|
@volatile var parseFailedForSomeAnalysis = false
|
||||||
val deepReportsTriesIterable: Iterable[Map[String, Try[Analysis]]] = for((k, (build, data, log)) <- successfulResults) yield {
|
val deepReportsTriesIterable: Iterable[Map[String, Try[Analysis]]] = for((k, (build, data, log)) <- successfulResults) yield {
|
||||||
@@ -155,7 +164,14 @@ final class DependencyCheckReportsParser @Inject() (cache: CacheApi, projects: P
|
|||||||
val failedAnalysises = deepReportsAndFailuresIterable.map(_._2).toSeq.flatten.toMap
|
val failedAnalysises = deepReportsAndFailuresIterable.map(_._2).toSeq.flatten.toMap
|
||||||
val flatReports = deepSuccessfulReports.flatten.toMap
|
val flatReports = deepSuccessfulReports.flatten.toMap
|
||||||
Logger.debug(s"[$rid] parse finished")
|
Logger.debug(s"[$rid] parse finished")
|
||||||
Result(flatReports, failedAnalysises, projects, failedReportDownloads = failedReportDownloads)
|
val projectReportInfo = new ProjectsWithReports(projects, flatReports.keySet++failedAnalysises.keySet++failedReportDownloads.keySet)
|
||||||
|
def convertKeys[T](m: Map[String, T]) = m.map{case (k, v) => projectReportInfo.reportIdToReportInfo(k) -> v}
|
||||||
|
Result(
|
||||||
|
convertKeys(flatReports),
|
||||||
|
convertKeys(failedAnalysises),
|
||||||
|
projectReportInfo,
|
||||||
|
failedReportDownloads = convertKeys(failedReportDownloads)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class Notifications @Inject()(
|
|||||||
myWatches <- myWatchesFuture
|
myWatches <- myWatchesFuture
|
||||||
} yield {
|
} yield {
|
||||||
val projects = dependencyCheckReportsParser.parseReports(successfulReports, failedReports).projectsReportInfo.sortedReportsInfo
|
val projects = dependencyCheckReportsParser.parseReports(successfulReports, failedReports).projectsReportInfo.sortedReportsInfo
|
||||||
Ok(views.html.notifications.index(projects, myWatches))
|
Ok(views.html.notifications.index(projects, myWatches, failedReports.keySet))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ class Notifications @Inject()(
|
|||||||
(successfulReports, failedReports) <- resultsFuture
|
(successfulReports, failedReports) <- resultsFuture
|
||||||
libraries <- librariesService.all
|
libraries <- librariesService.all
|
||||||
parsedReports = dependencyCheckReportsParser.parseReports(successfulReports, failedReports)
|
parsedReports = dependencyCheckReportsParser.parseReports(successfulReports, failedReports)
|
||||||
lds = LibDepStatistics(dependencies = parsedReports.groupedDependencies.toSet, libraries = libraries.toSet, failedReportDownloads = failedReports, parsedReports = parsedReports)
|
lds = LibDepStatistics(dependencies = parsedReports.groupedDependencies.toSet, libraries = libraries.toSet, parsedReports = parsedReports)
|
||||||
failed = lds.failedProjects
|
failed = lds.failedProjects
|
||||||
failedReportsExportFuture = Fut(()) // TODO: exportFailedReports(lds, failed)
|
failedReportsExportFuture = Fut(()) // TODO: exportFailedReports(lds, failed)
|
||||||
issuesExportResultFuture = exportToIssueTracker(lds, failed, parsedReports.projectsReportInfo)
|
issuesExportResultFuture = exportToIssueTracker(lds, failed, parsedReports.projectsReportInfo)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
final case class ReportInfo(
|
final case class ReportInfo private[controllers] (
|
||||||
projectId: String,
|
projectId: String,
|
||||||
projectName: String,
|
projectName: String,
|
||||||
fullId: String,
|
fullId: String,
|
||||||
@@ -22,7 +22,12 @@ final case class ReportInfo(
|
|||||||
|
|
||||||
override def hashCode(): Int = 517+fullId.hashCode
|
override def hashCode(): Int = 517+fullId.hashCode
|
||||||
|
|
||||||
def bare = copy(subprojectNameOption = None, fullId = fullId.takeWhile(_ != '/'))
|
def bare = ReportInfo(
|
||||||
|
fullId = fullId.takeWhile(_ != '/'),
|
||||||
|
projectId = projectId,
|
||||||
|
projectName = projectName,
|
||||||
|
subprojectNameOption = None
|
||||||
|
)
|
||||||
|
|
||||||
def isBare = subprojectNameOption.isEmpty
|
def isBare = subprojectNameOption.isEmpty
|
||||||
def isNotBare = !isBare
|
def isNotBare = !isBare
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class Statistics @Inject() (
|
|||||||
tagStatistics = tagStatistics,
|
tagStatistics = tagStatistics,
|
||||||
projectsWithSelection = selection.projectsWithSelection,
|
projectsWithSelection = selection.projectsWithSelection,
|
||||||
parsedReports = parsedReports,
|
parsedReports = parsedReports,
|
||||||
lds = LibDepStatistics(libraries.toSet, parsedReports.groupedDependencies.toSet, selection.result.failedReportDownloads, parsedReports)
|
lds = LibDepStatistics(libraries.toSet, parsedReports.groupedDependencies.toSet, parsedReports)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,6 @@ class Statistics @Inject() (
|
|||||||
stats = LibDepStatistics(
|
stats = LibDepStatistics(
|
||||||
libraries = tagLibraries,
|
libraries = tagLibraries,
|
||||||
dependencies = tagDependencies,
|
dependencies = tagDependencies,
|
||||||
failedReportDownloads = parsedReports.failedReportDownloads,
|
|
||||||
parsedReports = parsedReports
|
parsedReports = parsedReports
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
@@ -130,12 +129,11 @@ class Statistics @Inject() (
|
|||||||
statistics <- tagOption.fold(Future.successful(LibDepStatistics(
|
statistics <- tagOption.fold(Future.successful(LibDepStatistics(
|
||||||
dependencies = parsedReports.groupedDependencies.toSet,
|
dependencies = parsedReports.groupedDependencies.toSet,
|
||||||
libraries = libraries.values.toSet,
|
libraries = libraries.values.toSet,
|
||||||
failedReportDownloads = selection.result.failedReportDownloads,
|
|
||||||
parsedReports = parsedReports
|
parsedReports = parsedReports
|
||||||
))){ tag =>
|
))){ tag =>
|
||||||
statisticsForTags(parsedReports, Future.successful(Seq(tag))).map{
|
statisticsForTags(parsedReports, Future.successful(Seq(tag))).map{
|
||||||
case Seq(TagStatistics(_, stats)) => stats // statisticsForTags is designed for multiple tags, but we have just one…
|
case Seq(TagStatistics(_, stats)) => stats // statisticsForTags is designed for multiple tags, but we have just one…
|
||||||
case Seq() => LibDepStatistics(libraries = Set(), dependencies = Set(), failedReportDownloads = selection.result.failedReportDownloads, parsedReports) // We don't want to crash when no dependencies are there…
|
case Seq() => LibDepStatistics(libraries = Set(), dependencies = Set(), parsedReports = parsedReports) // We don't want to crash when no dependencies are there…
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} yield Ok(views.html.statistics.vulnerabilities(
|
} yield Ok(views.html.statistics.vulnerabilities(
|
||||||
@@ -162,7 +160,8 @@ class Statistics @Inject() (
|
|||||||
vulnOption <- odcService.getVulnerabilityDetails(name)
|
vulnOption <- odcService.getVulnerabilityDetails(name)
|
||||||
} yield Ok(views.html.statistics.vulnerabilityNotFound( // TODO: the not found page might be replaced by some page explaining that there is no project affected by that vulnerability
|
} yield Ok(views.html.statistics.vulnerabilityNotFound( // TODO: the not found page might be replaced by some page explaining that there is no project affected by that vulnerability
|
||||||
name = name,
|
name = name,
|
||||||
projectsWithSelection = selection.projectsWithSelection
|
projectsWithSelection = selection.projectsWithSelection,
|
||||||
|
failedProjects = selection.result.failedProjects
|
||||||
))
|
))
|
||||||
}{ vulnerableDependencies =>
|
}{ vulnerableDependencies =>
|
||||||
for {
|
for {
|
||||||
@@ -173,6 +172,7 @@ class Statistics @Inject() (
|
|||||||
sys.error("The vulnerability is not in the database, you seem to have outdated the local vulnerability database") // TODO: consider fallback or more friendly error message
|
sys.error("The vulnerability is not in the database, you seem to have outdated the local vulnerability database") // TODO: consider fallback or more friendly error message
|
||||||
}{vuln => Ok(views.html.statistics.vulnerability(
|
}{vuln => Ok(views.html.statistics.vulnerability(
|
||||||
vulnerability = vuln,
|
vulnerability = vuln,
|
||||||
|
failedProjects = selection.result.failedProjects,
|
||||||
affectedProjects = vulnerableDependencies.flatMap(dep => dep.projects.map(proj => (proj, dep))).groupBy(_._1).mapValues(_.map(_._2)),
|
affectedProjects = vulnerableDependencies.flatMap(dep => dep.projects.map(proj => (proj, dep))).groupBy(_._1).mapValues(_.map(_._2)),
|
||||||
vulnerableDependencies = vulnerableDependencies,
|
vulnerableDependencies = vulnerableDependencies,
|
||||||
affectedLibraries = plainLibs,
|
affectedLibraries = plainLibs,
|
||||||
@@ -196,7 +196,8 @@ class Statistics @Inject() (
|
|||||||
Future.successful(Ok(views.html.statistics.vulnerableLibraries(
|
Future.successful(Ok(views.html.statistics.vulnerableLibraries(
|
||||||
projectsWithSelection = selection.projectsWithSelection,
|
projectsWithSelection = selection.projectsWithSelection,
|
||||||
vulnerableDependencies = reports.vulnerableDependencies,
|
vulnerableDependencies = reports.vulnerableDependencies,
|
||||||
allDependenciesCount = reports.groupedDependencies.size
|
allDependenciesCount = reports.groupedDependencies.size,
|
||||||
|
reports = reports
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +209,8 @@ class Statistics @Inject() (
|
|||||||
select(allResults, selectorOption).fold(Future.successful(notFound())){ selection =>
|
select(allResults, selectorOption).fold(Future.successful(notFound())){ selection =>
|
||||||
Future.successful(Ok(views.html.statistics.allLibraries(
|
Future.successful(Ok(views.html.statistics.allLibraries(
|
||||||
projectsWithSelection = selection.projectsWithSelection,
|
projectsWithSelection = selection.projectsWithSelection,
|
||||||
allDependencies = selection.result.groupedDependencies
|
allDependencies = selection.result.groupedDependencies,
|
||||||
|
failedProjects = selection.result.failedProjects
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
app/views/healthReport.scala.html
Normal file
12
app/views/healthReport.scala.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
@import com.ysoft.odc.statistics.FailedProjects
|
||||||
|
@(failedProjects: FailedProjects)
|
||||||
|
@if(failedProjects.nonEmpty){
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
Some projects are excluded from the report, because there is some failure for them:
|
||||||
|
<ul>
|
||||||
|
@for(proj <- failedProjects.failedProjectsSet.toIndexedSeq.sortBy(_.projectName.toLowerCase)){
|
||||||
|
<li>@proj.projectName</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
@(projects: Seq[ReportInfo], watchedProjects: Set[String])(implicit req: DefaultRequest)
|
|
||||||
@import helper._
|
@import helper._
|
||||||
|
@(projects: Seq[ReportInfo], watchedProjects: Set[String], failedReports: Set[String])(implicit req: DefaultRequest)
|
||||||
@button(action: Call)(label: String) = {
|
@button(action: Call)(label: String) = {
|
||||||
@form(action, 'style -> "display: inline-block"){
|
@form(action, 'style -> "display: inline-block"){
|
||||||
@CSRF.formField
|
@CSRF.formField
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
@for(
|
@for(
|
||||||
isWatchedDirectly <- Some(watchedProjects contains project.fullId); // hack allowing one to define a variable
|
isWatchedDirectly <- Some(watchedProjects contains project.fullId); // hack allowing one to define a variable
|
||||||
isWatchedByParent = project.isNotBare && (watchedProjects contains project.bare.fullId);
|
isWatchedByParent = project.isNotBare && (watchedProjects contains project.bare.fullId);
|
||||||
|
isFailed = failedReports contains project.fullId; // actually fullId should be equal bare id in such cases
|
||||||
watchedChildCount = subprojects.count(p => watchedProjects contains p.fullId);
|
watchedChildCount = subprojects.count(p => watchedProjects contains p.fullId);
|
||||||
hasWatchedChild = watchedChildCount > 0;
|
hasWatchedChild = watchedChildCount > 0;
|
||||||
hasButtons = !subprojects.isEmpty;
|
hasButtons = !subprojects.isEmpty;
|
||||||
@@ -31,6 +32,9 @@
|
|||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
@friendlyProjectName(project)
|
@friendlyProjectName(project)
|
||||||
|
@if(isFailed){
|
||||||
|
(!)
|
||||||
|
}
|
||||||
@if(project.isBare){
|
@if(project.isBare){
|
||||||
@if(isWatchedDirectly){
|
@if(isWatchedDirectly){
|
||||||
<span class="badge">You watch this project with all subprojects.</span>
|
<span class="badge">You watch this project with all subprojects.</span>
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
|
@import com.ysoft.odc.statistics.FailedProjects
|
||||||
@(
|
@(
|
||||||
projectsWithSelection: ProjectsWithSelection,
|
projectsWithSelection: ProjectsWithSelection,
|
||||||
allDependencies: Seq[GroupedDependency]
|
allDependencies: Seq[GroupedDependency],
|
||||||
|
failedProjects: FailedProjects
|
||||||
)(implicit header: DefaultRequest)
|
)(implicit header: DefaultRequest)
|
||||||
|
|
||||||
@main(
|
@main(
|
||||||
title = s"All libraries for ${projectsWithSelection.projectNameText}",
|
title = s"All libraries for ${projectsWithSelection.projectNameText}",
|
||||||
projectsOption = Some((projectsWithSelection, routes.Statistics.allLibraries(_)))
|
projectsOption = Some((projectsWithSelection, routes.Statistics.allLibraries(_)))
|
||||||
){
|
){
|
||||||
|
@healthReport(failedProjects)
|
||||||
@dependencyList(
|
@dependencyList(
|
||||||
"all",
|
"all",
|
||||||
allDependencies.sortBy(_.identifiers.toIndexedSeq.sortBy(i => (i.confidence.id, i.identifierType, i.name)).mkString(", ")),
|
allDependencies.sortBy(_.identifiers.toIndexedSeq.sortBy(i => (i.confidence.id, i.identifierType, i.name)).mkString(", ")),
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
headExtension = he,
|
headExtension = he,
|
||||||
projectsOption = Some((projectsWithSelection, routes.Statistics.basic(_)))
|
projectsOption = Some((projectsWithSelection, routes.Statistics.basic(_)))
|
||||||
){
|
){
|
||||||
|
@healthReport(lds.failedProjects)
|
||||||
All dependencies: @parsedReports.groupedDependencies.size <br>
|
All dependencies: @parsedReports.groupedDependencies.size <br>
|
||||||
Vulnerable dependencies: @parsedReports.vulnerableDependencies.size <br>
|
Vulnerable dependencies: @parsedReports.vulnerableDependencies.size <br>
|
||||||
Vulnerabilities: @parsedReports.vulnerableDependencies.flatMap(_.vulnerabilities.map(_.name)).toSet.size<br>
|
Vulnerabilities: @parsedReports.vulnerableDependencies.flatMap(_.vulnerabilities.map(_.name)).toSet.size<br>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
title = s"details for ${projectsWithSelection.projectNameText}${tagOption.map(_._2.name).fold("")(" and tag "+_)}",
|
title = s"details for ${projectsWithSelection.projectNameText}${tagOption.map(_._2.name).fold("")(" and tag "+_)}",
|
||||||
projectsOption = Some((projectsWithSelection, x => routes.Statistics.vulnerabilities(x, tagOption.map(_._1))))
|
projectsOption = Some((projectsWithSelection, x => routes.Statistics.vulnerabilities(x, tagOption.map(_._1))))
|
||||||
){
|
){
|
||||||
|
@healthReport(statistics.failedProjects)
|
||||||
We have @statistics.vulnerabilitiesToDependencies.size vulnerabilities
|
We have @statistics.vulnerabilitiesToDependencies.size vulnerabilities
|
||||||
of @statistics.vulnerabilitiesToDependencies.flatMap(_._2).toSet.size dependencies (@statistics.vulnerabilitiesToDependencies.flatMap(_._2.flatMap(_.plainLibraryIdentifiers)).toSet.size libraries).
|
of @statistics.vulnerabilitiesToDependencies.flatMap(_._2).toSet.size dependencies (@statistics.vulnerabilitiesToDependencies.flatMap(_._2.flatMap(_.plainLibraryIdentifiers)).toSet.size libraries).
|
||||||
@if(!projectsWithSelection.isProjectSpecified){
|
@if(!projectsWithSelection.isProjectSpecified){
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
|
@import com.ysoft.odc.statistics.FailedProjects
|
||||||
@(
|
@(
|
||||||
projectsWithSelection: ProjectsWithSelection,
|
projectsWithSelection: ProjectsWithSelection,
|
||||||
vulnerability: Vulnerability,
|
vulnerability: Vulnerability,
|
||||||
affectedProjects: Map[ReportInfo, Set[GroupedDependency]],
|
affectedProjects: Map[ReportInfo, Set[GroupedDependency]],
|
||||||
vulnerableDependencies: Set[GroupedDependency],
|
vulnerableDependencies: Set[GroupedDependency],
|
||||||
affectedLibraries: Set[PlainLibraryIdentifier],
|
affectedLibraries: Set[PlainLibraryIdentifier],
|
||||||
issueOption: Option[(ExportedVulnerability[String], String)]
|
issueOption: Option[(ExportedVulnerability[String], String)],
|
||||||
|
failedProjects: FailedProjects
|
||||||
)(implicit header: DefaultRequest)
|
)(implicit header: DefaultRequest)
|
||||||
@section = @{views.html.genericSection("vuln")("h2") _}
|
@section = @{views.html.genericSection("vuln")("h2") _}
|
||||||
@main(
|
@main(
|
||||||
title = s"vulnerability ${vulnerability.name} for ${projectsWithSelection.projectNameText}",
|
title = s"vulnerability ${vulnerability.name} for ${projectsWithSelection.projectNameText}",
|
||||||
projectsOption = Some((projectsWithSelection, p => routes.Statistics.vulnerability(vulnerability.name, p)))
|
projectsOption = Some((projectsWithSelection, p => routes.Statistics.vulnerability(vulnerability.name, p)))
|
||||||
) {
|
) {
|
||||||
|
@healthReport(failedProjects)
|
||||||
@if(projectsWithSelection.isProjectSpecified){
|
@if(projectsWithSelection.isProjectSpecified){
|
||||||
<div class="alert alert-warning">The vulnerability details are limited to some subset of projects.<br><a class="btn btn-default" href="@routes.Statistics.vulnerability(vulnerability.name, None)">Show it for all projects!</a></div>
|
<div class="alert alert-warning">The vulnerability details are limited to some subset of projects.<br><a class="btn btn-default" href="@routes.Statistics.vulnerability(vulnerability.name, None)">Show it for all projects!</a></div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
|
@import com.ysoft.odc.statistics.FailedProjects
|
||||||
@(
|
@(
|
||||||
projectsWithSelection: ProjectsWithSelection,
|
projectsWithSelection: ProjectsWithSelection,
|
||||||
name: String
|
name: String,
|
||||||
|
failedProjects: FailedProjects
|
||||||
)(implicit header: DefaultRequest)
|
)(implicit header: DefaultRequest)
|
||||||
|
|
||||||
@main(
|
@main(
|
||||||
title = s"Unknown vulnerability $name for ${projectsWithSelection.projectNameText}",
|
title = s"Unknown vulnerability $name for ${projectsWithSelection.projectNameText}",
|
||||||
projectsOption = Some((projectsWithSelection, p => routes.Statistics.vulnerability(name, p)))
|
projectsOption = Some((projectsWithSelection, p => routes.Statistics.vulnerability(name, p)))
|
||||||
){
|
){
|
||||||
|
@healthReport(failedProjects)
|
||||||
<div class="alert alert-warning">Vulnerability <i>@name</i> is not found@if(projectsWithSelection.isProjectSpecified){ for selected project(s)}.</div>
|
<div class="alert alert-warning">Vulnerability <i>@name</i> is not found@if(projectsWithSelection.isProjectSpecified){ for selected project(s)}.</div>
|
||||||
<h2>Possible solutions</h2>
|
<h2>Possible solutions</h2>
|
||||||
<ul class="solutions">
|
<ul class="solutions">
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
@(
|
@(
|
||||||
projectsWithSelection: ProjectsWithSelection,
|
projectsWithSelection: ProjectsWithSelection,
|
||||||
vulnerableDependencies: Seq[GroupedDependency],
|
vulnerableDependencies: Seq[GroupedDependency],
|
||||||
allDependenciesCount: Int
|
allDependenciesCount: Int,
|
||||||
|
reports: DependencyCheckReportsParser.Result
|
||||||
)(implicit header: DefaultRequest)
|
)(implicit header: DefaultRequest)
|
||||||
|
|
||||||
@main(
|
@main(
|
||||||
title = s"Vulnerable libraries for ${projectsWithSelection.projectNameText} (${vulnerableDependencies.size} deps, ${vulnerableDependencies.flatMap(_.cpeIdentifiers.map(_.toCpeIdentifierOption.get)).toSet.size} CPEs)",
|
title = s"Vulnerable libraries for ${projectsWithSelection.projectNameText} (${vulnerableDependencies.size} deps, ${vulnerableDependencies.flatMap(_.cpeIdentifiers.map(_.toCpeIdentifierOption.get)).toSet.size} CPEs)",
|
||||||
projectsOption = Some((projectsWithSelection, routes.Statistics.vulnerableLibraries(_)))
|
projectsOption = Some((projectsWithSelection, routes.Statistics.vulnerableLibraries(_)))
|
||||||
){
|
){
|
||||||
|
@healthReport(reports.failedProjects)
|
||||||
<script type="text/javascript" src="@routes.Assets.versioned("lib/jqplot/jquery.jqplot.min.js")"></script>
|
<script type="text/javascript" src="@routes.Assets.versioned("lib/jqplot/jquery.jqplot.min.js")"></script>
|
||||||
<script type="text/javascript" src="@routes.Assets.versioned("lib/jqplot/plugins/jqplot.pieRenderer.min.js")"></script>
|
<script type="text/javascript" src="@routes.Assets.versioned("lib/jqplot/plugins/jqplot.pieRenderer.min.js")"></script>
|
||||||
<h2>Plot</h2>
|
<h2>Plot</h2>
|
||||||
|
|||||||
Reference in New Issue
Block a user