Skip to content

Commit 773a98a

Browse files
authored
Merge pull request #18340 from jbj/diff-informed-getASelectedLocation
Java: make more queries diff-informed with getASelectedLocation
2 parents 5bfd22e + 2b1c70c commit 773a98a

13 files changed

+167
-5
lines changed

java/ql/lib/semmle/code/java/security/BrokenCryptoAlgorithmQuery.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ module InsecureCryptoConfig implements DataFlow::ConfigSig {
3131
predicate isSink(DataFlow::Node n) { exists(CryptoAlgoSpec c | n.asExpr() = c.getAlgoSpec()) }
3232

3333
predicate isBarrier(DataFlow::Node node) { node instanceof SimpleTypeSanitizer }
34+
35+
predicate observeDiffInformedIncrementalMode() { any() }
36+
37+
Location getASelectedSinkLocation(DataFlow::Node sink) {
38+
exists(CryptoAlgoSpec c | sink.asExpr() = c.getAlgoSpec() | result = c.getLocation())
39+
}
3440
}
3541

3642
/**

java/ql/lib/semmle/code/java/security/CommandLineQuery.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ module InputToArgumentToExecFlowConfig implements DataFlow::ConfigSig {
5858
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
5959
any(CommandInjectionAdditionalTaintStep s).step(n1, n2)
6060
}
61+
62+
// It's valid to use diff-informed data flow for this configuration because
63+
// the location of the selected element in the query is contained inside the
64+
// location of the sink. The query, as a predicate, is used negated in
65+
// another query, but that's only to prevent overlapping results between two
66+
// queries.
67+
predicate observeDiffInformedIncrementalMode() { any() }
6168
}
6269

6370
/**

java/ql/lib/semmle/code/java/security/ImproperIntentVerificationQuery.qll

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,25 @@ private module VerifiedIntentConfig implements DataFlow::ConfigSig {
2525
sink.asExpr() = ma.getQualifier()
2626
)
2727
}
28+
29+
predicate observeDiffInformedIncrementalMode() { any() }
30+
31+
Location getASelectedSourceLocation(DataFlow::Node src) {
32+
exists(AndroidReceiverXmlElement rec, OnReceiveMethod orm, SystemActionName sa |
33+
src.asParameter() = orm.getIntentParameter() and
34+
anySystemReceiver(rec, orm, sa)
35+
|
36+
result = rec.getLocation()
37+
or
38+
result = orm.getLocation()
39+
or
40+
result = sa.getLocation()
41+
)
42+
}
43+
44+
// All sinks are set to have no locations because sinks aren't selected in
45+
// the query. This effectively means that we're filtering on sources only.
46+
Location getASelectedSinkLocation(DataFlow::Node sink) { none() }
2847
}
2948

3049
private module VerifiedIntentFlow = DataFlow::Global<VerifiedIntentConfig>;
@@ -67,13 +86,20 @@ class SystemActionName extends AndroidActionXmlElement {
6786
string getSystemActionName() { result = name }
6887
}
6988

70-
/** Holds if the XML element `rec` declares a receiver `orm` to receive the system action named `sa` that doesn't verify intents it receives. */
71-
predicate unverifiedSystemReceiver(
72-
AndroidReceiverXmlElement rec, UnverifiedOnReceiveMethod orm, SystemActionName sa
89+
private predicate anySystemReceiver(
90+
AndroidReceiverXmlElement rec, OnReceiveMethod orm, SystemActionName sa
7391
) {
7492
exists(Class ormty |
7593
ormty = orm.getDeclaringType() and
7694
rec.getComponentName() = ["." + ormty.getName(), ormty.getQualifiedName()] and
7795
rec.getAnIntentFilterElement().getAnActionElement() = sa
7896
)
7997
}
98+
99+
/** Holds if the XML element `rec` declares a receiver `orm` to receive the system action named `sa` that doesn't verify intents it receives. */
100+
predicate unverifiedSystemReceiver(
101+
AndroidReceiverXmlElement rec, UnverifiedOnReceiveMethod orm, SystemActionName sa
102+
) {
103+
// The type of `orm` is different in these two predicates
104+
anySystemReceiver(rec, orm, sa)
105+
}

java/ql/lib/semmle/code/java/security/InsecureTrustManagerQuery.qll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ module InsecureTrustManagerConfig implements DataFlow::ConfigSig {
1818
node.getType() instanceof Array and
1919
c instanceof DataFlow::ArrayContent
2020
}
21+
22+
predicate observeDiffInformedIncrementalMode() { any() }
23+
24+
Location getASelectedSourceLocation(DataFlow::Node source) {
25+
isSource(source) and
26+
(
27+
result = source.getLocation()
28+
or
29+
result = source.asExpr().(ClassInstanceExpr).getConstructedType().getLocation()
30+
)
31+
}
2132
}
2233

