Skip to content

Commit 5abf776

Browse files
author
Stephan Brandauer
committed
Java: automodel application mode: use endpoint class like in framework mode
1 parent e14e0cd commit 5abf776

8 files changed

+119
-84
lines changed

java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll

Lines changed: 93 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,81 @@ import AutomodelEndpointTypes as AutomodelEndpointTypes
2121

2222
newtype JavaRelatedLocationType = CallContext()
2323

24+
newtype TApplicationModeEndpoint =
25+
TExplicitArgument(Call call, DataFlow::Node arg) {
26+
exists(Argument argExpr |
27+
arg.asExpr() = argExpr and not argExpr.isVararg() and call = argExpr.getCall()
28+
)
29+
} or
30+
TInstanceArgument(Call call, DataFlow::Node arg) { arg = DataFlow::getInstanceArgument(call) } or
31+
TImplicitVarargsArray(Call call, DataFlow::ImplicitVarargsArray varargs, int idx) {
32+
varargs.getCall() = call and
33+
idx = min(Argument arg, int n | arg = call.getArgument(n) and arg.isVararg() | n)
34+
}
35+
36+
abstract private class ApplicationModeEndpoint extends TApplicationModeEndpoint {
37+
abstract predicate isArgOf(Call c, int idx);
38+
39+
Call getCall() { this.isArgOf(result, _) }
40+
41+
int getArgIndex() { this.isArgOf(_, result) }
42+
43+
abstract Top asTop();
44+
45+
abstract DataFlow::Node asNode();
46+
47+
abstract string toString();
48+
}
49+
2450
/**
2551
* A class representing nodes that are arguments to calls.
2652
*/
27-
private class ArgumentNode extends DataFlow::Node {
28-
Call c;
53+
class ExplicitArgument extends ApplicationModeEndpoint, TExplicitArgument {
54+
Call call;
55+
DataFlow::Node arg;
2956

30-
ArgumentNode() {
31-
exists(Argument arg | this.asExpr() = arg and not arg.isVararg() and c = arg.getCall())
32-
or
33-
this.(DataFlow::ImplicitVarargsArray).getCall() = c
34-
or
35-
this = DataFlow::getInstanceArgument(c)
57+
ExplicitArgument() { this = TExplicitArgument(call, arg) }
58+
59+
override predicate isArgOf(Call c, int idx) { c = call and this.asTop() = c.getArgument(idx) }
60+
61+
override Top asTop() { result = arg.asExpr() }
62+
63+
override DataFlow::Node asNode() { result = arg }
64+
65+
override string toString() { result = arg.toString() }
66+
}
67+
68+
class InstanceArgument extends ApplicationModeEndpoint, TInstanceArgument {
69+
Call call;
70+
DataFlow::Node arg;
71+
72+
InstanceArgument() { this = TInstanceArgument(call, arg) }
73+
74+
override predicate isArgOf(Call c, int idx) {
75+
c = call and this.asTop() = c.getQualifier() and idx = -1
3676
}
3777

38-
Call getCall() { result = c }
78+
override Top asTop() { if exists(arg.asExpr()) then result = arg.asExpr() else result = call }
79+
80+
override DataFlow::Node asNode() { result = arg }
81+
82+
override string toString() { result = arg.toString() }
83+
}
84+
85+
class ImplicitVarargsArray extends ApplicationModeEndpoint, TImplicitVarargsArray {
86+
Call call;
87+
DataFlow::ImplicitVarargsArray varargs;
88+
int idx;
89+
90+
ImplicitVarargsArray() { this = TImplicitVarargsArray(call, varargs, idx) }
91+
92+
override predicate isArgOf(Call c, int i) { c = call and i = idx }
93+
94+
override Top asTop() { result = this.getCall() }
95+
96+
override DataFlow::Node asNode() { result = varargs }
97+
98+
override string toString() { result = varargs.toString() }
3999
}
40100

41101
/**
@@ -47,7 +107,7 @@ private class ArgumentNode extends DataFlow::Node {
47107
*/
48108
module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig {
49109
// for documentation of the implementations here, see the QLDoc in the CandidateSig signature module.
50-
class Endpoint = ArgumentNode;
110+
class Endpoint = ApplicationModeEndpoint;
51111

52112
class EndpointType = AutomodelEndpointTypes::EndpointType;
53113

@@ -61,18 +121,18 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
61121
predicate isSanitizer(Endpoint e, EndpointType t) {
62122
exists(t) and
63123
(
64-
e.getType() instanceof BoxedType
124+
e.asNode().getType() instanceof BoxedType
65125
or
66-
e.getType() instanceof PrimitiveType
126+
e.asNode().getType() instanceof PrimitiveType
67127
or
68-
e.getType() instanceof NumberType
128+
e.asNode().getType() instanceof NumberType
69129
)
70130
or
71131
t instanceof AutomodelEndpointTypes::PathInjectionSinkType and
72-
e instanceof PathSanitizer::PathInjectionSanitizer
132+
e.asNode() instanceof PathSanitizer::PathInjectionSanitizer
73133
}
74134

75-
RelatedLocation asLocation(Endpoint e) { result = e.asExpr() }
135+
RelatedLocation asLocation(Endpoint e) { result = e.asTop() }
76136

77137
predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2;
78138

@@ -98,16 +158,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
98158
ApplicationModeGetCallable::getCallable(e).hasQualifiedName(package, type, name) and
99159
signature = ExternalFlow::paramsString(ApplicationModeGetCallable::getCallable(e)) and
100160
ext = "" and
101-
(
102-
exists(Call c, int argIdx |
103-
e.asExpr() = c.getArgument(argIdx) and
104-
input = AutomodelJavaUtil::getArgumentForIndex(argIdx)
105-
)
106-
or
107-
exists(Call c |
108-
e.asExpr() = c.getQualifier() and input = AutomodelJavaUtil::getArgumentForIndex(-1)
109-
)
110-
)
161+
input = AutomodelJavaUtil::getArgumentForIndex(e.getArgIndex())
111162
}
112163

113164
/**
@@ -118,7 +169,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
118169
*/
119170
RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) {
120171
type = CallContext() and
121-
result = any(Call c | e.asExpr() = [c.getAnArgument(), c.getQualifier()])
172+
result = e.asTop()
122173
}
123174
}
124175

