Skip to content

Commit ff4555a

Browse files
author
Max Schaefer
committed
Get rid of negative sink types.
Instead of positively implying the negative sink type, negative sink characteristics now negatively imply all sink types (but not source types). This is simpler and sice we will never have a huge number of sink types it doesn't impact performance either. Changes to test results: - The call to `createDirectories` at `Test.java:87` is now correctly classified as a source candidate, having previously been erroneously excluded by a negative _sink_ characteristic. - The call to `compareTo` at `Test.java:48` is now erroneously classified as a source candidate; it should be suppressed by `IsSanitizerCharacteristic`, which is a negative sink characteristic, but should really be a negative source characteristic. - In framework mode, several endpoints are now erroneously classified as source candidates even though they have neutral models, because `NeutralModelCharacteristic` is currently only a negative sink characteristic and not a negative source characteristic.
1 parent bcf4f4f commit ff4555a

10 files changed

+50
-53
lines changed

java/ql/automodel/src/AutomodelApplicationModeCharacteristics.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,9 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
259259

260260
class EndpointType = AutomodelEndpointTypes::EndpointType;
261261

262-
class NegativeEndpointType = AutomodelEndpointTypes::NegativeSinkType;
262+
class SinkType = AutomodelEndpointTypes::SinkType;
263+
264+
class SourceType = AutomodelEndpointTypes::SourceType;
263265

264266
class RelatedLocation = Location::Top;
265267

