Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit eab270e

Browse files
committed
Move the definitions of isEffectiveSink and getAReasonSinkExcluded to the base class.
They can now be implemented generically for all sink types.
1 parent fc56c5a commit eab270e

11 files changed

Lines changed: 44 additions & 269 deletions

File tree

javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/ATMConfig.qll

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
private import javascript as JS
88
import EndpointTypes
9-
import EndpointCharacteristics
9+
import EndpointCharacteristics as EndpointCharacteristics
1010

1111
/**
1212
* EXPERIMENTAL. This API may change in the future.
@@ -48,7 +48,7 @@ abstract class AtmConfig extends string {
4848
final predicate isKnownSink(JS::DataFlow::Node sink) {
4949
// If the list of characteristics includes positive indicators with maximal confidence for this class, then it's a
5050
// known sink for the class.
51-
exists(EndpointCharacteristic characteristic |
51+
exists(EndpointCharacteristics::EndpointCharacteristic characteristic |
5252
characteristic.getEndpoints(sink) and
5353
characteristic
5454
.getImplications(this.getASinkEndpointType(), true, characteristic.maximalConfidence())
@@ -69,7 +69,26 @@ abstract class AtmConfig extends string {
6969
* Holds if the candidate sink `candidateSink` predicted by the machine learning model should be
7070
* an effective sink, i.e. one considered as a possible sink of flow in the boosted query.
7171
*/
72-
predicate isEffectiveSink(JS::DataFlow::Node candidateSink) { none() }
72+
final predicate isEffectiveSink(JS::DataFlow::Node candidateSink) {
73+
not exists(getAReasonSinkExcluded(candidateSink))
74+
}
75+
76+
final EndpointCharacteristics::EndpointCharacteristic getAReasonSinkExcluded(
77+
JS::DataFlow::Node candidateSink
78+
) {
79+
// An endpoint is an effective sink if it has neither standard endpoint filter characteristics nor endpoint filter
80+
// characteristics that are specific to this sink type.
81+
exists(EndpointCharacteristics::StandardEndpointFilterCharacteristic standardFilter |
82+
standardFilter.getEndpoints(candidateSink) and
83+
result = standardFilter
84+
)
85+
or
86+
exists(EndpointCharacteristics::EndpointFilterCharacteristic specificFilter |
87+
specificFilter.getEndpoints(candidateSink) and
88+
specificFilter.getImplications(getASinkEndpointType(), false, _) and
89+
result = specificFilter
90+
)
91+
}
7392