@@ -132,20 +183,15 @@ private module ApplicationModeGetCallable implements AutomodelSharedGetCallable:
132183
/**
133184
* Returns the API callable being modeled.
134185
*/
135-
Callable getCallable(Endpoint e) {
136-
exists(Call c |
137-
e.asExpr() = [c.getAnArgument(), c.getQualifier()] and
138-
result = c.getCallee()
139-
)
140-
}
186+
Callable getCallable(Endpoint e) { result = e.getCall().getCallee() }
141187
}
142188

143189
/**
144190
* Contains endpoints that are defined in QL code rather than as a MaD model. Ideally this predicate
145191
* should be empty.
146192
*/
147193
private predicate isCustomSink(Endpoint e, string kind) {
148-
e instanceof QueryInjectionSink and kind = "sql"
194+
e.asNode() instanceof QueryInjectionSink and kind = "sql"
149195
}
150196

151197
module CharacteristicsImpl =
@@ -169,14 +215,9 @@ class ApplicationModeMetadataExtractor extends string {
169215
Endpoint e, string package, string type, string subtypes, string name, string signature,
170216
string input
171217
) {
172-
exists(Call call, Callable callable, int argIdx |
173-
call.getCallee() = callable and
174-
(
175-
e.asExpr() = call.getArgument(argIdx)
176-
or
177-
e.asExpr() = call.getQualifier() and argIdx = -1
178-
) and
179-
input = AutomodelJavaUtil::getArgumentForIndex(argIdx) and
218+
exists(Callable callable |
219+
e.getCall().getCallee() = callable and
220+
input = AutomodelJavaUtil::getArgumentForIndex(e.getArgIndex()) and
180221
package = callable.getDeclaringType().getPackage().getName() and
181222
// we're using the erased types because the MaD convention is to not specify type parameters.
182223
// Whether something is or isn't a sink doesn't usually depend on the type parameters.
@@ -253,28 +294,10 @@ private class IsMaDTaintStepCharacteristic extends CharacteristicsImpl::NotASink
253294
IsMaDTaintStepCharacteristic() { this = "taint step" }
254295

255296
override predicate appliesToEndpoint(Endpoint e) {
256-
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e, _, _) or
257-
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e, _, _) or
258-
FlowSummaryImpl::Private::Steps::summaryGetterStep(e, _, _, _) or
259-
FlowSummaryImpl::Private::Steps::summarySetterStep(e, _, _, _)
260-
}
261-
}
262-
263-
/**
264-
* A negative characteristic that filters out qualifiers that are classes (i.e. static calls). These
265-
* are unlikely to have any non-trivial flow going into them.
266-
*
267-
* Technically, an accessed type _could_ come from outside of the source code, but there's not
268-
* much likelihood of that being user-controlled.
269-
*/
270-
private class ClassQualifierCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
271-
ClassQualifierCharacteristic() { this = "class qualifier" }
272-
273-
override predicate appliesToEndpoint(Endpoint e) {
274-
exists(Call c |
275-
e.asExpr() = c.getQualifier() and
276-
c.getQualifier() instanceof TypeAccess
277-
)
297+
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e.asNode(), _, _) or
298+
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e.asNode(), _, _) or
299+
FlowSummaryImpl::Private::Steps::summaryGetterStep(e.asNode(), _, _, _) or
300+
FlowSummaryImpl::Private::Steps::summarySetterStep(e.asNode(), _, _, _)
278301
}
279302
}
280303