2334
module InsecureTrustManagerFlow = DataFlow::Global<InsecureTrustManagerConfig>;

java/ql/lib/semmle/code/java/security/RandomQuery.qll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ private module PredictableSeedFlowConfig implements DataFlow::ConfigSig {
3737
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
3838
predictableCalcStep(node1.asExpr(), node2.asExpr())
3939
}
40+
41+
predicate observeDiffInformedIncrementalMode() { any() }
42+
43+
Location getASelectedSinkLocation(DataFlow::Node sink) {
44+
// This predicate matches `PredictableSeed.ql`, which is the only place
45+
// where `PredictableSeedFlowConfig` is used.
46+
exists(GetRandomData da, VarRead use |
47+
result = da.getLocation() and
48+
da.getQualifier() = use and
49+
isSeeding(sink.asExpr(), use)
50+
)
51+
}
4052
}
4153

4254
private module PredictableSeedFlow = DataFlow::Global<PredictableSeedFlowConfig>;

java/ql/lib/semmle/code/java/security/SqlInjectionQuery.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ module QueryInjectionFlowConfig implements DataFlow::ConfigSig {
2424
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
2525
any(AdditionalQueryInjectionTaintStep s).step(node1, node2)
2626
}
27+
28+
predicate observeDiffInformedIncrementalMode() { any() }
2729
}
2830

2931
/** Tracks flow of unvalidated user input that is used in SQL queries. */

java/ql/lib/semmle/code/java/security/TaintedPermissionsCheckQuery.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ module TaintedPermissionsCheckFlowConfig implements DataFlow::ConfigSig {
5959
predicate isSink(DataFlow::Node sink) {
6060
sink.asExpr() = any(PermissionsConstruction p).getInput()
6161
}
62+
63+
predicate observeDiffInformedIncrementalMode() { any() }
64+
65+
Location getASelectedSinkLocation(DataFlow::Node sink) {
66+
exists(PermissionsConstruction p |
67+
sink.asExpr() = p.getInput() and
68+
result = p.getLocation()
69+
)
70+
}
6271
}
6372

6473
/** Tracks flow from user input to a permissions check. */

java/ql/lib/semmle/code/java/security/UnsafeHostnameVerificationQuery.qll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,17 @@ module TrustAllHostnameVerifierConfig implements DataFlow::ConfigSig {
6565
"|(set)?(accept|trust|ignore|allow)(all|every|any)" +
6666
"|(use|do|enable)insecure|(set|do|use)?no.*(check|validation|verify|verification)|disable).*$")
6767
}
68+
69+
predicate observeDiffInformedIncrementalMode() { any() }
70+
71+
Location getASelectedSourceLocation(DataFlow::Node source) {
72+
isSource(source) and
73+
(
74+
result = source.getLocation()
75+
or
76+
result = source.asExpr().(ClassInstanceExpr).getConstructedType().getLocation()
77+
)
78+
}
6879
}
6980

7081
/** Data flow to model the flow of a `TrustAllHostnameVerifier` to a `set(Default)HostnameVerifier` call. */

java/ql/lib/semmle/code/java/security/regexp/PolynomialReDoSQuery.qll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ module PolynomialRedosConfig implements DataFlow::ConfigSig {
4747
node instanceof SimpleTypeSanitizer or
4848
node.asExpr().(MethodCall).getMethod() instanceof LengthRestrictedMethod
4949
}
50+
51+
predicate observeDiffInformedIncrementalMode() { any() }
52+
53+
Location getASelectedSinkLocation(DataFlow::Node sink) {
54+
exists(SuperlinearBackTracking::PolynomialBackTrackingTerm regexp |
55+
regexp.getRootTerm() = sink.(PolynomialRedosSink).getRegExp()
56+
|
57+
result = sink.getLocation()
58+
or
59+
result = regexp.getLocation()
60+
)
61+
}
5062
}
5163

