|
| 1 | +package io.shiftleft.console |
| 2 | + |
| 3 | +import io.shiftleft.codepropertygraph.Cpg |
| 4 | +import io.shiftleft.codepropertygraph.generated.{NodeTypes, nodes} |
| 5 | +import overflowdb.traversal._ |
| 6 | +import io.shiftleft.semanticcpg.language._ |
| 7 | + |
| 8 | +package object scan { |
| 9 | + |
| 10 | + implicit class ScannerStarters(val cpg: Cpg) extends AnyVal { |
| 11 | + def finding: Traversal[nodes.Finding] = |
| 12 | + cpg.graph.nodes(NodeTypes.FINDING).cast[nodes.Finding] |
| 13 | + } |
| 14 | + |
| 15 | + implicit class QueryWrapper(q: Query) { |
| 16 | + |
| 17 | + /** |
| 18 | + * Obtain list of findings by running query on CPG |
| 19 | + * */ |
| 20 | + def apply(cpg: Cpg): List[nodes.NewFinding] = { |
| 21 | + q.f(cpg) |
| 22 | + .map( |
| 23 | + evidence => |
| 24 | + finding(evidence = evidence, |
| 25 | + name = q.name, |
| 26 | + author = q.author, |
| 27 | + title = q.title, |
| 28 | + description = q.description, |
| 29 | + score = q.score)) |
| 30 | + .l |
| 31 | + } |
| 32 | + } |
| 33 | + |
| 34 | + private object FindingKeys { |
| 35 | + val name = "name" |
| 36 | + val author = "author" |
| 37 | + val title = "title" |
| 38 | + val description = "description" |
| 39 | + val score = "score" |
| 40 | + } |
| 41 | + |
| 42 | + implicit class ScannerFindingStep(val traversal: Traversal[nodes.Finding]) extends AnyRef { |
| 43 | + |
| 44 | + def name: Traversal[String] = traversal.map(_.name) |
| 45 | + |
| 46 | + def author: Traversal[String] = traversal.map(_.author) |
| 47 | + |
| 48 | + def title: Traversal[String] = traversal.map(_.title) |
| 49 | + |
| 50 | + def description: Traversal[String] = traversal.map(_.description) |
| 51 | + |
| 52 | + def score: Traversal[Double] = traversal.map(_.score) |
| 53 | + |
| 54 | + } |
| 55 | + |
| 56 | + implicit class ScannerFindingExtension(val node: nodes.Finding) extends AnyRef { |
| 57 | + |
| 58 | + def name: String = getValue(FindingKeys.name) |
| 59 | + |
| 60 | + def author: String = getValue(FindingKeys.author) |
| 61 | + |
| 62 | + def title: String = getValue(FindingKeys.title) |
| 63 | + |
| 64 | + def description: String = getValue(FindingKeys.description) |
| 65 | + |
| 66 | + def score: Double = getValue(FindingKeys.score).toDouble |
| 67 | + |
| 68 | + protected def getValue(key: String, default: String = ""): String = |
| 69 | + node.keyValuePairs.find(_.key == key).map(_.value).getOrElse(default) |
| 70 | + |
| 71 | + } |
| 72 | + |
| 73 | + private def finding(evidence: nodes.StoredNode, |
| 74 | + name: String, |
| 75 | + author: String, |
| 76 | + title: String, |
| 77 | + description: String, |
| 78 | + score: Double): nodes.NewFinding = { |
| 79 | + nodes.NewFinding( |
| 80 | + evidence = List(evidence), |
| 81 | + keyValuePairs = List( |
| 82 | + nodes.NewKeyValuePair(FindingKeys.name, name), |
| 83 | + nodes.NewKeyValuePair(FindingKeys.author, author), |
| 84 | + nodes.NewKeyValuePair(FindingKeys.title, title), |
| 85 | + nodes.NewKeyValuePair(FindingKeys.description, description), |
| 86 | + nodes.NewKeyValuePair(FindingKeys.score, score.toString) |
| 87 | + ) |
| 88 | + ) |
| 89 | + } |
| 90 | + |
| 91 | + /** |
| 92 | + * Print human readable list of findings to standard out. |
| 93 | + * */ |
| 94 | + def outputFindings(cpg: Cpg): Unit = { |
| 95 | + cpg.finding.sortBy(_.score.toInt).foreach { finding => |
| 96 | + val evidence = finding.evidence.headOption |
| 97 | + .map { e => |
| 98 | + s"${e.location.filename}:${e.location.lineNumber.getOrElse(0)}:${e.location.methodFullName}" |
| 99 | + } |
| 100 | + .getOrElse("") |
| 101 | + println(s"Result: ${finding.score} : ${finding.title}: $evidence") |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | +} |
0 commit comments