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:
Šesták Vít
2016-05-13 19:31:47 +02:00
parent 604fc56d76
commit 6c8b2cf859
14 changed files with 104 additions and 51 deletions

View File

@@ -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)
} }
} }

View File

@@ -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
)
) )
} }

View File

@@ -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)
)
} }
} }

View File

@@ -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)

View File

@@ -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

View File

@@ -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
))) )))
} }
} }

View 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>
}

View File

@@ -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>

View File

@@ -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(", ")),

View File

@@ -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>

View File

@@ -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){

View File

@@ -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>
} }

View File

@@ -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">

View File

@@ -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>