Skip to content

Commit

Permalink
joern-slice: Quality Improvements (#2323)
Browse files Browse the repository at this point in the history
* Added `joern-slice` symlink to `joern_install.sh`
* Check extension before appending to output file
* Joern slice can generate CPG if given code
  • Loading branch information
DavidBakerEffendi authored Feb 28, 2023
1 parent b8ed0de commit e8dbf06
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 16 deletions.
25 changes: 15 additions & 10 deletions joern-cli/src/main/scala/io/joern/joerncli/JoernParse.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.joern.joerncli

import better.files.File
import io.joern.console.cpgcreation.{cpgGeneratorForLanguage, guessLanguage, CpgGenerator}
import io.joern.console.cpgcreation.{CpgGenerator, cpgGeneratorForLanguage, guessLanguage}
import io.joern.console.{FrontendConfig, InstallConfig}
import io.joern.joerncli.CpgBasedTool.newCpgCreatedString
import io.shiftleft.codepropertygraph.generated.Languages
Expand Down Expand Up @@ -71,20 +71,25 @@ object JoernParse {

parseConfig(parserArgs) match {
case Right(config) =>
if (config.listLanguages) {
Right(buildLanguageList())
} else
for {
_ <- checkInputPath(config)
language <- getLanguage(config)
_ <- generateCpg(installConfig, frontendArgs, config, language)
_ <- applyDefaultOverlays(config)
} yield newCpgCreatedString(config.outputCpgFile)
if (config.listLanguages) Right(buildLanguageList())
else run(config, frontendArgs, installConfig)

case Left(err) => Left(err)
}
}

def run(
config: ParserConfig,
frontendArgs: List[String] = List.empty,
installConfig: InstallConfig = InstallConfig()
): Either[String, String] =
for {
_ <- checkInputPath(config)
language <- getLanguage(config)
_ <- generateCpg(installConfig, frontendArgs, config, language)
_ <- applyDefaultOverlays(config)
} yield newCpgCreatedString(config.outputCpgFile)

private def checkInputPath(config: ParserConfig): Either[String, Unit] = {

if (config.inputPath == "") {
Expand Down
37 changes: 31 additions & 6 deletions joern-cli/src/main/scala/io/joern/joerncli/JoernSlice.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.joern.joerncli
import better.files.File
import io.circe.generic.auto._
import io.circe.syntax.EncoderOps
import io.joern.joerncli.JoernParse.ParserConfig
import io.joern.joerncli.slicing._
import io.shiftleft.codepropertygraph.Cpg
import io.shiftleft.semanticcpg.language._
Expand All @@ -26,7 +27,7 @@ object JoernSlice {
scopt.Read.reads(SliceMode withName)

case class Config(
cpgFileName: File = File("cpg.bin"),
inputPath: File = File("cpg.bin"),
outFile: File = File("slices"),
sliceMode: SliceModes = DataFlow,
sourceFile: Option[String] = None,
Expand All @@ -36,7 +37,10 @@ object JoernSlice {

def main(args: Array[String]): Unit = {
parseConfig(args).foreach { config =>
Using.resource(CpgBasedTool.loadFromOdb(config.cpgFileName.pathAsString)) { cpg =>
val inputCpgPath =
if (config.inputPath.isDirectory) generateTempCpg(config)
else config.inputPath.pathAsString
Using.resource(CpgBasedTool.loadFromOdb(inputCpgPath)) { cpg =>
val slice: ProgramSlice = config.sliceMode match {
case DataFlow => DataFlowSlicing.calculateDataFlowSlice(cpg, config)
case Usages => UsageSlicing.calculateUsageSlice(cpg, config)
Expand All @@ -46,6 +50,20 @@ object JoernSlice {
}
}

private def generateTempCpg(config: Config): String = {
val tmpFile = File.newTemporaryFile("joern-slice", ".bin")
println(s"Generating CPG from code at ${config.inputPath.pathAsString}")
(JoernParse.run(ParserConfig(config.inputPath.pathAsString, outputCpgFile = tmpFile.pathAsString)) match {
case Right(_) =>
println(s"Temporary CPG has been successfully generated at ${tmpFile.pathAsString}")
Right(tmpFile.deleteOnExit(swallowIOExceptions = true).pathAsString)
case x => x
}) match {
case Left(err) => throw new RuntimeException(err)
case Right(path) => path
}
}

private def parseConfig(args: Array[String]): Option[Config] =
new scopt.OptionParser[Config]("joern-slice") {
head("Extract intra-procedural slices from the CPG.")
Expand All @@ -56,7 +74,7 @@ object JoernSlice {
.action { (x, c) =>
val path = File(x)
if (!path.isRegularFile) failure(s"File at '$x' not found or not regular, e.g. a directory.")
c.copy(cpgFileName = path)
c.copy(inputPath = path)
}
opt[String]('o', "out")
.text("the output file to write slices to - defaults to `slices`. The file is suffixed based on the mode.")
Expand Down Expand Up @@ -99,16 +117,23 @@ object JoernSlice {
}
}

programSlice match {
def normalizePath(path: String, ext: String): String =
if (path.endsWith(ext)) path
else path + ext

val finalOutputPath = programSlice match {
case ProgramDataFlowSlice(dataFlowSlices) =>
val sliceCpg = File(outFile.pathAsString + ".cpg").createFileIfNotExists()
val sliceCpg = File(normalizePath(outFile.pathAsString, ".cpg")).createFileIfNotExists()
Using.resource(Cpg.withStorage(sliceCpg.pathAsString)) { newCpg =>
storeDataFlowSlices(newCpg, dataFlowSlices.flatMap(_._2).toSet)
}
sliceCpg.pathAsString
case programUsageSlice: ProgramUsageSlice =>
val sliceCpg = File(outFile.pathAsString + ".json").createFileIfNotExists()
val sliceCpg = File(normalizePath(outFile.pathAsString, ".json")).createFileIfNotExists()
sliceCpg.write(programUsageSlice.asJson.spaces2)
sliceCpg.pathAsString
}
println(s"Slices have been successfully generated and written to $finalOutputPath")
}

}
1 change: 1 addition & 0 deletions joern-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ else
sudo ln -sf "$JOERN_INSTALL_DIR"/joern-cli/joern-flow "$JOERN_LINK_DIR" || true
sudo ln -sf "$JOERN_INSTALL_DIR"/joern-cli/joern-scan "$JOERN_LINK_DIR" || true
sudo ln -sf "$JOERN_INSTALL_DIR"/joern-cli/joern-stats "$JOERN_LINK_DIR" || true
sudo ln -sf "$JOERN_INSTALL_DIR"/joern-cli/joern-slice "$JOERN_LINK_DIR" || true
fi
fi

Expand Down

0 comments on commit e8dbf06

Please sign in to comment.