java/ql/automodel/src/AutomodelApplicationModeExtractNegativeExamples.ql

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,20 @@ from
4848
where
4949
endpoint = getSampleForCharacteristic(characteristic, 100) and
5050
extensibleType = endpoint.getExtensibleType() and
51+
// the node is know not to be an endpoint of any appropriate type
52+
forall(EndpointType tp | tp = endpoint.getAPotentialType() |
53+
characteristic.hasImplications(tp, false, _)
54+
) and
55+
// the lowest confidence across all endpoint types should be at least highConfidence
56+
confidence = min(float c | characteristic.hasImplications(endpoint.getAPotentialType(), false, c)) and
5157
confidence >= SharedCharacteristics::highConfidence() and
52-
characteristic.hasImplications(any(NegativeSinkType negative), true, confidence) and
5358
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and
54-
// It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be
55-
// treated by the actual query as a sanitizer, since the final logic is something like
56-
// `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because
57-
// they're ambiguous and might confuse the model, so we explicitly exclude all known sinks from the negative examples.
58-
not exists(EndpointCharacteristic characteristic2, float confidence2, SinkType positiveType |
59-
not positiveType instanceof NegativeSinkType and
59+
// It's valid for a node to be both a potential source/sanitizer and a sink. We don't want to include such nodes
60+
// as negative examples in the prompt, because they're ambiguous and might confuse the model, so we explicitly them here.
61+
not exists(EndpointCharacteristic characteristic2, float confidence2, EndpointType type2 |
6062
characteristic2.appliesToEndpoint(endpoint) and
6163
confidence2 >= SharedCharacteristics::maximalConfidence() and
62-
characteristic2.hasImplications(positiveType, true, confidence2)
64+
characteristic2.hasImplications(type2, true, confidence2)
6365
) and
6466
message = characteristic
6567
select endpoint.asNode(),

java/ql/automodel/src/AutomodelEndpointTypes.qll

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,6 @@ abstract class SinkType extends EndpointType {
3030
SinkType() { any() }
3131
}
3232

33-
/** The `Negative` class for non-sinks. */
34-
class NegativeSinkType extends SinkType {
35-
NegativeSinkType() { this = "non-sink" }
36-
}
37-
3833
/** A sink relevant to the SQL injection query */
3934
class SqlInjectionSinkType extends SinkType {
4035
SqlInjectionSinkType() { this = "sql-injection" }

java/ql/automodel/src/AutomodelFrameworkModeCharacteristics.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,9 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
214214

215215
class EndpointType = AutomodelEndpointTypes::EndpointType;
216216

217-
class NegativeEndpointType = AutomodelEndpointTypes::NegativeSinkType;
217+
class SinkType = AutomodelEndpointTypes::SinkType;
218+
219+
class SourceType = AutomodelEndpointTypes::SourceType;
218220

219221
class RelatedLocation = Location::Top;
220222

java/ql/automodel/src/AutomodelFrameworkModeExtractNegativeExamples.ql

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,20 @@ from
2121
where
2222
endpoint.getExtensibleType() = extensibleType and
2323
characteristic.appliesToEndpoint(endpoint) and
24+
// the node is known not to be an endpoint of any appropriate type
25+
forall(EndpointType tp | tp = endpoint.getAPotentialType() |
26+
characteristic.hasImplications(tp, false, _)
27+
) and
28+
// the lowest confidence across all endpoint types should be at least highConfidence
29+
confidence = min(float c | characteristic.hasImplications(endpoint.getAPotentialType(), false, c)) and
2430
confidence >= SharedCharacteristics::highConfidence() and
25-
characteristic.hasImplications(any(NegativeSinkType negative), true, confidence) and
2631
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName) and
27-
// It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be
28-
// treated by the actual query as a sanitizer, since the final logic is something like
29-
// `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because
30-
// they're ambiguous and might confuse the model, so we explicitly exclude all known sinks from the negative examples.
31-
not exists(EndpointCharacteristic characteristic2, float confidence2, SinkType positiveType |
32-
not positiveType instanceof NegativeSinkType and
32+
// It's valid for a node to be both a potential source/sanitizer and a sink. We don't want to include such nodes
33+
// as negative examples in the prompt, because they're ambiguous and might confuse the model, so we explicitly them here.
34+
not exists(EndpointCharacteristic characteristic2, float confidence2, EndpointType type2 |
3335
characteristic2.appliesToEndpoint(endpoint) and
3436
confidence2 >= SharedCharacteristics::maximalConfidence() and
35-
characteristic2.hasImplications(positiveType, true, confidence2)
37+
characteristic2.hasImplications(type2, true, confidence2)
3638
) and
3739
message = characteristic
3840
select endpoint,

java/ql/automodel/src/AutomodelSharedCharacteristics.qll

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,19 @@ signature module CandidateSig {
3333
class RelatedLocationType;
3434

3535
/**
36-
* A class kind for an endpoint.
36+
* An endpoint type considered by this specification.
3737
*/
3838
class EndpointType extends string;
3939

4040
/**
41-
* An EndpointType that denotes the absence of any sink.
41+
* A sink endpoint type considered by this specification.
4242
*/
43-
class NegativeEndpointType extends EndpointType;
43+
class SinkType extends EndpointType;
44+
45+
/**
46+
* A source endpoint type considered by this specification.
47+
*/
48+
class SourceType extends EndpointType;
4449

4550
/**
4651
* Gets the endpoint as a location.
@@ -105,15 +110,14 @@ module SharedCharacteristics<CandidateSig Candidate> {
105110
}
106111

107112
/**
108-
* Holds if `endpoint` is modeled as `endpointType` (endpoint type must not be negative).
113+
* Holds if `endpoint` is modeled as `endpointType`.
109114
*/
110115
predicate isKnownAs(
111116
Candidate::Endpoint endpoint, Candidate::EndpointType endpointType,
112117
EndpointCharacteristic characteristic
113118
) {
114119
// If the list of characteristics includes positive indicators with maximal confidence for this class, then it's a
115120
// known sink for the class.
116-
not endpointType instanceof Candidate::NegativeEndpointType and
117121
characteristic.appliesToEndpoint(endpoint) and
118122
characteristic.hasImplications(endpointType, true, maximalConfidence())
119123
}
@@ -125,7 +129,6 @@ module SharedCharacteristics<CandidateSig Candidate> {
125129
* A candidate is an endpoint that cannot be excluded from `endpointType` based on its characteristics.
126130
*/
127131
predicate isCandidate(Candidate::Endpoint endpoint, Candidate::EndpointType endpointType) {
128-
not endpointType instanceof Candidate::NegativeEndpointType and
129132
endpointType = endpoint.getAPotentialType() and
130133
not exists(getAnExcludingCharacteristic(endpoint, endpointType))
131134
}
@@ -143,26 +146,16 @@ module SharedCharacteristics<CandidateSig Candidate> {
143146
}
144147

145148
/**
146-
* Gets a characteristics that disbar `endpoint` from being a candidate for `endpointType`.
149+
* Gets a characteristics that disbar `endpoint` from being a candidate for `endpointType`
150+
* with at least medium confidence.
147151
*/
148152
EndpointCharacteristic getAnExcludingCharacteristic(
149153
Candidate::Endpoint endpoint, Candidate::EndpointType endpointType
150154
) {
151-
// An endpoint is a sink candidate if none of its characteristics give much indication whether or not it is a sink.
152-
not endpointType instanceof Candidate::NegativeEndpointType and
153155
result.appliesToEndpoint(endpoint) and
154-
(
155-
// Exclude endpoints that have a characteristic that implies they're not sinks for _any_ sink type.
156-
exists(float confidence |
157-
confidence >= mediumConfidence() and
158-
result.hasImplications(any(Candidate::NegativeEndpointType t), true, confidence)
159-
)
160-
or
161-
// Exclude endpoints that have a characteristic that implies they're not sinks for _this particular_ sink type.
162-
exists(float confidence |
163-
confidence >= mediumConfidence() and
164-
result.hasImplications(endpointType, false, confidence)
165-
)
156+
exists(float confidence |
157+
confidence >= mediumConfidence() and
158+
result.hasImplications(endpointType, false, confidence)
166159
)
167160
}
168161

@@ -253,8 +246,8 @@ module SharedCharacteristics<CandidateSig Candidate> {
253246
override predicate hasImplications(
254247
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
255248
) {
256-
endpointType instanceof Candidate::NegativeEndpointType and
257-
isPositiveIndicator = true and
249+
endpointType instanceof Candidate::SinkType and
250+
isPositiveIndicator = false and
258251
confidence = highConfidence()
259252
}
260253
}
@@ -272,8 +265,8 @@ module SharedCharacteristics<CandidateSig Candidate> {
272265
override predicate hasImplications(
273266
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
274267
) {
275-
endpointType instanceof Candidate::NegativeEndpointType and
276-
isPositiveIndicator = true and
268+
endpointType instanceof Candidate::SinkType and
269+
isPositiveIndicator = false and
277270
confidence = mediumConfidence()
278271
}
279272
}
@@ -293,8 +286,8 @@ module SharedCharacteristics<CandidateSig Candidate> {
293286
override predicate hasImplications(
294287
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
295288
) {
296-
endpointType instanceof Candidate::NegativeEndpointType and
297-
isPositiveIndicator = true and
289+
endpointType instanceof Candidate::SinkType and
290+
isPositiveIndicator = false and
298291
confidence = mediumConfidence()
299292
}
300293
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
| Test.java:36:10:38:3 | newInputStream(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:36:10:38:3 | newInputStream(...) | CallContext | Test.java:36:10:38:3 | newInputStream(...) | MethodDoc | Test.java:36:10:38:3 | newInputStream(...) | ClassDoc | 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://:1:1:1:1 | | 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 |
88
| Test.java:37:4:37:11 | openPath | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:36:10:38:3 | newInputStream(...) | CallContext | Test.java:37:4:37:11 | openPath | MethodDoc | Test.java:37:4:37:11 | openPath | ClassDoc | 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 | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://ai-manual:1:1:1:1 | ai-manual | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
99
| Test.java:43:4:43:22 | get(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:43:4:43:22 | get(...) | CallContext | Test.java:43:4:43:22 | get(...) | MethodDoc | Test.java:43:4:43:22 | get(...) | ClassDoc | 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://:1:1:1:1 | | 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 |
10+
| Test.java:48:10:50:3 | compareTo(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:48:10:50:3 | compareTo(...) | CallContext | Test.java:48:10:50:3 | compareTo(...) | MethodDoc | Test.java:48:10:50:3 | compareTo(...) | ClassDoc | 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://:1:1:1:1 | | 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 |
1011
| Test.java:54:3:59:3 | walk(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:54:3:59:3 | walk(...) | CallContext | Test.java:54:3:59:3 | walk(...) | MethodDoc | Test.java:54:3:59:3 | walk(...) | ClassDoc | 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://:1:1:1:1 | | 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 |
1112
| Test.java:56:4:56:4 | o | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:54:3:59:3 | walk(...) | CallContext | Test.java:54:3:59:3 | walk(...) | MethodDoc | Test.java:54:3:59:3 | walk(...) | ClassDoc | 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 |
1213
| Test.java:63:3:63:3 | c | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:63:3:63:20 | getInputStream(...) | CallContext | Test.java:63:3:63:3 | c | MethodDoc | Test.java:63:3:63:3 | c | ClassDoc | 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 |
1314
| Test.java:68:30:68:47 | writer | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:68:30:68:47 | writer | CallContext | Test.java:68:30:68:47 | writer | MethodDoc | Test.java:68:30:68:47 | writer | ClassDoc | file://java.lang:1:1:1:1 | java.lang | package | file://Throwable:1:1:1:1 | Throwable | type | file://true:1:1:1:1 | true | subtypes | file://printStackTrace:1:1:1:1 | printStackTrace | name | file://(PrintWriter):1:1:1:1 | (PrintWriter) | 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 |
1415
| Test.java:86:3:88:3 | list(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:86:3:88:3 | list(...) | CallContext | Test.java:86:3:88:3 | list(...) | MethodDoc | Test.java:86:3:88:3 | list(...) | ClassDoc | 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://list:1:1:1:1 | list | name | file://(Path):1:1:1:1 | (Path) | signature | file://:1:1:1:1 | | 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 |
16+
| Test.java:87:4:87:29 | createDirectories(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:87:4:87:29 | createDirectories(...) | CallContext | Test.java:87:4:87:29 | createDirectories(...) | MethodDoc | Test.java:87:4:87:29 | createDirectories(...) | ClassDoc | 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://createDirectories:1:1:1:1 | createDirectories | name | file://(Path,FileAttribute[]):1:1:1:1 | (Path,FileAttribute[]) | signature | file://:1:1:1:1 | | 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 |
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
| Test.java:48:10:50:3 | compareTo(...) | known sanitizer\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:48:10:50:3 | compareTo(...) | CallContext | Test.java:48:10:50:3 | compareTo(...) | MethodDoc | Test.java:48:10:50:3 | compareTo(...) | ClassDoc | 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://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
21
| Test.java:49:4:49:5 | f2 | known non-sink\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:48:10:50:3 | compareTo(...) | CallContext | Test.java:49:4:49:5 | f2 | MethodDoc | Test.java:49:4:49:5 | f2 | ClassDoc | 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 | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
32
| Test.java:55:4:55:4 | p | taint step\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:54:3:59:3 | walk(...) | CallContext | Test.java:55:4:55:4 | p | MethodDoc | Test.java:55:4:55:4 | p | ClassDoc | 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 | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |

0 commit comments

Comments
 (0)