mirror of
https://github.com/ysoftdevs/odc-analyzer.git
synced 2026-03-28 12:02:00 +01:00
Drastically reduced memory usage, mostly using deduplication.
This commit is contained in:
25
app/com/ysoft/debug/KnownObjects.scala
Normal file
25
app/com/ysoft/debug/KnownObjects.scala
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package com.ysoft.debug
|
||||||
|
|
||||||
|
import java.util
|
||||||
|
|
||||||
|
import com.google.caliper.memory.ObjectVisitor.Traversal
|
||||||
|
import play.api.Logger
|
||||||
|
|
||||||
|
// We use Java collections because they can have the initial size configured
|
||||||
|
case class KnownObjects(
|
||||||
|
objSet: java.util.HashSet[Any] = new util.HashSet[Any](),
|
||||||
|
identitiesSet: java.util.Set[Any] = java.util.Collections.newSetFromMap(new util.IdentityHashMap[Any, java.lang.Boolean]())
|
||||||
|
){
|
||||||
|
def visit(obj: AnyRef) = {
|
||||||
|
val seen = !identitiesSet.add(obj)
|
||||||
|
if(seen){
|
||||||
|
Traversal.SKIP
|
||||||
|
}else{
|
||||||
|
objSet.add(obj)
|
||||||
|
Traversal.EXPLORE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def stats = (identitiesSet.size, objSet.size)
|
||||||
|
|
||||||
|
}
|
||||||
44
app/com/ysoft/debug/ObjectGraphDuplicityMeasurer.scala
Normal file
44
app/com/ysoft/debug/ObjectGraphDuplicityMeasurer.scala
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package com.ysoft.debug
|
||||||
|
|
||||||
|
import java.util
|
||||||
|
|
||||||
|
import com.google.caliper.memory.ObjectVisitor.Traversal
|
||||||
|
import com.google.caliper.memory.{Chain, ObjectExplorer, ObjectVisitor}
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
object ObjectGraphDuplicityMeasurer {
|
||||||
|
|
||||||
|
def measureUnique(obj: AnyRef) = {
|
||||||
|
ObjectExplorer.exploreObject(obj, new ObjectVisitor[((Int, Int), Map[Class[_], (Int, Int)])](){
|
||||||
|
val all = KnownObjects(
|
||||||
|
objSet = new util.HashSet[Any](),
|
||||||
|
identitiesSet = java.util.Collections.newSetFromMap(new util.IdentityHashMap[Any, java.lang.Boolean]())
|
||||||
|
)
|
||||||
|
|
||||||
|
val classMap = mutable.Map[Class[_], KnownObjects]()
|
||||||
|
def forClass(cl: Class[_]) = classMap.contains(cl) match{
|
||||||
|
case true => classMap(cl)
|
||||||
|
case false =>
|
||||||
|
val kn = KnownObjects()
|
||||||
|
classMap(cl) = kn
|
||||||
|
kn
|
||||||
|
}
|
||||||
|
|
||||||
|
override def visit(chain: Chain): Traversal = {
|
||||||
|
val value = chain.getValue
|
||||||
|
if(chain.isPrimitive || value == null || classOf[Enum[_]].isAssignableFrom(chain.getValueType) || value.isInstanceOf[Class[_]] ){
|
||||||
|
Traversal.SKIP
|
||||||
|
}else{
|
||||||
|
val res = all.visit(value)
|
||||||
|
forClass(value.getClass).visit(value)
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def result() = (all.stats, classMap.toMap.mapValues(_.stats).map(identity))
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
24
app/com/ysoft/memory/ObjectPool.scala
Normal file
24
app/com/ysoft/memory/ObjectPool.scala
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package com.ysoft.memory
|
||||||
|
|
||||||
|
import java.lang.ref.{WeakReference => JWeakReference}
|
||||||
|
import java.util
|
||||||
|
|
||||||
|
class ObjectPool{
|
||||||
|
|
||||||
|
private val objects = new util.WeakHashMap[Any, JWeakReference[Any]]()//new MapMaker().concurrencyLevel(1).weakKeys().weakValues().makeMap[Any, Any]()
|
||||||
|
|
||||||
|
def apply[T](obj: T): T = synchronized{
|
||||||
|
// The code is intentionally low-level for performance reasons. No Option[_] used for performance reasons, no scala.ref._ wrapper is used for memory overhead reasons.
|
||||||
|
val res = objects.get(obj) match {
|
||||||
|
case null => null
|
||||||
|
case weakObj => weakObj.get()
|
||||||
|
}
|
||||||
|
if(res == null){
|
||||||
|
objects.put(obj, new JWeakReference[Any](obj))
|
||||||
|
obj
|
||||||
|
}else{
|
||||||
|
res.asInstanceOf[T]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,16 +1,19 @@
|
|||||||
package com.ysoft.odc
|
package com.ysoft.odc
|
||||||
|
|
||||||
import com.github.nscala_time.time.Imports._
|
import com.github.nscala_time.time.Imports._
|
||||||
|
import com.ysoft.memory.ObjectPool
|
||||||
import controllers.ReportInfo
|
import controllers.ReportInfo
|
||||||
import models.{LibraryType, PlainLibraryIdentifier}
|
import models.{LibraryType, PlainLibraryIdentifier}
|
||||||
|
|
||||||
import scala.xml._
|
import scala.xml._
|
||||||
|
|
||||||
final case class SerializableXml private (xmlString: String, @transient private val xmlData: NodeSeq) extends Serializable{
|
|
||||||
@transient lazy val xml = Option(xmlData).getOrElse(SecureXml.loadString(xmlString))
|
final case class SerializableXml private (xmlString: String) extends Serializable{
|
||||||
|
def xml = SecureXml.loadString(xmlString) // TODO: cache
|
||||||
|
|
||||||
override def equals(obj: scala.Any): Boolean = obj match {
|
override def equals(obj: scala.Any): Boolean = obj match {
|
||||||
case SerializableXml(s, _) => s == this.xmlString
|
case SerializableXml(s/*, _*/) => s == this.xmlString
|
||||||
|
case other => false
|
||||||
}
|
}
|
||||||
|
|
||||||
override def hashCode(): Int = 42+xmlString.hashCode
|
override def hashCode(): Int = 42+xmlString.hashCode
|
||||||
@@ -18,8 +21,8 @@ final case class SerializableXml private (xmlString: String, @transient private
|
|||||||
}
|
}
|
||||||
|
|
||||||
object SerializableXml{
|
object SerializableXml{
|
||||||
def apply(xml: Node): SerializableXml = SerializableXml(xml.toString(), xml)
|
def apply(xml: Node): SerializableXml = SerializableXml(xml.toString())
|
||||||
def apply(xml: NodeSeq): SerializableXml = SerializableXml(xml.toString(), xml)
|
def apply(xml: NodeSeq): SerializableXml = SerializableXml(xml.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class Analysis(scanInfo: SerializableXml, name: String, reportDate: DateTime, dependencies: Seq[Dependency])
|
final case class Analysis(scanInfo: SerializableXml, name: String, reportDate: DateTime, dependencies: Seq[Dependency])
|
||||||
@@ -61,6 +64,7 @@ final case class Dependency(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A group of dependencies having the same fingerprints
|
* A group of dependencies having the same fingerprints
|
||||||
|
*
|
||||||
* @param dependencies
|
* @param dependencies
|
||||||
*/
|
*/
|
||||||
final case class GroupedDependency(dependencies: Map[Dependency, Set[ReportInfo]]) {
|
final case class GroupedDependency(dependencies: Map[Dependency, Set[ReportInfo]]) {
|
||||||
@@ -82,7 +86,10 @@ final case class GroupedDependency(dependencies: Map[Dependency, Set[ReportInfo]
|
|||||||
}
|
}
|
||||||
|
|
||||||
object GroupedDependency{
|
object GroupedDependency{
|
||||||
def apply(deps: Seq[(Dependency, ReportInfo)]): GroupedDependency = GroupedDependency(deps.groupBy(_._1).mapValues(_.map(_._2).toSet)) // TODO: the groupBy seems to be a CPU hog (because of GroupedDependency.equals); The mapValues is lazy, so its repeated might also be a performance hog, but I doubt that values are used frequently.
|
private val groupToSet = (_: Seq[(Dependency, ReportInfo)]).map(_._2).toSet // reduces number of lambda instances
|
||||||
|
def apply(deps: Seq[(Dependency, ReportInfo)]): GroupedDependency = {
|
||||||
|
GroupedDependency(deps.groupBy(_._1).mapValues(groupToSet))
|
||||||
|
} // TODO: the groupBy seems to be a CPU hog (because of GroupedDependency.equals); The mapValues is lazy, so its repeated might also be a performance hog, but I doubt that values are used frequently.
|
||||||
}
|
}
|
||||||
|
|
||||||
object Confidence extends Enumeration {
|
object Confidence extends Enumeration {
|
||||||
@@ -101,7 +108,7 @@ final case class VulnerableSoftware(allPreviousVersion: Boolean, name: String)
|
|||||||
|
|
||||||
final case class CvssRating(score: Option[Double], authenticationr: Option[String], availabilityImpact: Option[String], accessVector: Option[String], integrityImpact: Option[String], accessComplexity: Option[String], confidentialImpact: Option[String])
|
final case class CvssRating(score: Option[Double], authenticationr: Option[String], availabilityImpact: Option[String], accessVector: Option[String], integrityImpact: Option[String], accessComplexity: Option[String], confidentialImpact: Option[String])
|
||||||
|
|
||||||
final case class CWE(name: String) extends AnyVal{
|
final case class CWE private(name: String) /*extends AnyVal*/{ // extends AnyVal prevents pooling
|
||||||
override def toString = name
|
override def toString = name
|
||||||
def brief = name.takeWhile(_ != ' ')
|
def brief = name.takeWhile(_ != ' ')
|
||||||
def numberOption: Option[Int] = if(brief startsWith "CWE-") try {
|
def numberOption: Option[Int] = if(brief startsWith "CWE-") try {
|
||||||
@@ -111,6 +118,11 @@ final case class CWE(name: String) extends AnyVal{
|
|||||||
} else None
|
} else None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object CWE{
|
||||||
|
private val cwePool = new ObjectPool()
|
||||||
|
def forIdentifierWithDescription(name: String) = cwePool(new CWE(name))
|
||||||
|
}
|
||||||
|
|
||||||
final case class Vulnerability(name: String, cweOption: Option[CWE], cvss: CvssRating, description: String, vulnerableSoftware: Seq[VulnerableSoftware], references: Seq[Reference]){
|
final case class Vulnerability(name: String, cweOption: Option[CWE], cvss: CvssRating, description: String, vulnerableSoftware: Seq[VulnerableSoftware], references: Seq[Reference]){
|
||||||
def cvssScore = cvss.score
|
def cvssScore = cvss.score
|
||||||
def ysvssScore(affectedDeps: Set[GroupedDependency]) = cvssScore.map(_ * affectedDeps.flatMap(_.projects).toSet.size)
|
def ysvssScore(affectedDeps: Set[GroupedDependency]) = cvssScore.map(_ * affectedDeps.flatMap(_.projects).toSet.size)
|
||||||
@@ -134,6 +146,12 @@ final case class Identifier(name: String, confidence: Confidence.Confidence, url
|
|||||||
|
|
||||||
object OdcParser {
|
object OdcParser {
|
||||||
|
|
||||||
|
private val vulnPool = new ObjectPool()
|
||||||
|
private val evidencePool = new ObjectPool()
|
||||||
|
private val dependencyPool = new ObjectPool()
|
||||||
|
private val identifierPool = new ObjectPool()
|
||||||
|
private val vulnerableSoftwarePool = new ObjectPool()
|
||||||
|
|
||||||
def filterWhitespace(node: Node) = node.nonEmptyChildren.filter{
|
def filterWhitespace(node: Node) = node.nonEmptyChildren.filter{
|
||||||
case t: scala.xml.Text if t.text.trim == "" => false
|
case t: scala.xml.Text if t.text.trim == "" => false
|
||||||
case t: scala.xml.PCData if t.text.trim == "" => false
|
case t: scala.xml.PCData if t.text.trim == "" => false
|
||||||
@@ -168,10 +186,10 @@ object OdcParser {
|
|||||||
if(node.label != "software"){
|
if(node.label != "software"){
|
||||||
sys.error(s"Unexpected element for vulnerableSoftware: ${node.label}")
|
sys.error(s"Unexpected element for vulnerableSoftware: ${node.label}")
|
||||||
}
|
}
|
||||||
VulnerableSoftware(
|
vulnerableSoftwarePool(VulnerableSoftware(
|
||||||
name = node.text,
|
name = node.text,
|
||||||
allPreviousVersion = node.attribute("allPreviousVersion").map(_.text).map(Map("true"->true, "false"->false)).getOrElse(false)
|
allPreviousVersion = node.attribute("allPreviousVersion").map(_.text).map(Map("true"->true, "false"->false)).getOrElse(false)
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
def parseReference(node: Node): Reference = {
|
def parseReference(node: Node): Reference = {
|
||||||
@@ -207,10 +225,10 @@ object OdcParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Vulnerability(
|
vulnPool(Vulnerability(
|
||||||
name = (node \ "name").text,
|
name = (node \ "name").text,
|
||||||
//severity = (node \ "severity"), <- severity is useless, as it is computed from cvssScore :D
|
//severity = (node \ "severity"), <- severity is useless, as it is computed from cvssScore :D
|
||||||
cweOption = (node \ "cwe").headOption.map(_.text).map(CWE),
|
cweOption = (node \ "cwe").headOption.map(_.text).map(CWE.forIdentifierWithDescription),
|
||||||
description = (node \ "description").text,
|
description = (node \ "description").text,
|
||||||
cvss = CvssRating(
|
cvss = CvssRating(
|
||||||
score = (node \ "cvssScore").headOption.map(_.text.toDouble),
|
score = (node \ "cvssScore").headOption.map(_.text.toDouble),
|
||||||
@@ -223,21 +241,21 @@ object OdcParser {
|
|||||||
),
|
),
|
||||||
references = (node \ "references").flatMap(filterWhitespace).map(parseReference(_)),
|
references = (node \ "references").flatMap(filterWhitespace).map(parseReference(_)),
|
||||||
vulnerableSoftware = (node \ "vulnerableSoftware").flatMap(filterWhitespace).map(parseVulnerableSoftware)
|
vulnerableSoftware = (node \ "vulnerableSoftware").flatMap(filterWhitespace).map(parseVulnerableSoftware)
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
def parseIdentifier(node: Node): Identifier = {
|
def parseIdentifier(node: Node): Identifier = {
|
||||||
checkElements(node, Set("name", "url"))
|
checkElements(node, Set("name", "url"))
|
||||||
checkParams(node, Set("type", "confidence"))
|
checkParams(node, Set("type", "confidence"))
|
||||||
val ExtractPattern = """\((.*)\)""".r
|
val ExtractPattern = """\((.*)\)""".r
|
||||||
Identifier(
|
identifierPool(Identifier(
|
||||||
name = (node \ "name").text match {
|
name = (node \ "name").text match {
|
||||||
case ExtractPattern(text) => text
|
case ExtractPattern(text) => text
|
||||||
},
|
},
|
||||||
url = (node \ "url").text,
|
url = (node \ "url").text,
|
||||||
identifierType = node.attribute("type").get.text,
|
identifierType = node.attribute("type").get.text,
|
||||||
confidence = Confidence.withName(node.attribute("confidence").get.text)
|
confidence = Confidence.withName(node.attribute("confidence").get.text)
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
def parseIdentifiers(seq: Node): Seq[Identifier] = {
|
def parseIdentifiers(seq: Node): Seq[Identifier] = {
|
||||||
@@ -248,7 +266,7 @@ object OdcParser {
|
|||||||
checkElements(node, Set("fileName", "filePath", "md5", "sha1", "description", "evidenceCollected", "identifiers", "license", "vulnerabilities", "relatedDependencies"))
|
checkElements(node, Set("fileName", "filePath", "md5", "sha1", "description", "evidenceCollected", "identifiers", "license", "vulnerabilities", "relatedDependencies"))
|
||||||
checkParams(node, Set())
|
checkParams(node, Set())
|
||||||
val (vulnerabilities: Seq[Node], suppressedVulnerabilities: Seq[Node]) = (node \ "vulnerabilities").headOption.map(filterWhitespace).getOrElse(Seq()).partition(_.label == "vulnerability")
|
val (vulnerabilities: Seq[Node], suppressedVulnerabilities: Seq[Node]) = (node \ "vulnerabilities").headOption.map(filterWhitespace).getOrElse(Seq()).partition(_.label == "vulnerability")
|
||||||
Dependency(
|
dependencyPool(Dependency(
|
||||||
fileName = (node \ "fileName").text,
|
fileName = (node \ "fileName").text,
|
||||||
filePath = (node \ "filePath").text,
|
filePath = (node \ "filePath").text,
|
||||||
md5 = (node \ "md5").text,
|
md5 = (node \ "md5").text,
|
||||||
@@ -260,7 +278,7 @@ object OdcParser {
|
|||||||
vulnerabilities = vulnerabilities.map(parseVulnerability(_)),
|
vulnerabilities = vulnerabilities.map(parseVulnerability(_)),
|
||||||
suppressedVulnerabilities = suppressedVulnerabilities.map(parseVulnerability(_, "suppressedVulnerability")),
|
suppressedVulnerabilities = suppressedVulnerabilities.map(parseVulnerability(_, "suppressedVulnerability")),
|
||||||
relatedDependencies = SerializableXml(node \ "relatedDependencies")
|
relatedDependencies = SerializableXml(node \ "relatedDependencies")
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
def parseEvidence(node: Node): Evidence = {
|
def parseEvidence(node: Node): Evidence = {
|
||||||
@@ -269,13 +287,13 @@ object OdcParser {
|
|||||||
}
|
}
|
||||||
checkElements(node, Set("source", "name", "value"))
|
checkElements(node, Set("source", "name", "value"))
|
||||||
checkParams(node, Set("confidence", "type"))
|
checkParams(node, Set("confidence", "type"))
|
||||||
Evidence(
|
evidencePool(Evidence(
|
||||||
source = (node \ "source").text,
|
source = (node \ "source").text,
|
||||||
name = (node \ "name").text,
|
name = (node \ "name").text,
|
||||||
value = (node \ "value").text,
|
value = (node \ "value").text,
|
||||||
confidence = node.attribute("confidence").map(_.text).get,
|
confidence = node.attribute("confidence").map(_.text).get,
|
||||||
evidenceType = node.attribute("type").map(_.text).get
|
evidenceType = node.attribute("type").map(_.text).get
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
def parseDependencies(nodes: NodeSeq): Seq[Dependency] = nodes.map(parseDependency(_))
|
def parseDependencies(nodes: NodeSeq): Seq[Dependency] = nodes.map(parseDependency(_))
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import play.api.data.Forms._
|
|||||||
import play.api.data._
|
import play.api.data._
|
||||||
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
|
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
|
||||||
import play.api.http.ContentTypes
|
import play.api.http.ContentTypes
|
||||||
import play.api.i18n.{I18nSupport, MessagesApi}
|
import play.api.i18n.MessagesApi
|
||||||
import play.api.libs.json._
|
import play.api.libs.json._
|
||||||
import play.api.mvc._
|
import play.api.mvc._
|
||||||
import play.api.routing.JavaScriptReverseRouter
|
import play.api.routing.JavaScriptReverseRouter
|
||||||
@@ -62,7 +62,6 @@ class Application @Inject() (
|
|||||||
import dbConfig.driver.api._
|
import dbConfig.driver.api._
|
||||||
import models.tables.snoozesTable
|
import models.tables.snoozesTable
|
||||||
import reportsProcessor.processResults
|
import reportsProcessor.processResults
|
||||||
|
|
||||||
import secureRequestConversion._
|
import secureRequestConversion._
|
||||||
|
|
||||||
val dateFormatter = DateTimeFormat.forPattern("dd-MM-yyyy")
|
val dateFormatter = DateTimeFormat.forPattern("dd-MM-yyyy")
|
||||||
@@ -126,6 +125,11 @@ class Application @Inject() (
|
|||||||
lastRefreshTime <- lastRefreshTimeFuture
|
lastRefreshTime <- lastRefreshTimeFuture
|
||||||
} yield {
|
} yield {
|
||||||
Logger.debug("indexPage: Got all ingredients")
|
Logger.debug("indexPage: Got all ingredients")
|
||||||
|
/*val (global, classes) = ObjectGraphDuplicityMeasurer.measureUnique((vulnerableDependencies, allWarnings, groupedDependencies))
|
||||||
|
Logger.debug("(all,unique): "+global)
|
||||||
|
Logger.debug(classes.toIndexedSeq.sortBy(x => (x._2, x._1.getName)).mkString("\n"))
|
||||||
|
Logger.debug("footprint: "+ObjectGraphMeasurer.measure((vulnerableDependencies, allWarnings, groupedDependencies)))
|
||||||
|
//Logger.debug("footprint: "+ObjectGraphMeasurer.measure(Array((vulnerableDependencies, allWarnings, groupedDependencies))))*/
|
||||||
Ok(views.html.index(
|
Ok(views.html.index(
|
||||||
vulnerableDependencies = vulnerableDependencies,
|
vulnerableDependencies = vulnerableDependencies,
|
||||||
warnings = allWarnings,
|
warnings = allWarnings,
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ final class DependencyCheckReportsProcessor @Inject() (
|
|||||||
|
|
||||||
// TODO: log analysis
|
// TODO: log analysis
|
||||||
// TODO: related dependencies
|
// TODO: related dependencies
|
||||||
(vulnerableDependencies, allWarnings, groupedDependencies)
|
(vulnerableDependencies, allWarnings.map(_.optimize), groupedDependencies)
|
||||||
}
|
}
|
||||||
}finally{
|
}finally{
|
||||||
Logger.debug("Reports processed")
|
Logger.debug("Reports processed")
|
||||||
|
|||||||
@@ -12,10 +12,13 @@ object WarningSeverity extends Enumeration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed abstract class Warning {
|
sealed abstract class Warning {
|
||||||
|
def optimize: Warning
|
||||||
def html: Html
|
def html: Html
|
||||||
def id: String
|
def id: String
|
||||||
def allowSnoozes = true
|
def allowSnoozes = true
|
||||||
def severity: WarningSeverity
|
def severity: WarningSeverity
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class IdentifiedWarning(id: String, html: Html, severity: WarningSeverity) extends Warning
|
final case class IdentifiedWarning(id: String, html: Html, severity: WarningSeverity) extends Warning{
|
||||||
|
def optimize = copy(html = Html(html.body))
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ class Vulnerabilities(tag: Tag) extends Table[(Int, Vulnerability)](tag, "vulner
|
|||||||
def cvssConfidentialityImpact = column[String]("cvssConfidentialityImpact").?
|
def cvssConfidentialityImpact = column[String]("cvssConfidentialityImpact").?
|
||||||
|
|
||||||
def cvssRating = (cvssScore, authentication, availabilityImpact, accessVector, integrityImpact, cvssAccessComplexity, cvssConfidentialityImpact) <> (CvssRating.tupled, CvssRating.unapply)
|
def cvssRating = (cvssScore, authentication, availabilityImpact, accessVector, integrityImpact, cvssAccessComplexity, cvssConfidentialityImpact) <> (CvssRating.tupled, CvssRating.unapply)
|
||||||
def cweOptionMapped = cweOption <> ((_: Option[String]).map(CWE.apply), (_: Option[CWE]).map(CWE.unapply))
|
def cweOptionMapped = cweOption <> ((_: Option[String]).map(CWE.forIdentifierWithDescription), (_: Option[CWE]).map(CWE.unapply))
|
||||||
def base = (cve, description, cweOptionMapped, cvssRating) <> (Vulnerability.tupled, Vulnerability.unapply)
|
def base = (cve, description, cweOptionMapped, cvssRating) <> (Vulnerability.tupled, Vulnerability.unapply)
|
||||||
|
|
||||||
def * = (id, base)
|
def * = (id, base)
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ libraryDependencies += "org.owasp" % "dependency-check-core" % "1.3.0"
|
|||||||
|
|
||||||
libraryDependencies += "com.typesafe.play" %% "play-mailer" % "3.0.1"
|
libraryDependencies += "com.typesafe.play" %% "play-mailer" % "3.0.1"
|
||||||
|
|
||||||
|
libraryDependencies += "com.google.caliper" % "caliper" % "1.0-beta-2"
|
||||||
|
|
||||||
routesImport += "binders.QueryBinders._"
|
routesImport += "binders.QueryBinders._"
|
||||||
|
|
||||||
// Uncomment to use Akka
|
// Uncomment to use Akka
|
||||||
|
|||||||
Reference in New Issue
Block a user