Skip to content

Commit 01e8109

Browse files
authored
Merge branch 'main' into ockers/certification_not_certificate
2 parents e477909 + 525f271 commit 01e8109

File tree

18 files changed

+493
-68
lines changed

18 files changed

+493
-68
lines changed

java/documentation/library-coverage/coverage.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ jakarta.xml.bind.attachment,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,
7777
java.awt,1,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,3
7878
java.beans,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
7979
java.io,50,1,46,,,,,,,,,22,,,,,,,,,,,,,,,28,,,,,,,,,,,,,,,,,,,,,1,,44,2
80-
java.lang,33,3,103,,13,,,,,,1,,,,,,,,,,,,8,,,,6,,,4,,,1,,,,,,,,,,,,,,3,,,60,43
80+
java.lang,33,3,101,,13,,,,,,1,,,,,,,,,,,,8,,,,6,,,4,,,1,,,,,,,,,,,,,,3,,,58,43
8181
java.net,21,3,23,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,19,,,,,,,,,,,,,3,23,
8282
java.nio,49,,36,,,,,,,,,5,,,,,,,,,,,,,,,43,,,,,,,,,1,,,,,,,,,,,,,,36,
8383
java.security,21,,,,,11,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

java/documentation/library-coverage/coverage.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ Java framework & library support
1818
`Google Guava <https://guava.dev/>`_,``com.google.common.*``,,730,43,9,,,,,
1919
JBoss Logging,``org.jboss.logging``,,,324,,,,,,
2020
`JSON-java <https://github.com/stleary/JSON-java>`_,``org.json``,,236,,,,,,,
21-
Java Standard Library,``java.*``,10,733,237,79,,9,,,24
21+
Java Standard Library,``java.*``,10,731,237,79,,9,,,24
2222
Java extensions,"``javax.*``, ``jakarta.*``",67,688,80,5,4,2,1,1,4
2323
Kotlin Standard Library,``kotlin*``,,1849,16,14,,,,,2
2424
`Spring <https://spring.io/>`_,``org.springframework.*``,38,481,118,5,,28,14,,35
2525
Others,"``actions.osgi``, ``antlr``, ``ch.ethz.ssh2``, ``cn.hutool.core.codec``, ``com.alibaba.druid.sql``, ``com.alibaba.fastjson2``, ``com.amazonaws.auth``, ``com.auth0.jwt.algorithms``, ``com.azure.identity``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.google.gson``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.microsoft.sqlserver.jdbc``, ``com.mitchellbosecke.pebble``, ``com.mongodb``, ``com.opensymphony.xwork2``, ``com.rabbitmq.client``, ``com.sshtools.j2ssh.authentication``, ``com.sun.crypto.provider``, ``com.sun.jndi.ldap``, ``com.sun.net.httpserver``, ``com.sun.net.ssl``, ``com.sun.rowset``, ``com.sun.security.auth.module``, ``com.sun.security.ntlm``, ``com.sun.security.sasl.digest``, ``com.thoughtworks.xstream``, ``com.trilead.ssh2``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.text``, ``groovy.util``, ``hudson``, ``io.jsonwebtoken``, ``io.netty.bootstrap``, ``io.netty.buffer``, ``io.netty.channel``, ``io.netty.handler.codec``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``liquibase.database.jvm``, ``liquibase.statement.core``, ``net.schmizz.sshj``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.cxf.catalog``, ``org.apache.cxf.common.classloader``, ``org.apache.cxf.common.jaxb``, ``org.apache.cxf.common.logging``, ``org.apache.cxf.configuration.jsse``, ``org.apache.cxf.helpers``, ``org.apache.cxf.resource``, ``org.apache.cxf.staxutils``, ``org.apache.cxf.tools.corba.utils``, ``org.apache.cxf.tools.util``, ``org.apache.cxf.transform``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hadoop.hive.ql.exec``, ``org.apache.hadoop.hive.ql.metadata``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.ibatis.mapping``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.shiro.mgt``, ``org.apache.sshd.client.session``, ``org.apache.struts.beanvalidation.validation.interceptor``, ``org.apache.struts2``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.cargo.container.installer``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.eclipse.jetty.client``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.gradle.api.file``, ``org.hibernate``, ``org.influxdb``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.jooq``, ``org.keycloak.models.map.storage``, ``org.kohsuke.stapler``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.owasp.esapi``, ``org.pac4j.jwt.config.encryption``, ``org.pac4j.jwt.config.signature``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``org.yaml.snakeyaml``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``, ``sun.jvmstat.perfdata.monitor.protocol.local``, ``sun.jvmstat.perfdata.monitor.protocol.rmi``, ``sun.misc``, ``sun.net.ftp``, ``sun.net.www.protocol.http``, ``sun.security.acl``, ``sun.security.jgss.krb5``, ``sun.security.krb5``, ``sun.security.pkcs``, ``sun.security.pkcs11``, ``sun.security.provider``, ``sun.security.ssl``, ``sun.security.x509``, ``sun.tools.jconsole``",131,10516,889,121,6,22,18,,208
26-
Totals,,308,18947,2551,331,16,128,33,1,407
26+
Totals,,308,18945,2551,331,16,128,33,1,407
2727

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/** Provides classes and predicates for working with Android layouts and UI elements. */
2+
3+
import java
4+
import semmle.code.xml.AndroidManifest
5+
private import semmle.code.java.dataflow.DataFlow
6+
7+
/** An Android Layout XML file. */
8+
class AndroidLayoutXmlFile extends XmlFile {
9+
AndroidLayoutXmlFile() { this.getRelativePath().matches("%/res/layout/%.xml") }
10+
}
11+
12+
/** A component declared in an Android layout file. */
13+
class AndroidLayoutXmlElement extends XmlElement {
14+
AndroidLayoutXmlElement() { this.getFile() instanceof AndroidLayoutXmlFile }
15+
16+
/** Gets the ID of this component, if any. */
17+
string getId() { result = this.getAttribute("id").getValue() }
18+
19+
/** Gets the class of this component. */
20+
Class getClass() {
21+
this.getName() = "view" and
22+
this.getAttribute("class").getValue() = result.getQualifiedName()
23+
or
24+
this.getName() = result.getQualifiedName()
25+
or
26+
result.hasQualifiedName(["android.widget", "android.view"], this.getName())
27+
}
28+
}
29+
30+
/** An XML element that represents an editable text field. */
31+
class AndroidEditableXmlElement extends AndroidLayoutXmlElement {
32+
AndroidEditableXmlElement() {
33+
this.getClass().getASourceSupertype*().hasQualifiedName("android.widget", "EditText")
34+
}
35+
36+
/** Gets the input type of this field, if any. */
37+
string getInputType() { result = this.getAttribute("inputType").(AndroidXmlAttribute).getValue() }
38+
}
39+
40+
/** A `findViewById` or `requireViewById` method on `Activity` or `View`. */
41+
private class FindViewMethod extends Method {
42+
FindViewMethod() {
43+
exists(Method m | this.getAnOverride*() = m |
44+
m.hasQualifiedName("android.app", "Activity", ["findViewById", "requireViewById"])
45+
or
46+
m.hasQualifiedName("android.view", "View", ["findViewById", "requireViewById"])
47+
or
48+
m.hasQualifiedName("android.app", "Dialog", ["findViewById", "requireViewById"])
49+
)
50+
}
51+
}
52+
53+
/** Gets a use of the view that has the given id. (that is, from a call to a method like `findViewById`) */
54+
MethodCall getAUseOfViewWithId(string id) {
55+
exists(string name, NestedClass r_id, Field id_field |
56+
id = ["@+id/", "@id/"] + name and
57+
result.getMethod() instanceof FindViewMethod and
58+
r_id.getEnclosingType().hasName("R") and
59+
r_id.hasName("id") and
60+
id_field.getDeclaringType() = r_id and
61+
id_field.hasName(name)
62+
|
63+
DataFlow::localExprFlow(id_field.getAnAccess(), result.getArgument(0))
64+
)
65+
}

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