@@ -351,7 +374,7 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics
351374
private class FunctionValueCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
352375
FunctionValueCharacteristic() { this = "function value" }
353376

354-
override predicate appliesToEndpoint(Endpoint e) { e.asExpr() instanceof FunctionalExpr }
377+
override predicate appliesToEndpoint(Endpoint e) { e.asNode().asExpr() instanceof FunctionalExpr }
355378
}
356379

357380
/**
@@ -371,12 +394,12 @@ private class CannotBeTaintedCharacteristic extends CharacteristicsImpl::LikelyN
371394
* Holds if the node `n` is known as the predecessor in a modeled flow step.
372395
*/
373396
private predicate isKnownOutNodeForStep(Endpoint e) {
374-
e.asExpr() instanceof Call or // we just assume flow in that case
375-
TaintTracking::localTaintStep(_, e) or
376-
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(_, e, _) or
377-
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(_, e, _) or
378-
FlowSummaryImpl::Private::Steps::summaryGetterStep(_, _, e, _) or
379-
FlowSummaryImpl::Private::Steps::summarySetterStep(_, _, e, _)
397+
e.asNode().asExpr() instanceof Call or // we just assume flow in that case
398+
TaintTracking::localTaintStep(_, e.asNode()) or
399+
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(_, e.asNode(), _) or
400+
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(_, e.asNode(), _) or
401+
FlowSummaryImpl::Private::Steps::summaryGetterStep(_, _, e.asNode(), _) or
402+
FlowSummaryImpl::Private::Steps::summarySetterStep(_, _, e.asNode(), _)
380403
}
381404
}
382405

