Skip to content

Commit 98a943c

Browse files
committed
Add 'CallGraphFromTo` queries for supported langs
Adds a 'CallGraphFromTo.ql' query, associated 'CallGraphFromTo.md' documentation, and query unit tests for all currently supported code languages. Incorporates and extends the "CallGraphFromTo" query concept from GitHubSecurityLab's seclab-taskflow-agent.
1 parent 3487416 commit 98a943c

File tree

40 files changed

+1198
-0
lines changed

40 files changed

+1198
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# CallGraphFromTo for C++
2+
3+
Displays calls on reachable paths from a source function to a target function, showing transitive call graph connectivity.
4+
5+
## Overview
6+
7+
This query identifies all function calls that lie on any transitive call path from a specified source function to a specified target function. Given both a source and target function name, it reports each call site along the connecting paths, which is useful for understanding indirect call chains, security-relevant data flow paths, and function reachability.
8+
9+
The query uses transitive closure (`calls*`) to determine reachability, then reports only the direct call sites that contribute to paths between the source and target. It accepts function names via external predicates (`sourceFunction` and `targetFunction`) and supports both simple and qualified name matching.
10+
11+
## Use Cases
12+
13+
This query is primarily used for:
14+
15+
- Determining if a call path exists between two functions
16+
- Mapping indirect call chains from a source to a target function
17+
- Analyzing security-relevant paths (e.g., from user input handlers to sensitive operations)
18+
- Understanding transitive dependencies between functions
19+
20+
## Example
21+
22+
The following C++ code demonstrates a transitive call chain from `source` through `intermediate` to `target`:
23+
24+
```cpp
25+
void target() {}
26+
27+
void intermediate() {
28+
target();
29+
}
30+
31+
void source() {
32+
intermediate();
33+
}
34+
```
35+
36+
Running with `sourceFunction = "source"` and `targetFunction = "target"` produces results showing each call site on the path with the message pattern ``Reachable call from `source`to`intermediate```.
37+
38+
## Output Format
39+
40+
The query is a `@kind problem` query producing rows of:
41+
42+
- ``select call, "Reachable call from `caller` to `callee`"``
43+
44+
## References
45+
46+
- [C++ Functions](https://en.cppreference.com/w/cpp/language/functions)
47+
- [CodeQL Call Graph Analysis](https://codeql.github.com/docs/writing-codeql-queries/about-data-flow-analysis/)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* @name Call Graph From To for cpp
3+
* @description Displays calls on reachable paths from a source function to a target function, showing transitive call graph connectivity.
4+
* @id cpp/tools/call-graph-from-to
5+
* @kind problem
6+
* @problem.severity recommendation
7+
* @tags call-graph
8+
*/
9+
10+
import cpp
11+
12+
/**
13+
* Gets the source function name for call graph reachability analysis.
14+
* Can be a single function name or comma-separated list of function names.
15+
*/
16+
external string sourceFunction();
17+
18+
/**
19+
* Gets the target function name for call graph reachability analysis.
20+
* Can be a single function name or comma-separated list of function names.
21+
*/
22+
external string targetFunction();
23+
24+
/**
25+
* Gets a single source function name from the comma-separated list.
26+
*/
27+
string getSourceFunctionName() { result = sourceFunction().splitAt(",").trim() }
28+
29+
/**
30+
* Gets a single target function name from the comma-separated list.
31+
*/
32+
string getTargetFunctionName() { result = targetFunction().splitAt(",").trim() }
33+
34+
/**
35+
* Gets a function by matching against the selected source function names.
36+
*/
37+
Function getSourceFunction() {
38+
exists(string selectedFunc |
39+
selectedFunc = getSourceFunctionName() and
40+
(
41+
// Match by exact function name
42+
result.getName() = selectedFunc or
43+
// Match by qualified name
44+
result.getQualifiedName() = selectedFunc
45+
)
46+
)
47+
}
48+
49+
/**
50+
* Gets a function by matching against the selected target function names.
51+
*/
52+
Function getTargetFunction() {
53+
exists(string selectedFunc |
54+
selectedFunc = getTargetFunctionName() and
55+
(
56+
// Match by exact function name
57+
result.getName() = selectedFunc or
58+
// Match by qualified name
59+
result.getQualifiedName() = selectedFunc
60+
)
61+
)
62+
}
63+
64+
/**
65+
* Holds if function `caller` directly calls function `callee`.
66+
*/
67+
predicate calls(Function caller_, Function callee_) {
68+
exists(FunctionCall c | c.getEnclosingFunction() = caller_ and c.getTarget() = callee_)
69+
}
70+
71+
from FunctionCall call, Function caller, Function callee
72+
where
73+
call.getTarget() = callee and
74+
call.getEnclosingFunction() = caller and
75+
(
76+
// Use external predicates if available: show calls on paths from source to target
77+
exists(Function source, Function target |
78+
source = getSourceFunction() and
79+
target = getTargetFunction() and
80+
calls*(source, caller) and
81+
calls*(callee, target)
82+
)
83+
or
84+
// Fallback for unit tests: include test files
85+
(
86+
not exists(getSourceFunctionName()) and
87+
not exists(getTargetFunctionName()) and
88+
caller.getFile().getParentContainer().getParentContainer().getBaseName() = "test"
89+
)
90+
)
91+
select call,
92+
"Reachable call from `" + caller.getQualifiedName() + "` to `" + callee.getQualifiedName() + "`"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| Example1.cpp:6:5:6:13 | call to unrelated | Reachable call from `target` to `unrelated` |
2+
| Example1.cpp:10:5:10:10 | call to target | Reachable call from `intermediate` to `target` |
3+
| Example1.cpp:14:5:14:16 | call to intermediate | Reachable call from `source` to `intermediate` |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CallGraphFromTo/CallGraphFromTo.ql
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
void unrelated() {
2+
// No calls
3+
}
4+
5+
void target() {
6+
unrelated();
7+
}
8+
9+
void intermediate() {
10+
target();
11+
}
12+
13+
void source() {
14+
intermediate();
15+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# CallGraphFromTo for `csharp` Source Files
2+
3+
Displays calls on reachable paths from a source method to a target method, showing transitive call graph connectivity.
4+
5+
## Overview
6+
7+
This query identifies all method calls that lie on any transitive call path from a specified source method to a specified target method. Given both a source and target method name, it reports each call site along the connecting paths, which is useful for understanding indirect call chains, security-relevant data flow paths, and method reachability.
8+
9+
The query uses transitive closure (`calls*`) to determine reachability, then reports only the direct call sites that contribute to paths between the source and target. It accepts method names via external predicates (`sourceFunction` and `targetFunction`).
10+
11+
## Use Cases
12+
13+
This query is primarily used for:
14+
15+
- Determining if a call path exists between two methods
16+
- Mapping indirect call chains from a source to a target method
17+
- Analyzing security-relevant paths (e.g., from user input handlers to sensitive operations)
18+
- Understanding transitive dependencies between methods
19+
20+
## Example
21+
22+
The following C# code demonstrates a transitive call chain from `Source` through `Intermediate` to `Target`:
23+
24+
```csharp
25+
class Example {
26+
void Target() {}
27+
28+
void Intermediate() {
29+
Target();
30+
}
31+
32+
void Source() {
33+
Intermediate();
34+
}
35+
}
36+
```
37+
38+
Running with `sourceFunction = "Source"` and `targetFunction = "Target"` produces results showing each call site on the path with the message pattern ``Reachable call from `Source`to`Intermediate```.
39+
40+
## Output Format
41+
42+
The query is a `@kind problem` query producing rows of:
43+
44+
- ``select call, "Reachable call from `caller` to `callee`"``
45+
46+
## References
47+
48+
- [C# Methods](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/methods)
49+
- [CodeQL Call Graph Analysis](https://codeql.github.com/docs/writing-codeql-queries/about-data-flow-analysis/)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/**
2+
* @name Call Graph From To for csharp
3+
* @description Displays calls on reachable paths from a source method to a target method, showing transitive call graph connectivity.
4+
* @id csharp/tools/call-graph-from-to
5+
* @kind problem
6+
* @problem.severity recommendation
7+
* @tags call-graph
8+
*/
9+
10+
import csharp
11+
12+
/**
13+
* Gets the source method name for call graph reachability analysis.
14+
* Can be a single method name or comma-separated list of method names.
15+
*/
16+
external string sourceFunction();
17+
18+
/**
19+
* Gets the target method name for call graph reachability analysis.
20+
* Can be a single method name or comma-separated list of method names.
21+
*/
22+
external string targetFunction();
23+
24+
/**
25+
* Gets a single source method name from the comma-separated list.
26+
*/
27+
string getSourceFunctionName() { result = sourceFunction().splitAt(",").trim() }
28+
29+
/**
30+
* Gets a single target method name from the comma-separated list.
31+
*/
32+
string getTargetFunctionName() { result = targetFunction().splitAt(",").trim() }
33+
34+
/**
35+
* Gets a method by matching against the selected source method names.
36+
*/
37+
Callable getSourceFunction() {
38+
exists(string selectedFunc |
39+
selectedFunc = getSourceFunctionName() and
40+
result.getName() = selectedFunc
41+
)
42+
}
43+
44+
/**
45+
* Gets a method by matching against the selected target method names.
46+
*/
47+
Callable getTargetFunction() {
48+
exists(string selectedFunc |
49+
selectedFunc = getTargetFunctionName() and
50+
result.getName() = selectedFunc
51+
)
52+
}
53+
54+
/**
55+
* Holds if callable `caller` directly calls callable `callee`.
56+
*/
57+
predicate calls(Callable caller_, Callable callee_) {
58+
exists(Call c | c.getEnclosingCallable() = caller_ and c.getTarget() = callee_)
59+
}
60+
61+
from Call call, Callable caller, Callable callee
62+
where
63+
call.getEnclosingCallable() = caller and
64+
call.getTarget() = callee and
65+
(
66+
// Use external predicates if available: show calls on paths from source to target
67+
exists(Callable source, Callable target |
68+
source = getSourceFunction() and
69+
target = getTargetFunction() and
70+
calls*(source, caller) and
71+
calls*(callee, target)
72+
)
73+
or
74+
// Fallback for unit tests: include test files
75+
(
76+
not exists(getSourceFunctionName()) and
77+
not exists(getTargetFunctionName()) and
78+
caller.getFile().getParentContainer().getParentContainer().getBaseName() = "test"
79+
)
80+
)
81+
select call,
82+
"Reachable call from `" + caller.getName() + "` to `" + callee.getName() + "`"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
| Example1.cs:1:7:1:14 | call to constructor Object | Reachable call from `Example1` to `Object` |
2+
| Example1.cs:1:7:1:14 | call to method <object initializer> | Reachable call from `Example1` to `<object initializer>` |
3+
| Example1.cs:7:9:7:19 | call to method Unrelated | Reachable call from `Target` to `Unrelated` |
4+
| Example1.cs:11:9:11:16 | call to method Target | Reachable call from `Intermediate` to `Target` |
5+
| Example1.cs:15:9:15:22 | call to method Intermediate | Reachable call from `Source` to `Intermediate` |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CallGraphFromTo/CallGraphFromTo.ql
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class Example1 {
2+
void Unrelated() {
3+
// No calls
4+
}
5+
6+
void Target() {
7+
Unrelated();
8+
}
9+
10+
void Intermediate() {
11+
Target();
12+
}
13+
14+
void Source() {
15+
Intermediate();
16+
}
17+
}

0 commit comments

Comments
 (0)