Lines changed: 1 addition & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,71 +3,7 @@
33
import java
44
import semmle.code.java.dataflow.DataFlow
55
import semmle.code.java.security.SensitiveActions
6-
import semmle.code.xml.AndroidManifest
7-
8-
/** An Android Layout XML file. */
9-
private class AndroidLayoutXmlFile extends XmlFile {
10-
AndroidLayoutXmlFile() { this.getRelativePath().matches("%/res/layout/%.xml") }
11-
}
12-
13-
/** A component declared in an Android layout file. */
14-
class AndroidLayoutXmlElement extends XmlElement {
15-
AndroidXmlAttribute id;
16-
17-
AndroidLayoutXmlElement() {
18-
this.getFile() instanceof AndroidLayoutXmlFile and
19-
id = this.getAttribute("id")
20-
}
21-
22-
/** Gets the ID of this component. */
23-
string getId() { result = id.getValue() }
24-
25-
/** Gets the class of this component. */
26-
Class getClass() {
27-
this.getName() = "view" and
28-
this.getAttribute("class").getValue() = result.getQualifiedName()
29-
or
30-
this.getName() = result.getQualifiedName()
31-
or
32-
result.hasQualifiedName(["android.widget", "android.view"], this.getName())
33-
}
34-
}
35-
36-
/** An XML element that represents an editable text field. */
37-
class AndroidEditableXmlElement extends AndroidLayoutXmlElement {
38-
AndroidEditableXmlElement() {
39-
this.getClass().getASourceSupertype*().hasQualifiedName("android.widget", "EditText")
40-
}
41-
42-
/** Gets the input type of this field, if any. */
43-
string getInputType() { result = this.getAttribute("inputType").(AndroidXmlAttribute).getValue() }
44-
}
45-
46-
/** A `findViewById` or `requireViewById` method on `Activity` or `View`. */
47-
private class FindViewMethod extends Method {
48-
FindViewMethod() {
49-
this.hasQualifiedName("android.view", "View", ["findViewById", "requireViewById"])
50-
or
51-
exists(Method m |
52-
m.hasQualifiedName("android.app", "Activity", ["findViewById", "requireViewById"]) and
53-
this = m.getAnOverride*()
54-
)
55-
}
56-
}
57-
58-
/** Gets a use of the view that has the given id. */
59-
private MethodCall getAUseOfViewWithId(string id) {
60-
exists(string name, NestedClass r_id, Field id_field |
61-
id = "@+id/" + name and
62-
result.getMethod() instanceof FindViewMethod and
63-
r_id.getEnclosingType().hasName("R") and
64-
r_id.hasName("id") and
65-
id_field.getDeclaringType() = r_id and
66-
id_field.hasName(name)
67-
|
68-
DataFlow::localExprFlow(id_field.getAnAccess(), result.getArgument(0))
69-
)
70-
}
6+
import semmle.code.java.frameworks.android.Layout
717

