Skip to content

Commit 47941fb

Browse files
[joern-scan] Propagate scan defaults into REPL and Scan (#5855)
1 parent ed8bb13 commit 47941fb

File tree

4 files changed

+61
-5
lines changed

4 files changed

+61
-5
lines changed

console/src/main/scala/io/joern/console/BridgeBase.scala

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ case class Config(
3737
verbose: Boolean = false,
3838
dependencies: Seq[String] = Seq.empty,
3939
resolvers: Seq[String] = Seq.empty,
40-
maxHeight: Option[Int] = None
40+
maxHeight: Option[Int] = None,
41+
scanMaxCallDepth: Option[Int] = None,
42+
scanScriptNames: Array[String] = Array.empty,
43+
scanTagNames: Array[String] = Array.empty
4144
)
4245

4346
/** Base class for ReplBridge, split by topic into multiple self types.
@@ -250,6 +253,19 @@ trait BridgeBase extends InteractiveShell with ScriptExecution with PluginHandli
250253
builder += s"""openForInputPath("$path")""".stripMargin
251254
}
252255
builder ++= config.runBefore.map(code => escapeForWindows(code, isInteractive))
256+
257+
if (config.pluginToRun.contains("scan")) {
258+
builder += "import io.joern.joerncli.Scan"
259+
builder += s"""Scan.defaultOpts.names = Array(${escapeForWindows(
260+
config.scanScriptNames.map(n => s""""$n"""").mkString(", "),
261+
isInteractive
262+
)})"""
263+
builder += s"""Scan.defaultOpts.tags = Array(${escapeForWindows(
264+
config.scanTagNames.map(n => s""""$n"""").mkString(", "),
265+
isInteractive
266+
)})"""
267+
builder += config.scanMaxCallDepth.map(d => s"Scan.defaultOpts.maxCallDepth = $d").getOrElse("")
268+
}
253269
builder.result()
254270
}
255271

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import io.joern.joerncli.JoernScan.getQueriesFromQueryDb
88
import io.joern.joerncli.Scan.{allTag, defaultTag}
99
import io.joern.joerncli.console.ReplBridge
1010
import io.shiftleft.codepropertygraph.generated.Languages
11-
import io.shiftleft.semanticcpg.language.{DefaultNodeExtensionFinder, NodeExtensionFinder, locationCreator}
11+
import io.shiftleft.semanticcpg.language.locationCreator
1212
import io.shiftleft.semanticcpg.layers.{LayerCreator, LayerCreatorContext, LayerCreatorOptions}
1313
import io.shiftleft.semanticcpg.utils.FileUtil
1414
import io.shiftleft.semanticcpg.utils.FileUtil.*
@@ -174,7 +174,10 @@ object JoernScan extends BridgeBase {
174174
overwrite = config.overwrite,
175175
store = config.store,
176176
language = config.language,
177-
frontendArgs = frontendArgs.toArray
177+
frontendArgs = frontendArgs.toArray,
178+
scanMaxCallDepth = Some(Scan.defaultOpts.maxCallDepth),
179+
scanScriptNames = Scan.defaultOpts.names,
180+
scanTagNames = Scan.defaultOpts.tags
178181
)
179182
run(shellConfig)
180183
println(s"Run `joern --for-input-path ${config.src}` to explore interactively")
@@ -263,12 +266,17 @@ class Scan(options: ScanOptions)(implicit engineContext: EngineContext) extends
263266
override val description: String = Scan.description
264267

265268
override def create(context: LayerCreatorContext): Unit = {
266-
val allQueries = getQueriesFromQueryDb(new JoernDefaultArgumentProvider(options.maxCallDepth))
269+
val maxCallDepth =
270+
if (options.maxCallDepth == 2 && Scan.defaultOpts.maxCallDepth != 2) Scan.defaultOpts.maxCallDepth
271+
else options.maxCallDepth
272+
val allQueries = getQueriesFromQueryDb(new JoernDefaultArgumentProvider(maxCallDepth))
267273
if (allQueries.isEmpty) {
268274
println("No queries found, you probably forgot to install a query database.")
269275
return
270276
}
271-
val queriesAfterFilter = filteredQueries(allQueries, options.names, options.tags)
277+
val names = if (options.names.isEmpty) Scan.defaultOpts.names else options.names
278+
val tags = if (options.tags.isEmpty) Scan.defaultOpts.tags else options.tags
279+
val queriesAfterFilter = filteredQueries(allQueries, names, tags)
272280
if (queriesAfterFilter.isEmpty) {
273281
println("No queries matched current filter selection (total number of queries: `" + allQueries.length + "`)")
274282
return

testDistro.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ def scan_test(self):
211211

212212
# Create test file
213213
(foo_path / "foo.c").write_text("int foo(int a, int b, int c, int d, int e, int f) {}")
214+
215+
# Use test file for joern-scan parameter test
216+
uaf_c_path = self.script_dir / "tests" / "code" / "c" / "uaf.c"
214217

215218
# Run tests
216219
self.run_command([self.joern_exe, "--src", str(foo_path), "--run", "scan"],
@@ -222,6 +225,16 @@ def scan_test(self):
222225
self.run_command([self.joern_scan_exe, "--dump"],
223226
"Joern-scan dump")
224227

228+
# Run scan on real test file to verify functionality
229+
proc = self.run_command([self.joern_scan_exe, "--overwrite", "--tags", "all", str(uaf_c_path)],
230+
f"Joern-scan on {uaf_c_path}")
231+
232+
expected_result = "Result: 5.0 : A value that is free'd is reused without reassignment.: uaf.c:7:bad"
233+
234+
result_count = sum(1 for line in proc.stdout.splitlines() if expected_result in line)
235+
if result_count == 0:
236+
raise RuntimeError("Result from joern-scan not found")
237+
225238
@stage
226239
def slice_test(self):
227240
"""Test slicing functionality"""

tests/code/c/uaf.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <stddef.h>
2+
#include <stdlib.h>
3+
4+
void *bad() {
5+
void *x = NULL;
6+
free(x);
7+
return x;
8+
}
9+
10+
void *false_positive() {
11+
void *x = NULL;
12+
free(x);
13+
x = NULL;
14+
return x;
15+
}
16+
17+
int main(int argc, char * argv[]){
18+
bad();
19+
}

0 commit comments

Comments
 (0)