5264
module PolynomialRedosFlow = TaintTracking::Global<PolynomialRedosConfig>;

java/ql/test/query-tests/security/CWE-297/UnsafeHostnameVerification.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
| UnsafeHostnameVerification.java:47:55:47:71 | ...->... | UnsafeHostnameVerification.java:47:55:47:71 | ...->... | UnsafeHostnameVerification.java:47:55:47:71 | ...->... | The $@ defined by $@ always accepts any certificate, even if the hostname does not match. | UnsafeHostnameVerification.java:47:55:47:71 | ...->... | hostname verifier | UnsafeHostnameVerification.java:47:55:47:71 | new HostnameVerifier(...) { ... } | this type |
55
| UnsafeHostnameVerification.java:81:55:81:62 | verifier | UnsafeHostnameVerification.java:66:37:80:9 | new (...) : new HostnameVerifier(...) { ... } | UnsafeHostnameVerification.java:81:55:81:62 | verifier | The $@ defined by $@ always accepts any certificate, even if the hostname does not match. | UnsafeHostnameVerification.java:66:37:80:9 | new (...) : new HostnameVerifier(...) { ... } | hostname verifier | UnsafeHostnameVerification.java:66:41:66:56 | new HostnameVerifier(...) { ... } | this type |
66
| UnsafeHostnameVerification.java:94:55:94:62 | verifier | UnsafeHostnameVerification.java:88:37:93:9 | new (...) : new HostnameVerifier(...) { ... } | UnsafeHostnameVerification.java:94:55:94:62 | verifier | The $@ defined by $@ always accepts any certificate, even if the hostname does not match. | UnsafeHostnameVerification.java:88:37:93:9 | new (...) : new HostnameVerifier(...) { ... } | hostname verifier | UnsafeHostnameVerification.java:88:41:88:56 | new HostnameVerifier(...) { ... } | this type |
7+
| UnsafeHostnameVerification.java:116:55:116:78 | new AlwaysTrueVerifier(...) | UnsafeHostnameVerification.java:116:55:116:78 | new AlwaysTrueVerifier(...) | UnsafeHostnameVerification.java:116:55:116:78 | new AlwaysTrueVerifier(...) | The $@ defined by $@ always accepts any certificate, even if the hostname does not match. | UnsafeHostnameVerification.java:116:55:116:78 | new AlwaysTrueVerifier(...) | hostname verifier | UnsafeHostnameVerification.java:104:26:104:43 | AlwaysTrueVerifier | this type |
78
edges
89
| UnsafeHostnameVerification.java:66:37:80:9 | new (...) : new HostnameVerifier(...) { ... } | UnsafeHostnameVerification.java:81:55:81:62 | verifier | provenance | Sink:MaD:1 |
910
| UnsafeHostnameVerification.java:88:37:93:9 | new (...) : new HostnameVerifier(...) { ... } | UnsafeHostnameVerification.java:94:55:94:62 | verifier | provenance | Sink:MaD:1 |
@@ -23,4 +24,5 @@ nodes
2324
| UnsafeHostnameVerification.java:94:55:94:62 | verifier | semmle.label | verifier |
2425
| UnsafeHostnameVerification.java:97:42:97:68 | ALLOW_ALL_HOSTNAME_VERIFIER : new HostnameVerifier(...) { ... } | semmle.label | ALLOW_ALL_HOSTNAME_VERIFIER : new HostnameVerifier(...) { ... } |
2526
| UnsafeHostnameVerification.java:97:72:102:5 | new (...) : new HostnameVerifier(...) { ... } | semmle.label | new (...) : new HostnameVerifier(...) { ... } |
27+
| UnsafeHostnameVerification.java:116:55:116:78 | new AlwaysTrueVerifier(...) | semmle.label | new AlwaysTrueVerifier(...) |
2628
subpaths

