Skip to content

Commit db69413

Browse files
[swiftsrc2cpg] Added support for binary .plist files
1 parent 7a79301 commit db69413

File tree

4 files changed

+110
-3
lines changed

4 files changed

+110
-3
lines changed

joern-cli/frontends/swiftsrc2cpg/build.sbt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ libraryDependencies ++= Seq(
2626
"io.shiftleft" %% "codepropertygraph" % Versions.cpg,
2727
"com.lihaoyi" %% "upickle" % Versions.upickle,
2828
// we want to use also Google Gson for its streaming abilities for very large Json files:
29-
"com.google.code.gson" % "gson" % Versions.gson,
29+
"com.google.code.gson" % "gson" % Versions.gson,
30+
// to handle property list files of various formats (i.e., binary and plain XML)
31+
"com.googlecode.plist" % "dd-plist" % "1.28",
3032
"org.scalatest" %% "scalatest" % Versions.scalatest % Test
3133
)
3234

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,47 @@
11
package io.joern.swiftsrc2cpg.passes
22

3+
import com.dd.plist.PropertyListParser
34
import io.joern.swiftsrc2cpg.Config
45
import io.joern.swiftsrc2cpg.utils.AstGenRunner
56
import io.joern.x2cpg.passes.frontend.XConfigFileCreationPass
67
import io.shiftleft.codepropertygraph.generated.Cpg
8+
import io.shiftleft.codepropertygraph.generated.nodes.NewConfigFile
9+
import io.shiftleft.semanticcpg.utils.FileUtil.*
10+
import io.shiftleft.utils.IOUtils
11+
import org.slf4j.LoggerFactory
712

813
import java.nio.file.Path
14+
import scala.util.{Failure, Success, Try}
915

1016
class ConfigFileCreationPass(cpg: Cpg, config: Config)
1117
extends XConfigFileCreationPass(
1218
cpg,
1319
config = config.withDefaultIgnoredFilesRegex(AstGenRunner.AstGenDefaultIgnoreRegex)
1420
) {
1521

16-
override val configFileFilters: List[Path => Boolean] = List(extensionFilter(".plist"), extensionFilter(".xib"))
22+
private val logger = LoggerFactory.getLogger(this.getClass)
1723

24+
private val PlistExt: String = ".plist"
25+
26+
override val configFileFilters: List[Path => Boolean] = List(extensionFilter(PlistExt), extensionFilter(".xib"))
27+
28+
override def runOnPart(diffGraph: DiffGraphBuilder, file: Path): Unit = {
29+
val contentMaybe = if (file.extension().contains(PlistExt)) {
30+
Try(PropertyListParser.parse(file.toFile).toXMLPropertyList)
31+
} else {
32+
Try(IOUtils.readEntireFile(file))
33+
}
34+
contentMaybe match {
35+
case Success(content) =>
36+
val configFileContent =
37+
s"""<!--This has been generated from ${file.toAbsolutePath}-->
38+
|$content""".stripMargin
39+
val name = configFileName(file)
40+
val configNode = NewConfigFile().name(name).content(configFileContent)
41+
logger.debug(s"Adding config file $name")
42+
diffGraph.addNode(configNode)
43+
case Failure(error) =>
44+
logger.warn(s"Unable to create config file node for ${file.toAbsolutePath}: $error")
45+
}
46+
}
1847
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package io.joern.swiftsrc2cpg.passes.config
2+
3+
import com.dd.plist.PropertyListConverter
4+
import flatgraph.DNode
5+
import io.joern.swiftsrc2cpg.Config
6+
import io.joern.swiftsrc2cpg.passes.ConfigFileCreationPass
7+
import io.shiftleft.codepropertygraph.generated.GraphSchema
8+
import io.shiftleft.codepropertygraph.generated.nodes.NewConfigFile
9+
import io.shiftleft.semanticcpg.utils.FileUtil
10+
import org.scalatest.funspec.AnyFunSpec
11+
import org.scalatest.matchers.should.Matchers
12+
13+
import java.nio.file.Files
14+
15+
class ConfigFileCreationPassTests extends AnyFunSpec with Matchers {
16+
17+
private class TestDiffGraphBuilder extends io.shiftleft.codepropertygraph.generated.DiffGraphBuilder(GraphSchema) {
18+
val nodes = scala.collection.mutable.Buffer[Any]()
19+
override def addNode(newNode: DNode): this.type = {
20+
this.nodes.append(newNode)
21+
this
22+
}
23+
}
24+
25+
private val xml: String =
26+
"""<?xml version="1.0" encoding="UTF-8"?>
27+
|<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
28+
|<plist version="1.0">
29+
|<dict>
30+
| <key>Example</key>
31+
| <string>value</string>
32+
|</dict>
33+
|</plist>""".stripMargin
34+
.replace("\n", System.lineSeparator())
35+
.replace(" ", "\t") // com.dd.plist.NSObject#toXMLPropertyList uses tabs when converting
36+
37+
describe("ConfigFileCreationPass") {
38+
it("creates config node for an XML plist") {
39+
FileUtil.usingTemporaryFile("test", ".plist") { xmlFile =>
40+
Files.write(xmlFile, xml.getBytes("UTF-8"))
41+
42+
val config = Config()
43+
val pass = new ConfigFileCreationPass(null /* cpg not used in here */, config)
44+
val diff = new TestDiffGraphBuilder
45+
46+
// run the pass on the XML file
47+
pass.runOnPart(diff, xmlFile)
48+
diff.nodes.nonEmpty shouldBe true
49+
val node = diff.nodes.collectFirst { case n: NewConfigFile => n }.get
50+
node.name should (startWith("test") and endWith(".plist"))
51+
node.content should (startWith("<!--This has been generated from") and endWith(xml))
52+
}
53+
}
54+
55+
it("creates config node for a binary plist") {
56+
FileUtil.usingTemporaryFile("testbin-src", ".plist") { xmlFile =>
57+
FileUtil.usingTemporaryFile("testbin", ".plist") { binFile =>
58+
Files.write(xmlFile, xml.getBytes("UTF-8"))
59+
PropertyListConverter.convertToBinary(xmlFile, binFile)
60+
61+
val config = Config()
62+
val pass = new ConfigFileCreationPass(null /* cpg not used in here */, config)
63+
val diff = new TestDiffGraphBuilder
64+
65+
pass.runOnPart(diff, binFile)
66+
67+
diff.nodes.nonEmpty shouldBe true
68+
val node = diff.nodes.collectFirst { case n: NewConfigFile => n }.get
69+
node.name should (startWith("testbin") and endWith(".plist"))
70+
// binary should have been converted to XML content:
71+
node.content should (startWith("<!--This has been generated from") and endWith(xml))
72+
}
73+
}
74+
}
75+
}
76+
}

joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/passes/frontend/XConfigFileCreationPass.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ abstract class XConfigFileCreationPass(cpg: Cpg, private val rootDir: Option[Str
5454
}
5555
}
5656

57-
private def configFileName(configFile: Path): String = {
57+
protected def configFileName(configFile: Path): String = {
5858
Try(Paths.get(rootDir.getOrElse(cpg.metaData.root.head)).toAbsolutePath)
5959
.map(_.relativize(configFile.toAbsolutePath).toString)
6060
.getOrElse(configFile.fileName)

0 commit comments

Comments
 (0)