java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ private Endpoint getSampleForSignature(
3333
|
3434
result =
3535
rank[n](Endpoint e, Location loc |
36-
loc = e.getLocation() and
36+
loc = e.asTop().getLocation() and
3737
meta.hasMetadata(e, package, type, subtypes, name, signature, input)
3838
|
3939
e
@@ -75,7 +75,8 @@ where
7575
|
7676
sinkType, ", "
7777
)
78-
select endpoint, message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
78+
select endpoint.asNode(),
79+
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
7980
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
8081
package, "package", //
8182
type, "type", //

java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
2424
exists(int n, int num_endpoints | num_endpoints = count(Endpoint e | c.appliesToEndpoint(e)) |
2525
result =
2626
rank[n](Endpoint e, Location loc |
27-
loc = e.getLocation() and c.appliesToEndpoint(e)
27+
loc = e.asTop().getLocation() and c.appliesToEndpoint(e)
2828
|
2929
e
3030
order by
@@ -63,7 +63,8 @@ where
6363
characteristic2.hasImplications(positiveType, true, confidence2)
6464
) and
6565
message = characteristic
66-
select endpoint, message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
66+
select endpoint.asNode(),
67+
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
6768
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
6869
package, "package", //
6970
type, "type", //

java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ where
2222
not erroneousEndpoints(endpoint, _, _, _, _, false) and
2323
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
2424
// Extract positive examples of sinks belonging to the existing ATM query configurations.
25-
CharacteristicsImpl::isKnownSink(endpoint, sinkType)
26-
select endpoint, sinkType + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
25+
CharacteristicsImpl::isKnownSink(endpoint, sinkType) and
26+
exists(CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()))
27+
select endpoint.asNode(),
28+
sinkType + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
2729
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
2830
package, "package", //
2931
type, "type", //
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
| Test.java:16:3:16:11 | reference | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:16:3:16:24 | set(...) | CallContext | file://java.util.concurrent.atomic:1:1:1:1 | java.util.concurrent.atomic | package | file://AtomicReference:1:1:1:1 | AtomicReference | type | file://false:1:1:1:1 | false | subtypes | file://set:1:1:1:1 | set | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input |
2-
| Test.java:21:3:21:10 | supplier | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:21:3:21:16 | get(...) | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input |
1+
| Test.java:16:3:16:11 | reference | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:16:3:16:11 | reference | CallContext | file://java.util.concurrent.atomic:1:1:1:1 | java.util.concurrent.atomic | package | file://AtomicReference:1:1:1:1 | AtomicReference | type | file://false:1:1:1:1 | false | subtypes | file://set:1:1:1:1 | set | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input |
2+
| Test.java:21:3:21:10 | supplier | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:21:3:21:10 | supplier | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input |
3+
| Test.java:51:3:54:3 | new ..[] { .. } | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:51:3:54: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 |
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
| Test.java:40:14:40:21 | openPath | taint step\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:40:4:40:22 | get(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Paths:1:1:1:1 | Paths | type | file://false:1:1:1:1 | false | subtypes | file://get:1:1:1:1 | get | name | file://(String,String[]):1:1:1:1 | (String,String[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
2-
| Test.java:46:4:46:5 | f2 | known non-sink\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:45:10:47:3 | compareTo(...) | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
1+
| Test.java:46:4:46:5 | f2 | known non-sink\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:46:4:46:5 | f2 | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
2+
| Test.java:52:4:52:4 | p | taint step\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:52:4:52:4 | p | 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[0]:1:1:1:1 | Argument[0] | input |
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
| Test.java:26:4:26:9 | source | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | 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://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
2-
| Test.java:27:4:27:9 | target | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | 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://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input |
3-
| Test.java:34:4:34:11 | openPath | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:33:10:35:3 | newInputStream(...) | 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://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
1+
| Test.java:26:4:26:9 | source | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:26:4:26:9 | source | 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://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
2+
| Test.java:27:4:27:9 | target | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:27:4:27:9 | target | 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://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input |
3+
| Test.java:34:4:34:11 | openPath | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:34:4:34:11 | openPath | 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://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |

java/ql/test/query-tests/Telemetry/AutomodelApplicationModeExtraction/Test.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import java.util.concurrent.atomic.AtomicReference;
99
import java.util.function.Supplier;
1010
import java.io.File;
11-
11+
import java.nio.file.FileVisitOption;
1212

1313
class Test {
1414
public static void main(String[] args) throws Exception {
@@ -46,5 +46,12 @@ public static int compareFiles(File f1, File f2) {
4646
f2 // negative example (modeled as not a sink)
4747
);
4848
}
49+
50+
public static void FilesWalkExample(Path p) throws Exception {
51+
Files.walk(
52+
p, // negative example (modeled as a taint step)
53+
FileVisitOption.FOLLOW_LINKS // the implicit varargs array is a candidate
54+
);
55+
}
4956
}
5057

0 commit comments

Comments
 (0)