728
/** Gets the argument of a use of `setInputType` called on the view with the given id. */
739
private Argument setInputTypeForId(string id) {

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

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import java
44
private import semmle.code.java.dataflow.ExternalFlow
55
private import semmle.code.java.dataflow.TaintTracking
66
private import semmle.code.java.security.SensitiveActions
7+
private import semmle.code.java.frameworks.android.Layout
8+
private import semmle.code.java.security.Sanitizers
79

810
/** A configuration for tracking sensitive information to system notifications. */
911
private module NotificationTrackingConfig implements DataFlow::ConfigSig {
@@ -20,3 +22,94 @@ private module NotificationTrackingConfig implements DataFlow::ConfigSig {
2022

2123
/** Taint tracking flow for sensitive data flowing to system notifications. */
2224
module NotificationTracking = TaintTracking::Global<NotificationTrackingConfig>;
25+
26+
/** A call to a method that sets the text of a `TextView`. */
27+
private class SetTextCall extends MethodCall {
28+
SetTextCall() {
29+
this.getMethod()
30+
.getAnOverride*()
31+
.hasQualifiedName("android.widget", "TextView", ["append", "setText", "setHint"]) and
32+
(
33+
this.getMethod()
34+
.getParameter(0)
35+
.getType()
36+
.(RefType)
37+
.hasQualifiedName("java.lang", "CharSequence")
38+
or
39+
this.getMethod().getParameter(0).getType().(Array).getElementType() instanceof CharacterType
40+
)
41+
}
42+
43+
/** Gets the string argument of this call. */
44+
Expr getStringArgument() { result = this.getArgument(0) }
45+
}
46+
47+
/** A call to a method indicating that the contents of a UI element are safely masked. */
48+
private class MaskCall extends MethodCall {
49+
MaskCall() {
50+
this.getMethod().getAnOverride*().hasQualifiedName("android.widget", "TextView", "setInputType")
51+
or
52+
this.getMethod().getAnOverride*().hasQualifiedName("android.view", "View", "setVisibility")
53+
}
54+
}
55+
56+
/** A configuration for tracking sensitive information to text fields. */
57+
private module TextFieldTrackingConfig implements DataFlow::ConfigSig {
58+
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SensitiveExpr }
59+
60+
predicate isSink(DataFlow::Node sink) {
61+
exists(SetTextCall call |
62+
sink.asExpr() = call.getStringArgument() and
63+
not setTextCallIsMasked(call)
64+
)
65+
}
66+
67+
predicate isBarrier(DataFlow::Node node) { node instanceof SimpleTypeSanitizer }
68+
69+
predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
70+
}
71+
72+
/** A local flow step that also flows through access to fields containing `View`s */
73+
private predicate localViewFieldFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
74+
DataFlow::localFlowStep(node1, node2)
75+
or
76+
exists(Field f |
77+
f.getType().(Class).getASupertype*().hasQualifiedName("android.view", "View") and
78+
node1.asExpr() = f.getAnAccess().(FieldWrite).getASource() and
79+
node2.asExpr() = f.getAnAccess().(FieldRead)
80+
)
81+
}
82+
83+
/** Holds if data can flow from `node1` to `node2` with local flow steps as well as flow through fields containing `View`s */
84+
private predicate localViewFieldFlow(DataFlow::Node node1, DataFlow::Node node2) {
85+
localViewFieldFlowStep*(node1, node2)
86+
}
87+
88+
/** Holds if data can flow from `e1` to `e2` with local flow steps as well as flow through fields containing `View`s */
89+
private predicate localViewFieldExprFlow(Expr e1, Expr e2) {
90+
localViewFieldFlow(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
91+
}
92+
93+
/** Holds if the given view may be properly masked. */
94+
private predicate viewIsMasked(AndroidLayoutXmlElement view) {
95+
localViewFieldExprFlow(getAUseOfViewWithId(view.getId()), any(MaskCall mcall).getQualifier())
96+
or
97+
view.getAttribute("inputType")
98+
.(AndroidXmlAttribute)
99+
.getValue()
100+
.regexpMatch("(?i).*(text|number)(web)?password.*")
101+
or
102+
view.getAttribute("visibility").(AndroidXmlAttribute).getValue().toLowerCase() =
103+
["invisible", "gone"]
104+
}
105+
106+
/** Holds if the qualifier of `call` may be properly masked. */
107+
private predicate setTextCallIsMasked(SetTextCall call) {
108+
exists(AndroidLayoutXmlElement view |
109+
localViewFieldExprFlow(getAUseOfViewWithId(view.getId()), call.getQualifier()) and
110+
viewIsMasked(view.getParent*())
111+
)
112+
}
113+
114+
/** Taint tracking flow for sensitive data flowing to text fields. */
115+
module TextFieldTracking = TaintTracking::Global<TextFieldTrackingConfig>;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
TextView pwView = getViewById(R.id.pw_text);
2+
pwView.setText("Your password is: " + password);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>
7+
Sensitive information such as passwords should not be displayed in UI components unless explicitly required, to mitigate shoulder-surfing attacks.
8+
</p>
9+
</overview>
10+
11+
<recommendation>
12+
<p>
13+
For editable text fields containing sensitive information, the <code>inputType</code> should be set to <code>textPassword</code> or similar to ensure it is properly masked.
14+
Otherwise, sensitive data that must be displayed should be hidden by default, and only revealed based on an explicit user action.
15+
</p>
16+
</recommendation>
17+
18+
<example>
19+
<p>
20+
In the following (bad) case, sensitive information in <code>password</code> is exposed to the <code>TextView</code>.
21+
</p>
22+
23+
<sample src="AndroidSensitiveTextBad.java"/>
24+
25+
<p>
26+
In the following (good) case, the user must press a button to reveal sensitive information.
27+
</p>
28+
29+
<sample src="AndroidSensitiveTextGood.java"/>
30+
</example>
31+
32+
<references>
33+
<li>
34+
OWASP Mobile Application Security: <a href="https://mas.owasp.org/MASTG/Android/0x05d-Testing-Data-Storage/#ui-components">Android Data Storage - UI Components</a>
35+
</li>
36+
</references>
37+
38+
</qhelp>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @name Exposure of sensitive information to UI text views.
3+
* @id java/android/sensitive-text
4+
* @kind path-problem
5+
* @description Sensitive information displayed in UI text views should be properly masked.
6+
* @problem.severity warning
7+
* @precision medium
8+
* @security-severity 6.5
9+
* @tags security
10+
* external/cwe/cwe-200
11+
*/
12+
13+
import java
14+
import java
15+
import semmle.code.java.security.SensitiveUiQuery
16+
import TextFieldTracking::PathGraph
17+
18+
from TextFieldTracking::PathNode source, TextFieldTracking::PathNode sink
19+
where TextFieldTracking::flowPath(source, sink)
20+
select sink, source, sink, "This $@ is exposed in a text view.", source, "sensitive information"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
TextView pwView = findViewById(R.id.pw_text);
2+
pwView.setVisibility(View.INVISIBLE);
3+
pwView.setText("Your password is: " + password);
4+
5+
Button showButton = findViewById(R.id.show_pw_button);
6+
showButton.setOnClickListener(new View.OnClickListener() {
7+
public void onClick(View v) {
8+
pwView.setVisibility(View.VISIBLE);
9+
}
10+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: newQuery
3+
---
4+
* Added a new query `java/android/sensitive-text` to detect instances of sensitive data being exposed through text fields without being properly masked.

0 commit comments

Comments
 (0)