mirror of
https://github.com/ysoftdevs/odc-analyzer.git
synced 2026-03-27 11:31:47 +01:00
Added more fail safety for vulnerability export.
This should affect all exports when a vulnerability disappears.
This commit is contained in:
@@ -8,7 +8,7 @@ import com.ysoft.html.HtmlWithText._
|
||||
import com.ysoft.odc.{Absolutizer, SetDiff}
|
||||
import controllers._
|
||||
import models.Change.Direction
|
||||
import models.{Change, EmailMessageId}
|
||||
import models.{Change, EmailMessageId, VulnerabilityOverview}
|
||||
import play.api.libs.mailer.{Email, MailerClient}
|
||||
import play.twirl.api.{Html, HtmlFormat}
|
||||
|
||||
@@ -19,50 +19,11 @@ object EmailExportType extends Enumeration {
|
||||
val Digest = Value("digest")
|
||||
|
||||
|
||||
}
|
||||
object EmailExportService {
|
||||
|
||||
private object VulnerabilityDescription{
|
||||
def apply(name: String, v: Option[Vulnerability]): VulnerabilityDescription = v.fold(UnknownVulnerabilityDescription(name))(new StandardVulnerabilityDescription(_))
|
||||
}
|
||||
|
||||
private abstract class VulnerabilityDescription {
|
||||
def name: String
|
||||
def description: String
|
||||
def cvssScore: Option[Double]
|
||||
}
|
||||
|
||||
private final class StandardVulnerabilityDescription(vulnerability: Vulnerability) extends VulnerabilityDescription {
|
||||
override def name: String = vulnerability.name
|
||||
override def description: String = vulnerability.description
|
||||
override def cvssScore: Option[Double] = vulnerability.cvssScore
|
||||
}
|
||||
|
||||
private final class UnknownVulnerabilityDescription(override val name: String, link: String) extends VulnerabilityDescription {
|
||||
override def description: String = s"Unknown vulnerability. Try looking at the following address for more details: $link"
|
||||
override def cvssScore: Option[Double] = None
|
||||
}
|
||||
|
||||
private final class TotallyUnknownVulnerabilityDescription(override val name: String) extends VulnerabilityDescription {
|
||||
override def description: String = s"Unknown vulnerability. Not even sure where to look for other details. Maybe Googling the identifier will help."
|
||||
override def cvssScore: Option[Double] = None
|
||||
}
|
||||
|
||||
private object UnknownVulnerabilityDescription {
|
||||
def apply(name: String): VulnerabilityDescription = name match {
|
||||
case cveId if name startsWith "CVE-" => new UnknownVulnerabilityDescription(name, s"https://nvd.nist.gov/vuln/detail/$cveId")
|
||||
case ossIndexId if name startsWith "OSSINDEX-" => new UnknownVulnerabilityDescription(name, s"https://ossindex.net/resource/vulnerability/$ossIndexId")
|
||||
case other => new TotallyUnknownVulnerabilityDescription(other)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class EmailExportService(from: String, nobodyInterestedContact: String, val exportType: EmailExportType.Value, odcService: OdcDbService, mailerClient: MailerClient, notificationService: VulnerabilityNotificationService, emailSendingExecutionContext: ExecutionContext, absolutizer: Absolutizer)(implicit executionContext: ExecutionContext) {
|
||||
// Maybe it is not the best place for exportType, but I am not sure if we want this to be configurable. If no, then we can get rid of it. If yes, we should refactor it.
|
||||
|
||||
import EmailExportService.VulnerabilityDescription
|
||||
|
||||
private def getEmail(loginInfo: LoginInfo) = loginInfo.providerKey // TODO: get the email in a cleaner way
|
||||
|
||||
def recipientsForProjects(projects: Set[ReportInfo]) = for{
|
||||
@@ -74,7 +35,7 @@ class EmailExportService(from: String, nobodyInterestedContact: String, val expo
|
||||
}
|
||||
}
|
||||
|
||||
def mailForVulnerabilityProjectsChange(vuln: Vulnerability, emailMessageId: EmailMessageId, diff: SetDiff[String], projects: ProjectsWithReports) = {
|
||||
def mailForVulnerabilityProjectsChange(vuln: VulnerabilityOverview, emailMessageId: EmailMessageId, diff: SetDiff[String], projects: ProjectsWithReports) = {
|
||||
def showProjects(s: Set[String]) = s.map(p =>
|
||||
"* " + (try{
|
||||
friendlyProjectNameString(projects.parseUnfriendlyName(p))
|
||||
@@ -119,7 +80,7 @@ class EmailExportService(from: String, nobodyInterestedContact: String, val expo
|
||||
def emailDigest(subscriber: LoginInfo, changes: Seq[Change], projects: ProjectsWithReports): Future[Email] = {
|
||||
val vulnNames = changes.map(_.vulnerabilityName).toSet
|
||||
for {
|
||||
vulns <- Future.traverse(vulnNames.toSeq)(name => odcService.getVulnerabilityDetails(name).map(v => name -> VulnerabilityDescription(name, v))).map(_.toMap)
|
||||
vulns <- Future.traverse(vulnNames.toSeq)(name => odcService.getVulnerabilityDetails(name).map(v => name -> VulnerabilityOverview(name, v))).map(_.toMap)
|
||||
groups = changes.groupBy(_.direction).withDefaultValue(Seq())
|
||||
} yield {
|
||||
val changesMarks = Map(Direction.Added -> "❢", Direction.Removed -> "☑")
|
||||
@@ -131,9 +92,9 @@ class EmailExportService(from: String, nobodyInterestedContact: String, val expo
|
||||
text = "more info: "+link,
|
||||
html = Html("<a href=\""+HtmlFormat.escape(link)+"\">more info</a>")
|
||||
)
|
||||
def vulnerabilityText(change: Change, vulnerability: VulnerabilityDescription): HtmlWithText = (
|
||||
def vulnerabilityText(change: Change, vulnerability: VulnerabilityOverview): HtmlWithText = (
|
||||
heading(4)(s"${changesMarks(change.direction)} ${vulnerability.name}${vulnerability.cvssScore.fold("")(sev => s" (CVSS severity: $sev)")}")
|
||||
+ justHtml("<p>") + plainText(vulnerability.description) + justHtml("<br>") + justText("\n")
|
||||
+ justHtml("<p>") + plainText(vulnerability.descriptionAttempt) + justHtml("<br>") + justText("\n")
|
||||
+ moreInfo(absolutizer.absolutize(routes.Statistics.vulnerability(vulnerability.name, None))) + justHtml("</p>")
|
||||
)
|
||||
def vulnChanges(changes: Seq[Change]): HtmlWithText =
|
||||
|
||||
Reference in New Issue
Block a user