7493
/**
7594
* EXPERIMENTAL. This API may change in the future.

javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/NosqlInjectionATM.qll

Lines changed: 0 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -11,92 +11,13 @@ import AdaptiveThreatModeling
1111
private import CoreKnowledge as CoreKnowledge
1212
private import StandardEndpointFilters as StandardEndpointFilters
1313

14-
module SinkEndpointFilter {
15-
/**
16-
* Provides a set of reasons why a given data flow node should be excluded as a sink candidate.
17-
*
18-
* If this predicate has no results for a sink candidate `n`, then we should treat `n` as an
19-
* effective sink.
20-
*/
21-
string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) {
22-
result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate)
23-
or
24-
exists(DataFlow::CallNode call | sinkCandidate = call.getAnArgument() |
25-
// additional databases accesses that aren't modeled yet
26-
call.(DataFlow::MethodCallNode).getMethodName() =
27-
["create", "createCollection", "createIndexes"] and
28-
result = "matches database access call heuristic"
29-
or
30-
// Remove modeled sinks
31-
CoreKnowledge::isArgumentToKnownLibrarySinkFunction(sinkCandidate) and
32-
result = "modeled sink"
33-
or
34-
// Remove common kinds of unlikely sinks
35-
CoreKnowledge::isKnownStepSrc(sinkCandidate) and
36-
result = "predecessor in a modeled flow step"
37-
or
38-
// Remove modeled database calls. Arguments to modeled calls are very likely to be modeled
39-
// as sinks if they are true positives. Therefore arguments that are not modeled as sinks
40-
// are unlikely to be true positives.
41-
call instanceof DatabaseAccess and
42-
result = "modeled database access"
43-
or
44-
// Remove calls to APIs that aren't relevant to NoSQL injection
45-
call.getReceiver() instanceof Http::RequestNode and
46-
result = "receiver is a HTTP request expression"
47-
or
48-
call.getReceiver() instanceof Http::ResponseNode and
49-
result = "receiver is a HTTP response expression"
50-
)
51-
or
52-
// Require NoSQL injection sink candidates to be (a) direct arguments to external library calls
53-
// or (b) heuristic sinks for NoSQL injection.
54-
//
55-
// ## Direct arguments to external library calls
56-
//
57-
// The `StandardEndpointFilters::flowsToArgumentOfLikelyExternalLibraryCall` endpoint filter
58-
// allows sink candidates which are within object literals or array literals, for example
59-
// `req.sendFile(_, { path: ENDPOINT })`.
60-
//
61-
// However, the NoSQL injection query deals differently with these types of sinks compared to
62-
// other security queries. Other security queries such as SQL injection tend to treat
63-
// `ENDPOINT` as the ground truth sink, but the NoSQL injection query instead treats
64-
// `{ path: ENDPOINT }` as the ground truth sink and defines an additional flow step to ensure
65-
// data flows from `ENDPOINT` to the ground truth sink `{ path: ENDPOINT }`.
66-
//
67-
// Therefore for the NoSQL injection boosted query, we must ignore sink candidates within object
68-
// literals or array literals, to avoid having multiple alerts for the same security
69-
// vulnerability (one FP where the sink is `ENDPOINT` and one TP where the sink is
70-
// `{ path: ENDPOINT }`). We accomplish this by directly testing that the sink candidate is an
71-
// argument of a likely external library call.
72-
//
73-
// ## Heuristic sinks
74-
//
75-
// We also allow heuristic sinks in addition to direct arguments to external library calls.
76-
// These are copied from the `HeuristicNosqlInjectionSink` class defined within
77-
// `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`.
78-
// We can't reuse the class because importing that file would cause us to treat these
79-
// heuristic sinks as known sinks.
80-
not sinkCandidate = StandardEndpointFilters::getALikelyExternalLibraryCall().getAnArgument() and
81-
not (
82-
isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)(nosql|query)") or
83-
isArgTo(sinkCandidate, "(?i)(query)")
84-
) and
85-
result = "not a direct argument to a likely external library call or a heuristic sink"
86-
}
87-
}
88-
8914
class NosqlInjectionAtmConfig extends AtmConfig {
9015
NosqlInjectionAtmConfig() { this = "NosqlInjectionATMConfig" }
9116

9217
override predicate isKnownSource(DataFlow::Node source) {
9318
source instanceof NosqlInjection::Source or TaintedObject::isSource(source, _)
9419
}
9520

96-
override predicate isEffectiveSink(DataFlow::Node sinkCandidate) {
97-
not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate))
98-
}
99-
10021
override EndpointType getASinkEndpointType() { result instanceof NosqlInjectionSinkType }
10122
}
10223

javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/SqlInjectionATM.qll

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,65 +10,11 @@ import AdaptiveThreatModeling
1010
import CoreKnowledge as CoreKnowledge
1111
import StandardEndpointFilters as StandardEndpointFilters
1212

13-
/**
14-
* This module provides logic to filter candidate sinks to those which are likely SQL injection
15-
* sinks.
16-
*/
17-
module SinkEndpointFilter {
18-
private import javascript
19-
private import SQL
20-
21-
/**
22-
* Provides a set of reasons why a given data flow node should be excluded as a sink candidate.
23-
*
24-
* If this predicate has no results for a sink candidate `n`, then we should treat `n` as an
25-
* effective sink.
26-
*/
27-
string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) {
28-
result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate)
29-
or
30-
exists(DataFlow::CallNode call | sinkCandidate = call.getAnArgument() |
31-
// prepared statements for SQL
32-
any(DataFlow::CallNode cn | cn.getCalleeName() = "prepare")
33-
.getAMethodCall("run")
34-
.getAnArgument() = sinkCandidate and
35-
result = "prepared SQL statement"
36-
or
37-
sinkCandidate instanceof DataFlow::ArrayCreationNode and
38-
result = "array creation"
39-
or
40-
// UI is unrelated to SQL
41-
call.getCalleeName().regexpMatch("(?i).*(render|html).*") and
42-
result = "HTML / rendering"
43-
)
44-
or
45-
// Require SQL injection sink candidates to be (a) arguments to external library calls
46-
// (possibly indirectly), or (b) heuristic sinks.
47-
//
48-
// Heuristic sinks are copied from the `HeuristicSqlInjectionSink` class defined within
49-
// `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`.
50-
// We can't reuse the class because importing that file would cause us to treat these
51-
// heuristic sinks as known sinks.
52-
not StandardEndpointFilters::flowsToArgumentOfLikelyExternalLibraryCall(sinkCandidate) and
53-
not (
54-
isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)(sql|query)") or
55-
isArgTo(sinkCandidate, "(?i)(query)") or
56-
isConcatenatedWithString(sinkCandidate,
57-
"(?s).*(ALTER|COUNT|CREATE|DATABASE|DELETE|DISTINCT|DROP|FROM|GROUP|INSERT|INTO|LIMIT|ORDER|SELECT|TABLE|UPDATE|WHERE).*")
58-
) and
59-
result = "not an argument to a likely external library call or a heuristic sink"
60-
}
61-
}
62-
6313
class SqlInjectionAtmConfig extends AtmConfig {
6414
SqlInjectionAtmConfig() { this = "SqlInjectionATMConfig" }
6515

6616
override predicate isKnownSource(DataFlow::Node source) { source instanceof SqlInjection::Source }
6717

68-
override predicate isEffectiveSink(DataFlow::Node sinkCandidate) {
69-
not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate))
70-
}
71-
7218
override EndpointType getASinkEndpointType() { result instanceof SqlInjectionSinkType }
7319
}
7420

javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StandardEndpointFilters.qll

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,6 @@ private import semmle.javascript.heuristics.SyntacticHeuristics
1212
private import CoreKnowledge as CoreKnowledge
1313
import EndpointCharacteristics as EndpointCharacteristics
1414

15-
/** Provides a set of reasons why a given data flow node should be excluded as a sink candidate. */
16-
string getAReasonSinkExcluded(DataFlow::Node n) {
17-
exists(EndpointCharacteristics::StandardEndpointFilterCharacteristic characteristic |
18-
characteristic.getEndpoints(n) and
19-
result = characteristic
20-
)
21-
}
22-
2315
/**
2416
* Holds if the node `n` is an argument to a function that has a manual model.
2517
*/
@@ -77,7 +69,7 @@ private DataFlow::SourceNode getACallback(DataFlow::ParameterNode p, DataFlow::T
7769
* Get calls for which we do not have the callee (i.e. the definition of the called function). This
7870
* acts as a heuristic for identifying calls to external library functions.
7971
*/
80-
private DataFlow::CallNode getACallWithoutCallee() {
72+
DataFlow::CallNode getACallWithoutCallee() {
8173
forall(Function callee | callee = result.getACallee() | callee.getTopLevel().isExterns()) and
8274
not exists(DataFlow::ParameterNode param, DataFlow::FunctionNode callback |
8375
param.flowsTo(result.getCalleeNode()) and

javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/TaintedPathATM.qll

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,64 +10,11 @@ import AdaptiveThreatModeling
1010
import CoreKnowledge as CoreKnowledge
1111
import StandardEndpointFilters as StandardEndpointFilters
1212

13-
/**
14-
* This module provides logic to filter candidate sinks to those which are likely path injection
15-
* sinks.
16-
*/
17-
module SinkEndpointFilter {
18-
private import javascript
19-
private import TaintedPath
20-
21-
/**
22-
* Provides a set of reasons why a given data flow node should be excluded as a sink candidate.
23-
*
24-
* If this predicate has no results for a sink candidate `n`, then we should treat `n` as an
25-
* effective sink.
26-
*/
27-
string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) {
28-
result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate)
29-
or
30-
// Require path injection sink candidates to be (a) arguments to external library calls
31-
// (possibly indirectly), or (b) heuristic sinks.
32-
//
33-
// Heuristic sinks are mostly copied from the `HeuristicTaintedPathSink` class defined within
34-
// `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`.
35-
// We can't reuse the class because importing that file would cause us to treat these
36-
// heuristic sinks as known sinks.
37-
not StandardEndpointFilters::flowsToArgumentOfLikelyExternalLibraryCall(sinkCandidate) and
38-
not (
39-
isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)(file|folder|dir|absolute)")
40-
or
41-
isArgTo(sinkCandidate, "(?i)(get|read)file")
42-
or
43-
exists(string pathPattern |
44-
// paths with at least two parts, and either a trailing or leading slash
45-
pathPattern = "(?i)([a-z0-9_.-]+/){2,}" or
46-
pathPattern = "(?i)(/[a-z0-9_.-]+){2,}"
47-
|
48-
isConcatenatedWithString(sinkCandidate, pathPattern)
49-
)
50-
or
51-
isConcatenatedWithStrings(".*/", sinkCandidate, "/.*")
52-
or
53-
// In addition to the names from `HeuristicTaintedPathSink` in the
54-
// `isAssignedToOrConcatenatedWith` predicate call above, we also allow the noisier "path"
55-
// name.
56-
isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)path")
57-
) and
58-
result = "not a direct argument to a likely external library call or a heuristic sink"
59-
}
60-
}
61-
6213
class TaintedPathAtmConfig extends AtmConfig {
6314
TaintedPathAtmConfig() { this = "TaintedPathATMConfig" }
6415

6516
override predicate isKnownSource(DataFlow::Node source) { source instanceof TaintedPath::Source }
6617

67-
override predicate isEffectiveSink(DataFlow::Node sinkCandidate) {
68-
not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate))
69-
}
70-
7118
override EndpointType getASinkEndpointType() { result instanceof TaintedPathSinkType }
7219
}
7320

javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssATM.qll

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,65 +10,11 @@ import AdaptiveThreatModeling
1010
import CoreKnowledge as CoreKnowledge
1111
import StandardEndpointFilters as StandardEndpointFilters
1212

13-
/**
14-
* This module provides logic to filter candidate sinks to those which are likely XSS sinks.
15-
*/
16-
module SinkEndpointFilter {
17-
private import javascript
18-
private import DomBasedXss
19-
20-
/**
21-
* Provides a set of reasons why a given data flow node should be excluded as a sink candidate.
22-
*
23-
* If this predicate has no results for a sink candidate `n`, then we should treat `n` as an
24-
* effective sink.
25-
*/
26-
string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) {
27-
result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate)
28-
or
29-
exists(DataFlow::CallNode call | sinkCandidate = call.getAnArgument() |
30-
call.getCalleeName() = "setState"
31-
) and
32-
result = "setState calls ought to be safe in react applications"
33-
or
34-
// Require XSS sink candidates to be (a) arguments to external library calls (possibly
35-
// indirectly), or (b) heuristic sinks.
36-
//
37-
// Heuristic sinks are copied from the `HeuristicDomBasedXssSink` class defined within
38-
// `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`.
39-
// We can't reuse the class because importing that file would cause us to treat these
40-
// heuristic sinks as known sinks.
41-
not StandardEndpointFilters::flowsToArgumentOfLikelyExternalLibraryCall(sinkCandidate) and
42-
not (
43-
isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)(html|innerhtml)")
44-
or
45-
isArgTo(sinkCandidate, "(?i)(html|render)")
46-
or
47-
sinkCandidate instanceof StringOps::HtmlConcatenationLeaf
48-
or
49-
isConcatenatedWithStrings("(?is).*<[a-z ]+.*", sinkCandidate, "(?s).*>.*")
50-
or
51-
// In addition to the heuristic sinks from `HeuristicDomBasedXssSink`, explicitly allow
52-
// property writes like `elem.innerHTML = <TAINT>` that may not be picked up as HTML
53-
// concatenation leaves.
54-
exists(DataFlow::PropWrite pw |
55-
pw.getPropertyName().regexpMatch("(?i).*html*") and
56-
pw.getRhs() = sinkCandidate
57-
)
58-
) and
59-
result = "not a direct argument to a likely external library call or a heuristic sink"
60-
}
61-
}
62-
6313
class DomBasedXssAtmConfig extends AtmConfig {
6414
DomBasedXssAtmConfig() { this = "DomBasedXssATMConfig" }
6515

6616
override predicate isKnownSource(DataFlow::Node source) { source instanceof DomBasedXss::Source }
6717

68-
override predicate isEffectiveSink(DataFlow::Node sinkCandidate) {
69-
not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate))
70-
}
71-
7218
override EndpointType getASinkEndpointType() { result instanceof XssSinkType }
7319
}
7420

javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/DebugResultInclusion.ql

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,23 @@
1212
import javascript
1313
import experimental.adaptivethreatmodeling.ATMConfig
1414
import extraction.ExtractEndpointDataTraining
15+
private import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
16+
private import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
17+
private import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
18+
private import experimental.adaptivethreatmodeling.XssATM as XssAtm
1519

1620
string getAReasonSinkExcluded(DataFlow::Node sinkCandidate, Query query) {
1721
query instanceof NosqlInjectionQuery and
18-
result = NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
22+
result = any(NosqlInjectionAtm::NosqlInjectionAtmConfig cfg).getAReasonSinkExcluded(sinkCandidate)
1923
or
2024
query instanceof SqlInjectionQuery and
21-
result = SqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
25+
result = any(SqlInjectionAtm::SqlInjectionAtmConfig cfg).getAReasonSinkExcluded(sinkCandidate)
2226
or
2327
query instanceof TaintedPathQuery and
24-
result = TaintedPathAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
28+
result = any(TaintedPathAtm::TaintedPathAtmConfig cfg).getAReasonSinkExcluded(sinkCandidate)
2529
or
2630
query instanceof XssQuery and
27-
result = XssAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
31+
result = any(XssAtm::DomBasedXssAtmConfig cfg).getAReasonSinkExcluded(sinkCandidate)
2832
}
2933

3034
pragma[inline]

0 commit comments

Comments
 (0)