Skip to content

Commit

Permalink
[sarif] Sarif Conversion from Finding Node (#5256)
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidBakerEffendi authored Jan 28, 2025
1 parent 5ef163c commit e2d410d
Show file tree
Hide file tree
Showing 13 changed files with 1,028 additions and 1 deletion.
5 changes: 4 additions & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,14 @@ jobs:
./joern --src /tmp/foo --run scan
./joern-scan /tmp/foo
./joern-scan --dump
- run: |
- name: Joern Slice Testing
run: |
mkdir /tmp/slice
./joern-slice data-flow tests/code/javasrc/SliceTest.java -o /tmp/slice/dataflow-slice-javasrc.json
echo "checking that the script output contains the content we expect:"
./joern --script "tests/test-dataflow-slice.sc" --param sliceFile=/tmp/slice/dataflow-slice-javasrc.json | grep 'List(boolean b, b, this, s, "MALICIOUS", s, new Foo("MALICIOUS"), s, s, "SAFE", s, b, this, this, b, s, System.out)'
- name: SARIF Export Testing
run: ./tests/finding-to-sarif-test.sh
- run: |
cd joern-cli/target/universal/stage
./schema-extender/test.sh
3 changes: 3 additions & 0 deletions console/src/main/scala/io/joern/console/BridgeBase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ trait BridgeBase extends InteractiveShell with ScriptExecution with PluginHandli
builder += s"""openForInputPath("$name")""".stripMargin
}
builder ++= config.runBefore
builder ++= "import _root_.io.shiftleft.semanticcpg.sarif.SarifConfig"
:: "implicit val sarifConfig: SarifConfig = SarifConfig(semanticVersion = version)"
:: Nil
builder.result()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.shiftleft.semanticcpg.language

import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.codepropertygraph.generated.help.Doc
import io.shiftleft.codepropertygraph.generated.nodes.Finding
import io.shiftleft.semanticcpg.sarif.SarifConfig.SarifVersion
import io.shiftleft.semanticcpg.sarif.SarifSchema.{Sarif, Sarif2_1_0}
import io.shiftleft.semanticcpg.sarif.{SarifConfig, SarifSchema, v2_1_0}
import org.json4s.Formats
import org.json4s.native.Serialization.{write, writePretty}

import java.net.URI

/** Converts findings written to the CPG to the SARIF format.
*
* @param traversal
* the findings
* @see
* https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
*/
class SarifExtension(val traversal: Iterator[Finding]) extends AnyVal {

@Doc(info = "execute this traversal and convert findings to SARIF format")
def toSarif(implicit config: SarifConfig = SarifConfig()): Sarif = {

def generateSarif(results: List[SarifSchema.Result], baseUri: Option[URI]): Sarif = {
config.sarifVersion match {
case SarifVersion.V2_1_0 =>
val tool = v2_1_0.Schema.ToolComponent(
name = config.toolName,
fullName = config.toolFullName,
organization = config.organization,
semanticVersion = config.semanticVersion,
informationUri = config.toolInformationUri
)
val projectBaseUri = Map(
"PROJECT_ROOT" -> v2_1_0.Schema
.ArtifactLocation(uriBaseId = baseUri.map(_.toString).orElse(Option("<empty>")))
)
val runs = v2_1_0.Schema.Run(
tool = v2_1_0.Schema.Tool(driver = tool),
originalUriBaseIds = projectBaseUri,
results = results
) :: Nil
Sarif2_1_0(runs = runs)
}
}

traversal.l match {
case Nil => generateSarif(results = Nil, baseUri = None)
case findings @ head :: _ =>
val baseUri = Cpg(head.graph).metaData.root.headOption.map(java.io.File(_).toURI)
val results = findings.map(config.resultConverter.convertFindingToResult)
generateSarif(results, baseUri)
}

}

@Doc(info = "execute this traversal and convert findings to SARIF format as JSON")
def toSarifJson(pretty: Boolean = false)(implicit config: SarifConfig = SarifConfig()): String = {
implicit val formats: Formats = org.json4s.DefaultFormats ++ config.customSerializers

val results = toSarif
if (pretty) writePretty(results)
else write(results)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import flatgraph.help.DocSearchPackages
import io.shiftleft.codepropertygraph.generated
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.codepropertygraph.generated.nodes.*
import io.shiftleft.semanticcpg.language.SarifExtension
import io.shiftleft.semanticcpg.language.bindingextension.{
MethodTraversal as BindingMethodTraversal,
TypeDeclTraversal as BindingTypeDeclTraversal
Expand Down Expand Up @@ -263,6 +264,9 @@ package object language
new MethodParameterOutTraversal(Iterator.single(a))

}

implicit def singleToSarifTraversal[A <: Finding](a: A): SarifExtension = new SarifExtension(Iterator.single(a))
implicit def iterOnceToSarifTraversal[A <: Finding](a: IterableOnce[A]): SarifExtension = new SarifExtension(a)
}

trait LowPrioImplicits {
Expand Down Expand Up @@ -290,4 +294,5 @@ trait LowPrioImplicits {
new DeclarationTraversal[A](Iterator.single(a))
implicit def iterOnceToDeclarationNodeTraversal[A <: Declaration](a: IterableOnce[A]): DeclarationTraversal[A] =
new DeclarationTraversal[A](a.iterator)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.shiftleft.semanticcpg.sarif

import io.shiftleft.semanticcpg.sarif.SarifConfig.SarifVersion
import io.shiftleft.semanticcpg.sarif.v2_1_0.JoernScanResultToSarifConverter
import org.json4s.Serializer

import java.net.URI

/** A configuration for tool-specific information and arguments on transforming how findings are to be converted to
* SARIF.
*
* @param toolName
* The name of the tool component.
* @param toolFullName
* The name of the tool component along with its version and any other useful identifying information, such as its
* locale.
* @param toolInformationUri
* The absolute URI at which information about this version of the tool component can be found.
* @param organization
* The organization or company that produced the tool component.
* @param semanticVersion
* The tool component version in the format specified by Semantic Versioning 2.0.
* @param sarifVersion
* The SARIF format version of the resulting log file.
* @param resultConverter
* A transformer class to map from Finding nodes to a SARIF `Result`.
* @param customSerializers
* Additional JSON serializers for any additional properties for [[io.shiftleft.semanticcpg.sarif.Sarif]] derived
* classes.
*/
case class SarifConfig(
toolName: String = "Joern",
toolFullName: String = "Joern - The Bug Hunter's Workbench",
toolInformationUri: URI = URI("https://joern.io"),
organization: String = "Joern.io",
semanticVersion: String = "0.0.1",
sarifVersion: SarifVersion = SarifVersion.V2_1_0,
resultConverter: ScanResultToSarifConverter = JoernScanResultToSarifConverter(),
customSerializers: List[Serializer[?]] = SarifSchema.serializers
)

object SarifConfig {

enum SarifVersion {
case V2_1_0
}

}
Loading

0 comments on commit e2d410d

Please sign in to comment.