java/ql/test/query-tests/security/CWE-297/UnsafeHostnameVerification.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,20 @@ public boolean verify(String hostname, SSLSession session) {
100100
return true; // BAD, always returns true
101101
}
102102
};
103+
104+
private static class AlwaysTrueVerifier implements HostnameVerifier {
105+
@Override
106+
public boolean verify(String hostname, SSLSession session) {
107+
return true; // BAD, always returns true
108+
}
109+
}
110+
111+
/**
112+
* Same as testTrustAllHostnameOfAnonymousClass, but with a named class.
113+
* This is for testing the diff-informed functionality of the query.
114+
*/
115+
public void testTrustAllHostnameOfNamedClass() {
116+
HttpsURLConnection.setDefaultHostnameVerifier(new AlwaysTrueVerifier());
117+
}
118+
103119
}

shared/dataflow/codeql/dataflow/DataFlow.qll

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,28 @@ module Configs<LocationSig Location, InputSig<Location> Lang> {
442442
* are used directly in a query result.
443443
*/
444444
default predicate observeDiffInformedIncrementalMode() { none() }
445+
446+
/**
447+
* Gets a location that will be associated with the given `source` in a
448+
* diff-informed query that uses this configuration (see
449+
* `observeDiffInformedIncrementalMode`). By default, this is the location
450+
* of the source itself, but this predicate should include any locations
451+
* that are reported as the primary-location of the query or as an
452+
* additional location ("$@" interpolation). For a query that doesn't
453+
* report the source at all, this predicate can be `none()`.
454+
*/
455+
default Location getASelectedSourceLocation(Node source) { result = source.getLocation() }
456+
457+
/**
458+
* Gets a location that will be associated with the given `sink` in a
459+
* diff-informed query that uses this configuration (see
460+
* `observeDiffInformedIncrementalMode`). By default, this is the location
461+
* of the sink itself, but this predicate should include any locations
462+
* that are reported as the primary-location of the query or as an
463+
* additional location ("$@" interpolation). For a query that doesn't
464+
* report the sink at all, this predicate can be `none()`.
465+
*/
466+
default Location getASelectedSinkLocation(Node sink) { result = sink.getLocation() }
445467
}
446468

447469
/** An input configuration for data flow using flow state. */
@@ -569,6 +591,28 @@ module Configs<LocationSig Location, InputSig<Location> Lang> {
569591
* are used directly in a query result.
570592
*/
571593
default predicate observeDiffInformedIncrementalMode() { none() }
594+
595+
/**
596+
* Gets a location that will be associated with the given `source` in a
597+
* diff-informed query that uses this configuration (see
598+
* `observeDiffInformedIncrementalMode`). By default, this is the location
599+
* of the source itself, but this predicate should include any locations
600+
* that are reported as the primary-location of the query or as an
601+
* additional location ("$@" interpolation). For a query that doesn't
602+
* report the source at all, this predicate can be `none()`.
603+
*/
604+
default Location getASelectedSourceLocation(Node source) { result = source.getLocation() }
605+
606+
/**
607+
* Gets a location that will be associated with the given `sink` in a
608+
* diff-informed query that uses this configuration (see
609+
* `observeDiffInformedIncrementalMode`). By default, this is the location
610+
* of the sink itself, but this predicate should include any locations
611+
* that are reported as the primary-location of the query or as an
612+
* additional location ("$@" interpolation). For a query that doesn't
613+
* report the sink at all, this predicate can be `none()`.
614+
*/
615+
default Location getASelectedSinkLocation(Node sink) { result = sink.getLocation() }
572616
}
573617
}
574618

shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
137137
* are used directly in a query result.
138138
*/
139139
predicate observeDiffInformedIncrementalMode();
140+
141+
Location getASelectedSourceLocation(Node source);
142+
143+
Location getASelectedSinkLocation(Node sink);
140144
}
141145

142146
/**
@@ -177,7 +181,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
177181
private predicate isFilteredSource(Node source) {
178182
Config::isSource(source, _) and
179183
if Config::observeDiffInformedIncrementalMode()
180-
then AlertFiltering::filterByLocation(source.getLocation())
184+
then AlertFiltering::filterByLocation(Config::getASelectedSourceLocation(source))
181185
else any()
182186
}
183187

@@ -188,7 +192,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
188192
Config::isSink(sink)
189193
) and
190194
if Config::observeDiffInformedIncrementalMode()
191-
then AlertFiltering::filterByLocation(sink.getLocation())
195+
then AlertFiltering::filterByLocation(Config::getASelectedSinkLocation(sink))
192196
else any()
193197
}
194198

0 commit comments

Comments
 (0)