Skip to content

Commit 6f7d781

Browse files
author
Stephan Brandauer
committed
Java: add endpoints for parameters of overridden methods in automodel application mode
1 parent dff8259 commit 6f7d781

File tree

4 files changed

+57
-12
lines changed

4 files changed

+57
-12
lines changed

java/ql/automodel/src/AutomodelApplicationModeCharacteristics.qll

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,18 @@ newtype TApplicationModeEndpoint =
3636
not exists(int i | i < idx and call.getArgument(i).(Argument).isVararg())
3737
)
3838
} or
39-
TMethodCall(Call call) { not call instanceof ConstructorCall }
39+
TMethodCall(Call call) { not call instanceof ConstructorCall } or
40+
TOverriddenParameter(Parameter p) {
41+
not p.getCallable().callsConstructor(_) and
42+
p.getCallable().(Method).overrides(_)
43+
}
4044

4145
/**
4246
* An endpoint is a node that is a candidate for modeling.
4347
*/
4448
abstract private class ApplicationModeEndpoint extends TApplicationModeEndpoint {
49+
abstract Callable getCallable();
50+
4551
abstract Call getCall();
4652

4753
abstract string getMaDInput();
@@ -74,6 +80,8 @@ class ExplicitArgument extends ApplicationModeEndpoint, TExplicitArgument {
7480

7581
ExplicitArgument() { this = TExplicitArgument(call, arg) }
7682

83+
override Callable getCallable() { result = call.getCallee() }
84+
7785
override Call getCall() { result = call }
7886

7987
private int getArgIndex() { this.asTop() = call.getArgument(result) }
@@ -95,6 +103,8 @@ class InstanceArgument extends ApplicationModeEndpoint, TInstanceArgument {
95103

96104
InstanceArgument() { this = TInstanceArgument(call, arg) }
97105

106+
override Callable getCallable() { result = call.getCallee() }
107+
98108
override Call getCall() { result = call }
99109

100110
override string getMaDInput() { result = "Argument[this]" }
@@ -124,13 +134,15 @@ class ImplicitVarargsArray extends ApplicationModeEndpoint, TImplicitVarargsArra
124134

125135
ImplicitVarargsArray() { this = TImplicitVarargsArray(call, vararg, idx) }
126136

137+
override Callable getCallable() { result = call.getCallee() }
138+
127139
override Call getCall() { result = call }
128140

129141
override string getMaDInput() { result = "Argument[" + idx + "]" }
130142

131143
override string getMaDOutput() { none() }
132144

133-
override Top asTop() { result = this.getCall() }
145+
override Top asTop() { result = call }
134146

135147
override DataFlow::Node asNode() { result = vararg }
136148

@@ -145,6 +157,8 @@ class MethodCall extends ApplicationModeEndpoint, TMethodCall {
145157

146158
MethodCall() { this = TMethodCall(call) }
147159

160+
override Callable getCallable() { result = call.getCallee() }
161+
148162
override Call getCall() { result = call }
149163

150164
override string getMaDInput() { result = "Argument[this]" }
@@ -158,6 +172,28 @@ class MethodCall extends ApplicationModeEndpoint, TMethodCall {
158172
override string toString() { result = call.toString() }
159173
}
160174

175+
class OverriddenParameter extends ApplicationModeEndpoint, TOverriddenParameter {
176+
Parameter p;
177+
178+
OverriddenParameter() { this = TOverriddenParameter(p) }
179+
180+
override Callable getCallable() { result = p.getCallable() }
181+
182+
override Call getCall() { none() }
183+
184+
private int getArgIndex() { p.getCallable().getParameter(result) = p }
185+
186+
override string getMaDInput() { none() }
187+
188+
override string getMaDOutput() { result = "Parameter[" + this.getArgIndex() + "]" }
189+
190+
override Top asTop() { result = p }
191+
192+
override DataFlow::Node asNode() { result.(DataFlow::ParameterNode).asParameter() = p }
193+
194+
override string toString() { result = p.toString() }
195+
}
196+
161197
/**
162198
* A candidates implementation.
163199
*
@@ -208,7 +244,8 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
208244
predicate isSource(Endpoint e, string kind, string provenance) {
209245
exists(string package, string type, string name, string signature, string ext, string output |
210246
sourceSpec(e, package, type, name, signature, ext, output) and
211-
ExternalFlow::sourceModel(package, type, _, name, [signature, ""], ext, output, kind, provenance)
247+
ExternalFlow::sourceModel(package, type, _, name, [signature, ""], ext, output, kind,
248+
provenance)
212249
)
213250
}
214251

@@ -230,7 +267,8 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
230267
}
231268

232269
additional predicate sourceSpec(
233-
Endpoint e, string package, string type, string name, string signature, string ext, string output
270+
Endpoint e, string package, string type, string name, string signature, string ext,
271+
string output
234272
) {
235273
ApplicationModeGetCallable::getCallable(e).hasQualifiedName(package, type, name) and
236274
signature = ExternalFlow::paramsString(ApplicationModeGetCallable::getCallable(e)) and
@@ -293,7 +331,7 @@ class ApplicationModeMetadataExtractor extends string {
293331
string input, string output, string isVarargsArray
294332
) {
295333
exists(Callable callable |
296-
e.getCall().getCallee() = callable and
334+
e.getCallable() = callable and
297335
(if exists(e.getMaDInput()) then input = e.getMaDInput() else input = "") and
298336
(if exists(e.getMaDOutput()) then output = e.getMaDOutput() else output = "") and
299337
package = callable.getDeclaringType().getPackage().getName() and
@@ -328,8 +366,8 @@ private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASin
328366

329367
override predicate appliesToEndpoint(Endpoint e) {
330368
not ApplicationCandidatesImpl::isSink(e, _, _) and
331-
ApplicationModeGetCallable::getCallable(e).getName().matches("is%") and
332-
ApplicationModeGetCallable::getCallable(e).getReturnType() instanceof BooleanType
369+
e.getCallable().getName().matches("is%") and
370+
e.getCallable().getReturnType() instanceof BooleanType
333371
}
334372
}
335373

java/ql/automodel/src/AutomodelApplicationModeExtractCandidates.ql

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ where
6666
endpoint =
6767
getSampleForSignature(9, package, type, subtypes, name, signature, input, output,
6868
isVarargsArray, extensibleType) and
69-
// If a node is already a known sink for any of our existing ATM queries and is already modeled as a MaD sink, we
70-
// don't include it as a candidate. Otherwise, we might include it as a candidate for query A, but the model will
71-
// label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in
72-
// overlap between our detected sinks and the pre-existing modeling. We assume that, if a sink has already been
73-
// modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it.
69+
// If a node is already modeled in MaD, we don't include it as a candidate. Otherwise, we might include it as a
70+
// candidate for query A, but the model will label it as a sink for one of the sink types of query B, for which it's
71+
// already a known sink. This would result in overlap between our detected sinks and the pre-existing modeling. We
72+
// assume that, if a sink has already been modeled in a MaD model, then it doesn't belong to any additional sink
73+
// types, and we don't need to reexamine it.
7474
(
7575
not CharacteristicsImpl::isSink(endpoint, _, _) and alreadyAiModeled = ""
7676
or

java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractCandidates.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
| Test.java:54:4:54:4 | o | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:52:3:57:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://:1:1:1:1 | | output | file://true:1:1:1:1 | true | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
1010
| Test.java:61:3:61:3 | c | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:61:3:61:20 | getInputStream(...) | CallContext | file://java.net:1:1:1:1 | java.net | package | file://URLConnection:1:1:1:1 | URLConnection | type | file://true:1:1:1:1 | true | subtypes | file://getInputStream:1:1:1:1 | getInputStream | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
1111
| Test.java:61:3:61:20 | getInputStream(...) | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:61:3:61:20 | getInputStream(...) | CallContext | file://java.net:1:1:1:1 | java.net | package | file://URLConnection:1:1:1:1 | URLConnection | type | file://true:1:1:1:1 | true | subtypes | file://getInputStream:1:1:1:1 | getInputStream | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
12+
| Test.java:66:24:66:31 | o | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:66:24:66:31 | o | CallContext | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://OverrideTest:1:1:1:1 | OverrideTest | type | file://true:1:1:1:1 | true | subtypes | file://equals:1:1:1:1 | equals | name | file://(Object):1:1:1:1 | (Object) | signature | file://:1:1:1:1 | | input | file://Parameter[0]:1:1:1:1 | Parameter[0] | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |

java/ql/automodel/test/AutomodelApplicationModeExtraction/Test.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,9 @@ public static void WebSocketExample(URLConnection c) throws Exception {
6161
c.getInputStream(); // the call is a source example, c is a sink candidate
6262
}
6363
}
64+
65+
class OverrideTest {
66+
public boolean equals(Object o) { // o is a source candidate because it overrides an existing method
67+
return false;
68+
}
69+
}

0 commit comments

Comments
 (0)