Skip to content

Commit 0ea4a8a

Browse files
authored
integrate querydb to this repo to avoid cyclic dependency (#649)
* move querydb over here, integrate build.sbt * adjust path * maintainer config * cleanup/refactor createDistribution * drop log4j dep to avoid multiple logging implementations * joern/createDistribution: invoke and include querydb/createDistribution * WIP - old version of querydb/install.sh * WIP * update joernscan * github actions config: run querydb/createDistribution, upload querydb * readme * fmt
1 parent 4826c89 commit 0ea4a8a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+3061
-5
lines changed

.github/workflows/release.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,12 @@ jobs:
6767
asset_path: target/joern-cli.zip.sha512
6868
asset_name: joern-cli.zip.sha512
6969
asset_content_type: text/plain
70+
- name: Upload querydb.zip
71+
uses: actions/upload-release-asset@v1
72+
env:
73+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
74+
with:
75+
upload_url: ${{ steps.create_release.outputs.upload_url }}
76+
asset_path: querydb/target/querydb.zip
77+
asset_name: querydb.zip
78+
asset_content_type: application/zip

build.sbt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
name := "joern"
2-
organization := "io.joern"
2+
ThisBuild / organization := "io.joern"
33
ThisBuild / scalaVersion := "2.13.5"
44
// don't upgrade to 2.13.6 until https://github.com/com-lihaoyi/Ammonite/issues/1182 is resolved
5-
ThisBuild /Test /fork := true
5+
66
val cpgVersion = "1.3.388"
77
val ghidra2cpgVersion = "0.0.47"
88
val js2cpgVersion = "0.2.21"
99
val javasrc2cpgVersion = "0.0.14"
1010
val jimple2cpgVersion = "0.0.7"
1111

12+
ThisBuild /Test /fork := true
13+
1214
ThisBuild / resolvers ++= Seq(
1315
Resolver.mavenLocal,
1416
Resolver.jcenterRepo,
@@ -25,12 +27,15 @@ homepage := Some(url("https://joern.io/"))
2527
licenses := List("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0"))
2628

2729
lazy val joerncli = project.in(file("joern-cli"))
30+
lazy val querydb = project.in(file("querydb"))
2831

2932
lazy val createDistribution = taskKey[File]("Create a complete Joern distribution")
3033
createDistribution := {
3134
val distributionFile = file("target/joern-cli.zip")
3235
val zip = (joerncli/Universal/packageBin).value
36+
3337
IO.copyFile(zip, distributionFile)
38+
val querydbDistribution = (querydb/createDistribution).value
3439

3540
println(s"created distribution - resulting files: $distributionFile")
3641
distributionFile

joern-cli/src/main/scala/io/shiftleft/joern/JoernScan.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import scala.jdk.CollectionConverters._
1818
import scala.reflect.runtime.universe._
1919

2020
object JoernScanConfig {
21-
val defaultDbVersion: String = "0.0.108"
21+
val defaultDbVersion: String = "latest"
2222
}
2323

2424
case class JoernScanConfig(src: String = "",
@@ -213,9 +213,9 @@ object JoernScan extends App with BridgeBase {
213213

214214
private def urlForVersion(version: String): String = {
215215
if (version == "latest") {
216-
"https://github.com/joernio/query-database/releases/latest/download/querydb.zip"
216+
"https://github.com/joernio/joern/releases/latest/download/querydb.zip"
217217
} else {
218-
s"https://github.com/joernio/query-database/releases/download/v$version/querydb.zip"
218+
s"https://github.com/joernio/joern/releases/download/v$version/querydb.zip"
219219
}
220220
}
221221

querydb-install.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env bash
2+
set -o errexit
3+
set -o pipefail
4+
set -o nounset
5+
set -eu
6+
7+
if [ "$(uname)" = 'Darwin' ]; then
8+
# get script location
9+
# https://unix.stackexchange.com/a/96238
10+
if [ "${BASH_SOURCE:-x}" != 'x' ]; then
11+
this_script=$BASH_SOURCE
12+
elif [ "${ZSH_VERSION:-x}" != 'x' ]; then
13+
setopt function_argzero
14+
this_script=$0
15+
elif eval '[[ -n ${.sh.file} ]]' 2>/dev/null; then
16+
eval 'this_script=${.sh.file}'
17+
else
18+
echo 1>&2 "Unsupported shell. Please use bash, ksh93 or zsh."
19+
exit 2
20+
fi
21+
relative_directory=$(dirname "$this_script")
22+
SCRIPT_ABS_DIR=$(cd "$relative_directory" && pwd)
23+
else
24+
SCRIPT_ABS_PATH=$(readlink -f "$0")
25+
SCRIPT_ABS_DIR=$(dirname "$SCRIPT_ABS_PATH")
26+
fi
27+
28+
# Check required tools are installed.
29+
check_installed() {
30+
if ! type "$1" > /dev/null; then
31+
echo "Please ensure you have $1 installed."
32+
exit 1
33+
fi
34+
}
35+
36+
37+
# https://stackoverflow.com/a/28085062
38+
: ${DEFAULT_JOERN_INSTALL_DIR:=$PWD/joern-cli/target/universal/stage}
39+
echo "where is the joern distribution installed?
40+
note: you can e.g. create one locally using 'sbt stage'
41+
[$DEFAULT_JOERN_INSTALL_DIR]:"
42+
read JOERN_INSTALL_DIR
43+
if [ -z "$JOERN_INSTALL_DIR" ]; then
44+
JOERN_INSTALL_DIR=$DEFAULT_JOERN_INSTALL_DIR
45+
fi
46+
47+
echo "building the plugin"
48+
sbt querydb/createDistribution
49+
readonly QUERYDB_ZIP=$PWD/querydb/target/querydb.zip
50+
51+
echo "Installing plugin"
52+
pushd $JOERN_INSTALL_DIR
53+
./joern --remove-plugin querydb
54+
./joern --add-plugin $QUERYDB_ZIP
55+
popd

querydb/README.md

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# Joern Query Database ("Joern-Scan")
2+
3+
This is the central query database for the open-source code analysis
4+
platform [Joern](https://github.com/ShiftLeftSecurity/joern). It has
5+
two purposes:
6+
7+
* It provides the batteries required to turn Joern into a ready-to-run code scanning tool.
8+
* Its queries serve as examples useful for those looking to write their own queries.
9+
* [built on JDK11](https://github.com/ShiftLeftSecurity/overflowdb/blob/master/.github/workflows/release.yml) but runs on JRE >= 1.8
10+
11+
The query database is distributed as a standalone library that
12+
includes Joern as a dependency. This means that it is not necessary to
13+
install Joern to make use of the queries in the database.
14+
15+
At the same time, the database is a Joern extension, that is, when
16+
dynamically loaded at startup, its functionality becomes available on
17+
the interactive Joern shell and in Joern scripts.
18+
19+
You can fork this project to build your own custom queries and
20+
scanners or kindly send a PR to to this repo to have them considered
21+
for inclusion in the default distribution.
22+
23+
## Installing and running
24+
25+
The installation script downloads joern and installs it in a sub-directory.
26+
The query database is installed as an extension.
27+
28+
```
29+
./install.sh
30+
```
31+
32+
You can run all queries as follows:
33+
34+
```
35+
./joern-scan path/to/code
36+
```
37+
38+
For example,
39+
40+
```
41+
mkdir foo
42+
echo "int foo(int a, int b, int c, int d, int e, int f) {}" > foo/foo.c
43+
./joern-scan foo
44+
```
45+
46+
runs all queries on the sample code in the directory `foo`, determining that the function `foo`
47+
has too many parameters.
48+
49+
## Adding your own queries
50+
51+
Please follow the rules below for a tear-free query writing experience:
52+
53+
* Queries in the package `io.joern.scanners` are picked up automatically at runtime,
54+
so please put your queries there.
55+
* Each query must begin with the annotation `@q` and must be placed in a query bundle.
56+
A query bundle is simply an `object` that derives from `QueryBundle`
57+
* Queries can have parameters,but you must provide a default value for each parameter
58+
* Please add unit tests for queries. These also serve as a spec for what your query does.
59+
* Please format the code before sending a PR using `sbt scalafmt` and `sbt test:scalafmt`
60+
61+
Take a look at the query bundle `Metrics` at `src/main/scala/io/joern/scanners/c/Metrics.scala`
62+
as an example:
63+
64+
```
65+
object Metrics extends QueryBundle {
66+
67+
@q
68+
def tooManyParameters(n: Int = 4): Query =
69+
Query.make(
70+
name = "too-many-params",
71+
author = Crew.fabs,
72+
title = s"Number of parameters larger than $n",
73+
description = s"This query identifies functions with more than $n formal parameters",
74+
score = 1.0,
75+
withStrRep({ cpg =>
76+
cpg.method.internal.filter(_.parameter.size > n)
77+
}),
78+
tags = List(QueryTags.metrics)
79+
)
80+
81+
@q
82+
def tooHighComplexity(n: Int = 4): Query =
83+
Query.make(
84+
name = "too-high-complexity",
85+
author = Crew.fabs,
86+
title = s"Cyclomatic complexity higher than $n",
87+
description = s"This query identifies functions with a cyclomatic complexity higher than $n",
88+
score = 1.0,
89+
withStrRep({ cpg =>
90+
cpg.method.internal.filter(_.controlStructure.size > n)
91+
}),
92+
tags = List(QueryTags.metrics)
93+
)
94+
...
95+
}
96+
```
97+
98+
Corresponding tests for queries are located in
99+
`src/test/scala/io/joern/scanners`. For example, tests for the metrics
100+
queries are located in
101+
`src/test/scala/io/joern/scanners/c/MetricsTests.scala`:
102+
103+
```
104+
class MetricsTests extends Suite {
105+
106+
override val code = """
107+
int too_many_params(int a, int b, int c, int d, int e) {
108+
}
109+
...
110+
"""
111+
112+
"find functions with too many parameters" in {
113+
Metrics.tooManyParameters(4)(cpg).map(_.evidence) match {
114+
case List(List(method: nodes.Method)) =>
115+
method.name shouldBe "too_many_params"
116+
case _ => fail
117+
}
118+
}
119+
...
120+
}
121+
```
122+
123+
These tests can be run individually from the IntelliJ IDE during query
124+
development.
125+
126+
## Building/Testing the database
127+
128+
We use the Scala Build Tool (sbt). Please make sure you have sbt
129+
installed. The version does not matter as sbt will fetch the required
130+
version based on the build file (`build.sbt`).
131+
132+
Once `sbt` is installed, you can build and test the database as
133+
follows:
134+
135+
```
136+
sbt test
137+
```
138+
139+
You can test newly developed queries
140+
141+
If you want to test newly created queries with `joern-scan` as follows:
142+
143+
```
144+
sbt joerncli/stage
145+
./querydb-install.sh && ./joern-scan <src>
146+
```
147+
148+
## Exporting the database to JSON
149+
150+
After running `install.sh`, you can launch
151+
```
152+
./joern-scan --dump
153+
```
154+
to create a file named `querydb.json` that contains the list of all available queries
155+
along with its meta information.

querydb/build.sbt

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name := "querydb"
2+
3+
enablePlugins(JavaAppPackaging)
4+
5+
libraryDependencies ++= Seq(
6+
"com.lihaoyi" %% "sourcecode" % "0.1.9",
7+
"com.lihaoyi" %% "upickle" % "1.2.2",
8+
"com.github.pathikrit" %% "better-files" % "3.8.0",
9+
"com.github.scopt" %% "scopt" % "3.7.1",
10+
"io.joern" %% "ghidra2cpg" % Versions.ghidra2cpg,
11+
"io.shiftleft" %% "semanticcpg" % Versions.cpg,
12+
"io.shiftleft" %% "console" % Versions.cpg,
13+
"io.shiftleft" %% "dataflowengineoss" % Versions.cpg,
14+
"io.shiftleft" %% "fuzzyc2cpg-tests" % Versions.cpg % Test classifier "tests",
15+
"io.shiftleft" %% "c2cpg-tests" % Versions.cpg % Test classifier "tests",
16+
"io.shiftleft" %% "semanticcpg-tests" % Versions.cpg % Test classifier "tests",
17+
"io.shiftleft" %% "fuzzyc2cpg" % Versions.cpg % Test,
18+
"io.shiftleft" %% "c2cpg" % Versions.cpg % Test,
19+
"org.scalatest" %% "scalatest" % "3.1.1" % Test,
20+
"io.joern" %% "ghidra2cpg-tests" % Versions.ghidra2cpg % Test classifier "tests"
21+
)
22+
23+
Compile/doc/sources := Seq.empty
24+
Compile/packageDoc/publishArtifact := false
25+
26+
topLevelDirectory := Some(name.value)
27+
28+
lazy val createDistribution = taskKey[File]("Create binary distribution of extension")
29+
createDistribution := {
30+
import better.files._
31+
val pkgBin = (Universal/packageBin).value
32+
val tmpDstArchive = "/tmp/querydb.zip"
33+
val dstArchive = (target.value / "querydb.zip").toScala
34+
if (dstArchive.exists) dstArchive.delete()
35+
IO.copy(
36+
List((pkgBin, file(tmpDstArchive))),
37+
CopyOptions(overwrite = true, preserveLastModified = true, preserveExecutable = true)
38+
)
39+
40+
File.usingTemporaryDirectory("querydb") { dir =>
41+
File(tmpDstArchive).unzipTo(dir)
42+
dir.listRecursively.filter { x =>
43+
val name = x.toString
44+
name.contains("org.scala") ||
45+
name.contains("net.sf.trove4") ||
46+
name.contains("com.google.guava") ||
47+
name.contains("org.apache.logging") ||
48+
name.contains("com.google.protobuf") ||
49+
name.contains("com.lihaoyi") ||
50+
name.contains("io.shiftleft") ||
51+
name.contains("org.typelevel") ||
52+
name.contains("io.undertow") ||
53+
name.contains("com.chuusai") ||
54+
name.contains("io.get-coursier") ||
55+
name.contains("io.circe") ||
56+
name.contains("net.java.dev") ||
57+
name.contains("com.github.javaparser") ||
58+
name.contains("org.javassist") ||
59+
name.contains("com.lihaoyi.ammonite") ||
60+
name.contains("io.joern.ghidra2cpg") ||
61+
name.contains("net.oneandone")
62+
}.foreach(_.delete())
63+
dir.zipTo(dstArchive)
64+
File(tmpDstArchive).delete()
65+
}
66+
println(s"created distribution - resulting files: $dstArchive")
67+
68+
dstArchive.toJava
69+
}
70+
71+
Compile/scalacOptions ++= Seq(
72+
"-Xfatal-warnings",
73+
"-feature",
74+
"-deprecation",
75+
"-language:implicitConversions",
76+
)
77+
78+
fork := true
79+
80+
maintainer := "[email protected]"

0 commit comments

Comments
 (0)