Merge branch 'main' into java/update-mad-decls-after-triage-2024-01-31T11-16-45

This commit is contained in:
Max Schaefer
2024-01-31 11:29:33 +00:00
committed by GitHub
265 changed files with 26602 additions and 6645 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added the `java.util.Date` and `java.util.UUID` classes to the list of types in the `SimpleTypeSanitizer` class in `semmle.code.java.security.Sanitizers`.

View File

@@ -0,0 +1,7 @@
---
category: minorAnalysis
---
* Added models for the following packages:
* com.fasterxml.jackson.databind
* javax.servlet

View File

@@ -31,6 +31,48 @@ extensions:
- ["android.app", "FragmentTransaction", True, "replace", "(int,Class,Bundle,String)", "", "Argument[1]", "fragment-injection", "manual"]
- ["android.app", "FragmentTransaction", True, "replace", "(int,Fragment)", "", "Argument[1]", "fragment-injection", "manual"]
- ["android.app", "FragmentTransaction", True, "replace", "(int,Fragment,String)", "", "Argument[1]", "fragment-injection", "manual"]
- ["android.app", "Notification$Action", True, "Action", "(int,CharSequence,PendingIntent)", "", "Argument[1]", "notification", "manual"]
- ["android.app", "Notification$Action$Builder", True, "Builder", "(Icon,CharSequence,PendingIntent)", "", "Argument[1]", "notification", "manual"]
- ["android.app", "Notification$Action$Builder", True, "Builder", "(int,CharSequence,PendingIntent)", "", "Argument[1]", "notification", "manual"]
- ["android.app", "Notification$Action$Builder", True, "addExtras", "(Bundle)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$BigPictureStyle", True, "setBigContentTitle", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$BigPictureStyle", True, "setContentDescription", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$BigPictureStyle", True, "setSummaryText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$BigTextStyle", True, "bigText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$BigTextStyle", True, "setBigContentTitle", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$BigTextStyle", True, "setSummaryText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "addAction", "(int,CharSequence,PendingIntent)", "", "Argument[1]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "addExtras", "(Bundle)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setCategory", "(String)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setChannelId", "(String)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setContent", "(RemoteViews)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setContentInfo", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setContentText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setContentTitle", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setCustomBigContentView", "(RemoteViews)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setCustomContentView", "(RemoteViews)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setCustomHeadsUpContentView", "(RemoteViews)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setExtras", "(Bundle)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setGroup", "(String)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setRemoteInputHistory", "(CharSequence[])", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setSettingsText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setSortKey", "(String)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setSubText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setTicker", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$Builder", True, "setTicker", "(CharSequence,RemoteViews)", "", "Argument[0..1]", "notification", "manual"]
- ["android.app", "Notification$CallStyle", True, "setVerificationText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$InboxStyle", True, "addLine", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$InboxStyle", True, "setBigContentTitle", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$InboxStyle", True, "setSummaryText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$MediaStyle", True, "setRemotePlaybackInfo", "(CharSequence,int,PendingIntent)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$MessagingStyle", True, "MessagingStyle", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$MessagingStyle", True, "addMessage", "(CharSequence,long,CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$MessagingStyle", True, "addMessage", "(CharSequence,long,CharSequence)", "", "Argument[2]", "notification", "manual"]
- ["android.app", "Notification$MessagingStyle", True, "addMessage", "(CharSequence,long,Person)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$MessagingStyle", True, "setConversationTitle", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$MessagingStyle$Message", True, "Message", "(CharSequence,long,CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "Notification$MessagingStyle$Message", True, "Message", "(CharSequence,long,CharSequence)", "", "Argument[2]", "notification", "manual"]
- ["android.app", "Notification$MessagingStyle$Message", True, "Message", "(CharSequence,long,Person)", "", "Argument[0]", "notification", "manual"]
- ["android.app", "NotificationManager", True, "notify", "(String,int,Notification)", "", "Argument[2]", "pending-intents", "manual"]
- ["android.app", "NotificationManager", True, "notify", "(int,Notification)", "", "Argument[1]", "pending-intents", "manual"]
- ["android.app", "NotificationManager", True, "notifyAsPackage", "(String,String,int,Notification)", "", "Argument[3]", "pending-intents", "manual"]
@@ -39,6 +81,7 @@ extensions:
- ["android.app", "PendingIntent", False, "send", "(Context,int,Intent,PendingIntent$OnFinished,Handler)", "", "Argument[2]", "pending-intents", "manual"]
- ["android.app", "PendingIntent", False, "send", "(Context,int,Intent,PendingIntent$OnFinished,Handler,String)", "", "Argument[2]", "pending-intents", "manual"]
- ["android.app", "PendingIntent", False, "send", "(Context,int,Intent,PendingIntent$OnFinished,Handler,String,Bundle)", "", "Argument[2]", "pending-intents", "manual"]
- addsTo:
pack: codeql/java-all
extensible: summaryModel

View File

@@ -7,8 +7,50 @@ extensions:
- ["androidx.core.app", "AlarmManagerCompat", True, "setAndAllowWhileIdle", "", "", "Argument[3]", "pending-intents", "manual"]
- ["androidx.core.app", "AlarmManagerCompat", True, "setExact", "", "", "Argument[3]", "pending-intents", "manual"]
- ["androidx.core.app", "AlarmManagerCompat", True, "setExactAndAllowWhileIdle", "", "", "Argument[3]", "pending-intents", "manual"]
- ["androidx.core.app", "NotificationCompat$Action", True, "Action", "(int,CharSequence,PendingIntent)", "", "Argument[1]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Action$Builder", True, "Builder", "(IconCompat,CharSequence,PendingIntent)", "", "Argument[1]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Action$Builder", True, "Builder", "(int,CharSequence,PendingIntent)", "", "Argument[1]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Action$Builder", True, "addExtras", "(Bundle)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$BigPictureStyle", True, "setBigContentTitle", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$BigPictureStyle", True, "setContentDescription", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$BigPictureStyle", True, "setSummaryText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$BigTextStyle", True, "bigText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$BigTextStyle", True, "setBigContentTitle", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$BigTextStyle", True, "setSummaryText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "addAction", "(int,CharSequence,PendingIntent)", "", "Argument[1]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "addExtras", "(Bundle)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setCategory", "(String)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setChannelId", "(String)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setContent", "(RemoteViews)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setContentInfo", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setContentText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setContentTitle", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setCustomBigContentView", "(RemoteViews)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setCustomContentView", "(RemoteViews)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setCustomHeadsUpContentView", "(RemoteViews)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setExtras", "(Bundle)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setGroup", "(String)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setRemoteInputHistory", "(CharSequence[])", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setSettingsText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setSortKey", "(String)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setSubText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setTicker", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$Builder", True, "setTicker", "(CharSequence,RemoteViews)", "", "Argument[0..1]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$CallStyle", True, "setVerificationText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$InboxStyle", True, "addLine", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$InboxStyle", True, "setBigContentTitle", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$InboxStyle", True, "setSummaryText", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$MessagingStyle", True, "addMessage", "(CharSequence,long,CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$MessagingStyle", True, "addMessage", "(CharSequence,long,CharSequence)", "", "Argument[2]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$MessagingStyle", True, "addMessage", "(CharSequence,long,Person)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$MessagingStyle", True, "setConversationTitle", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$MessagingStyle", True, "MessagingStyle", "(CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$MessagingStyle$Message", True, "Message", "(CharSequence,long,CharSequence)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$MessagingStyle$Message", True, "Message", "(CharSequence,long,CharSequence)", "", "Argument[2]", "notification", "manual"]
- ["androidx.core.app", "NotificationCompat$MessagingStyle$Message", True, "Message", "(CharSequence,long,Person)", "", "Argument[0]", "notification", "manual"]
- ["androidx.core.app", "NotificationManagerCompat", True, "notify", "(String,int,Notification)", "", "Argument[2]", "pending-intents", "manual"]
- ["androidx.core.app", "NotificationManagerCompat", True, "notify", "(int,Notification)", "", "Argument[1]", "pending-intents", "manual"]
- addsTo:
pack: codeql/java-all
extensible: summaryModel

View File

@@ -5,6 +5,8 @@ extensions:
data:
- ["com.fasterxml.jackson.databind", "ObjectMapper", True, "convertValue", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["com.fasterxml.jackson.databind", "ObjectMapper", False, "createParser", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["com.fasterxml.jackson.databind", "ObjectMapper", True, "readTree", "(URL)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"] # result is remote, but only user-controlled if the URL is
- ["com.fasterxml.jackson.databind", "ObjectMapper", True, "readValue", "(InputStream,Class)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["com.fasterxml.jackson.databind", "ObjectMapper", True, "valueToTree", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["com.fasterxml.jackson.databind", "ObjectMapper", True, "valueToTree", "", "", "Argument[0].MapValue", "ReturnValue", "taint", "manual"]
- ["com.fasterxml.jackson.databind", "ObjectMapper", True, "valueToTree", "", "", "Argument[0].MapValue.Element", "ReturnValue", "taint", "manual"]

View File

@@ -11,6 +11,10 @@ extensions:
data:
- ["java.net", "DatagramPacket", False, "DatagramPacket", "(byte[],int,InetAddress,int)", "", "Argument[2]", "request-forgery", "ai-manual"]
- ["java.net", "DatagramPacket", False, "DatagramPacket", "(byte[],int,int,InetAddress,int)", "", "Argument[3]", "request-forgery", "ai-manual"]
- ["java.net", "DatagramPacket", False, "DatagramPacket", "(byte[],int,SocketAddress,int)", "", "Argument[2]", "request-forgery", "ai-manual"]
- ["java.net", "DatagramPacket", False, "DatagramPacket", "(byte[],int,int,SocketAddress,int)", "", "Argument[3]", "request-forgery", "ai-manual"]
- ["java.net", "DatagramPacket", True, "setAddress", "(InetAddress)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["java.net", "DatagramPacket", True, "setSocketAddress", "(SocketAddress)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["java.net", "DatagramSocket", True, "connect", "(SocketAddress)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["java.net", "PasswordAuthentication", False, "PasswordAuthentication", "(String,char[])", "", "Argument[1]", "credentials-password", "hq-generated"]
- ["java.net", "Socket", True, "Socket", "(String,int)", "", "Argument[0]", "request-forgery", "ai-manual"]

View File

@@ -18,4 +18,4 @@ extensions:
pack: codeql/java-all
extensible: summaryModel
data:
- ["javax.servlet", "ServletRequest", True, "getParameter", "(String)", "", Argument[0], "ReturnValue", "taint", "ai-manual"]
- ["javax.servlet", "ServletRequest", False, "getRealPath", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]

View File

@@ -4,12 +4,15 @@ import java
private import semmle.code.java.dataflow.DataFlow
/**
* A node whose type is a simple type unlikely to carry taint, such as primitives or their boxed counterparts.
* A node whose type is a simple type unlikely to carry taint, such as primitives and their boxed counterparts,
* `java.util.UUID` and `java.util.Date`.
*/
class SimpleTypeSanitizer extends DataFlow::Node {
SimpleTypeSanitizer() {
this.getType() instanceof PrimitiveType or
this.getType() instanceof BoxedType or
this.getType() instanceof NumberType
this.getType() instanceof NumberType or
this.getType().(RefType).hasQualifiedName("java.util", "UUID") or
this.getType().(RefType).hasQualifiedName("java.util", "Date")
}
}

View File

@@ -0,0 +1,22 @@
/** Definitions for Android Sensitive UI queries */
import java
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.TaintTracking
private import semmle.code.java.security.SensitiveActions
/** A configuration for tracking sensitive information to system notifications. */
private module NotificationTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SensitiveExpr }
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "notification") }
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
isSink(node) and exists(c)
}
predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
/** Taint tracking flow for sensitive data flowing to system notifications. */
module NotificationTracking = TaintTracking::Global<NotificationTrackingConfig>;

View File

@@ -7,26 +7,28 @@
can result in sensitive information being revealed or deleted, or an attacker being able to influence
behavior by modifying unexpected files.</p>
<p>Paths that are naively constructed from data controlled by a user may contain unexpected special characters,
such as "..". Such a path may potentially point anywhere on the file system.</p>
<p>Paths that are naively constructed from data controlled by a user may be absolute paths, or may contain
unexpected special characters such as "..". Such a path could point anywhere on the file system.</p>
</overview>
<recommendation>
<p>Validate user input before using it to construct a file path.</p>
<p>The choice of validation depends on whether you want to allow the user to specify complex paths with
multiple components that may span multiple folders, or only simple filenames without a path component.</p>
<p>Common validation methods include checking that the normalized path is relative and does not contain
any ".." components, or checking that the path is contained within a safe folder. The method you should use depends
on how the path is used in the application, and whether the path should be a single path component.
</p>
<p>In the former case, a common strategy is to make sure that the constructed file path is contained within
a safe root folder, for example by checking that the path starts with the root folder. Additionally,
you need to ensure that the path does not contain any ".." components, since otherwise
even a path that starts with the root folder could be used to access files outside the root folder.</p>
<p>If the path should be a single path component (such as a file name), you can check for the existence
of any path separators ("/" or "\"), or ".." sequences in the input, and reject the input if any are found.
</p>
<p>In the latter case, if you want to ensure that the user input is interpreted as a simple filename without
a path component, you can remove all path separators ("/" or "\") and all ".." sequences from the input
before using it to construct a file path. Note that it is <i>not</i> sufficient to only remove "../" sequences:
for example, applying this filter to ".../...//" would still result in the string "../".</p>
<p>
Note that removing "../" sequences is <i>not</i> sufficient, since the input could still contain a path separator
followed by "..". For example, the input ".../...//" would still result in the string "../" if only "../" sequences
are removed.
</p>
<p>Finally, the simplest (but most restrictive) option is to use an allow list of safe patterns and make sure that
the user input matches one of these patterns.</p>
@@ -36,15 +38,22 @@ the user input matches one of these patterns.</p>
<p>In this example, a file name is read from a <code>java.net.Socket</code> and then used to access a file
and send it back over the socket. However, a malicious user could enter a file name anywhere on the file system,
such as "/etc/passwd".</p>
such as "/etc/passwd" or "../../../etc/passwd".</p>
<sample src="TaintedPath.java" />
<sample src="examples/TaintedPath.java" />
<p>Simply checking that the path is under a trusted location (such as a known public folder) is not enough,
however, since the path could contain relative components such as "..". To fix this, check that it does
not contain ".." and starts with the public folder.</p>
<p>
If the input should only be a file name, you can check that it doesn't contain any path separators or ".." sequences.
</p>
<sample src="TaintedPathGood.java" />
<sample src="examples/TaintedPathGoodNormalize.java" />
<p>
If the input should be within a specific directory, you can check that the resolved path
is still contained within that directory.
</p>
<sample src="examples/TaintedPathGoodFolder.java" />
</example>
<references>

View File

@@ -1,14 +0,0 @@
public void sendUserFileGood(Socket sock, String user) {
BufferedReader filenameReader = new BufferedReader(
new InputStreamReader(sock.getInputStream(), "UTF-8"));
String filename = filenameReader.readLine();
// GOOD: ensure that the file is in a designated folder in the user's home directory
if (!filename.contains("..") && filename.startsWith("/home/" + user + "/public/")) {
BufferedReader fileReader = new BufferedReader(new FileReader(filename));
String fileLine = fileReader.readLine();
while(fileLine != null) {
sock.getOutputStream().write(fileLine.getBytes());
fileLine = fileReader.readLine();
}
}
}

View File

@@ -45,12 +45,12 @@ the result is within the destination directory. If provided with a zip file cont
path like <code>..\sneaky-file</code>, then this file would be written outside the destination
directory.</p>
<sample src="ZipSlipBad.java" />
<sample src="examples/ZipSlipBad.java" />
<p>To fix this vulnerability, we need to verify that the normalized <code>file</code> still has
<code>destinationDir</code> as its prefix, and throw an exception if this is not the case.</p>
<sample src="ZipSlipGood.java" />
<sample src="examples/ZipSlipGood.java" />
</example>
<references>

View File

@@ -0,0 +1,19 @@
public void sendUserFileGood(Socket sock, String user) {
BufferedReader filenameReader = new BufferedReader(
new InputStreamReader(sock.getInputStream(), "UTF-8"));
String filename = filenameReader.readLine();
Path publicFolder = Paths.get("/home/" + user + "/public").normalize().toAbsolutePath();
Path filePath = publicFolder.resolve(filename).normalize().toAbsolutePath();
// GOOD: ensure that the path stays within the public folder
if (!filePath.startsWith(publicFolder + File.separator)) {
throw new IllegalArgumentException("Invalid filename");
}
BufferedReader fileReader = new BufferedReader(new FileReader(filePath.toString()));
String fileLine = fileReader.readLine();
while(fileLine != null) {
sock.getOutputStream().write(fileLine.getBytes());
fileLine = fileReader.readLine();
}
}

View File

@@ -0,0 +1,15 @@
public void sendUserFileGood(Socket sock, String user) {
BufferedReader filenameReader = new BufferedReader(
new InputStreamReader(sock.getInputStream(), "UTF-8"));
String filename = filenameReader.readLine();
// GOOD: ensure that the filename has no path separators or parent directory references
if (filename.contains("..") || filename.contains("/") || filename.contains("\\")) {
throw new IllegalArgumentException("Invalid filename");
}
BufferedReader fileReader = new BufferedReader(new FileReader(filename));
String fileLine = fileReader.readLine();
while(fileLine != null) {
sock.getOutputStream().write(fileLine.getBytes());
fileLine = fileReader.readLine();
}
}

View File

@@ -0,0 +1,8 @@
// BAD: `password` is exposed in a notification.
void confirmPassword(String password) {
NotificationManager manager = NotificationManager.from(this);
manager.send(
new Notification.Builder(this, CHANNEL_ID)
.setContentText("Your password is: " + password)
.build());
}

View File

@@ -0,0 +1,34 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Sensitive information such as passwords or two-factor authentication (2FA) codes should not be exposed in a system notification.
Notifications should not be considered secure, as other untrusted applications may be able to use a
<code>NotificationListenerService</code> to read the contents of notifications.
</p>
</overview>
<recommendation>
<p>
Do not expose sensitive data in notifications.
</p>
</recommendation>
<example>
<p>
In the following sample, the <code>password</code> is sent as part of a notification.
This can allow another application to read this password.
</p>
<sample src="AndroidSensitiveNotifications.java"/>
</example>
<references>
<li>
OWASP Mobile Application Security: <a href="https://mas.owasp.org/MASTG/Android/0x05d-Testing-Data-Storage/#app-notifications">Android Data Storage - Application Notifications</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,21 @@
/**
* @name Exposure of sensitive information to notifications
* @id java/android/sensitive-notification
* @kind path-problem
* @description Sensitive information exposed in a system notification can be read by an unauthorized application.
* @problem.severity error
* @precision medium
* @security-severity 6.5
* @tags security
* external/cwe/cwe-200
*/
import java
import java
import semmle.code.java.security.SensitiveUiQuery
import NotificationTracking::PathGraph
from NotificationTracking::PathNode source, NotificationTracking::PathNode sink
where NotificationTracking::flowPath(source, sink)
select sink, source, sink, "This $@ is exposed in a system notification.", source,
"sensitive information"

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* Added a new query `java/android/sensitive-notification` to detect instances of sensitive data being exposed through Android notifications.

View File

@@ -1,9 +1,5 @@
## 0.8.6
### Deprecated Queries
* The three queries `java/insufficient-key-size`, `java/server-side-template-injection`, and `java/android/implicit-pendingintents` had accidentally general extension points allowing arbitrary string-based flow state. This has been fixed and the old extension points have been deprecated where possible, and otherwise updated.
### New Queries
* Added the `java/insecure-randomness` query to detect uses of weakly random values which an attacker may be able to predict. Also added the `crypto-parameter` sink kind for sinks which represent the parameters and keys of cryptographic operations.
@@ -13,3 +9,7 @@
* Modified the `java/potentially-weak-cryptographic-algorithm` query to include the use of weak cryptographic algorithms from configuration values specified in properties files.
* The query `java/android/missing-certificate-pinning` should no longer alert about requests pointing to the local filesystem.
* Removed some spurious sinks related to `com.opensymphony.xwork2.TextProvider.getText` from the query `java/ognl-injection`.
### Bug Fixes
* The three queries `java/insufficient-key-size`, `java/server-side-template-injection`, and `java/android/implicit-pendingintents` had accidentally general extension points allowing arbitrary string-based flow state. This has been fixed and the old extension points have been deprecated where possible, and otherwise updated.

View File

@@ -15,68 +15,27 @@ comments
| comments.kt:71:9:73:11 | /**\n * An anonymous function comment\n */ | /**\n * An anonymous function comment\n */ |
| comments.kt:79:9:81:11 | /**\n * A local function comment\n */ | /**\n * A local function comment\n */ |
| comments.kt:88:10:90:11 | /**\n * An anonymous object comment\n */ | /**\n * An anonymous object comment\n */ |
| comments.kt:95:1:95:163 | // Diagnostic Matches: % Couldn't get owner of KDoc. The comment is extracted without an owner. ...while extracting a file (comments.kt) at %comments.kt:1:1:96:0% | // Diagnostic Matches: % Couldn't get owner of KDoc. The comment is extracted without an owner. ...while extracting a file (comments.kt) at %comments.kt:1:1:96:0% |
commentOwners
| comments.kt:4:1:11:3 | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ | comments.kt:12:1:31:1 | Group |
| comments.kt:4:1:11:3 | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ | comments.kt:12:1:31:1 | Group |
| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:5:17:46 | getMembers$private |
| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:5:17:46 | members |
| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:5:17:46 | members |
| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:13:17:46 | getMembers$private |
| comments.kt:19:5:22:7 | /**\n * Adds a [member] to this group.\n * @return the new size of the group.\n */ | comments.kt:23:5:26:5 | add |
| comments.kt:35:5:35:34 | /** Medium is in the middle */ | comments.kt:36:5:36:14 | Medium |
| comments.kt:37:5:37:23 | /** This is high */ | comments.kt:38:5:38:11 | High |
| comments.kt:48:1:50:3 | /**\n * A type alias comment\n */ | comments.kt:51:1:51:24 | MyType |
| comments.kt:54:5:56:7 | /**\n * An init block comment\n */ | comments.kt:53:1:58:1 | InitBlock |
| comments.kt:61:5:63:7 | /**\n * A prop comment\n */ | comments.kt:64:5:68:17 | prop |
| comments.kt:65:9:67:11 | /**\n * An accessor comment\n */ | comments.kt:68:9:68:17 | getProp |
| comments.kt:71:9:73:11 | /**\n * An anonymous function comment\n */ | comments.kt:70:5:76:10 | getL |
| comments.kt:71:9:73:11 | /**\n * An anonymous function comment\n */ | comments.kt:70:5:76:10 | l |
| comments.kt:71:9:73:11 | /**\n * An anonymous function comment\n */ | comments.kt:70:5:76:10 | l |
| comments.kt:79:9:81:11 | /**\n * A local function comment\n */ | comments.kt:82:9:82:24 | localFn |
| comments.kt:88:10:90:11 | /**\n * An anonymous object comment\n */ | comments.kt:87:15:92:5 | |
| comments.kt:88:10:90:11 | /**\n * An anonymous object comment\n */ | comments.kt:87:15:92:5 | new X(...) { ... } |
commentNoOwners
| comments.kt:1:1:1:25 | /** Kdoc with no owner */ |
| comments.kt:24:9:24:25 | // A line comment |
| comments.kt:28:5:30:6 | /*\n A block comment\n */ |
| comments.kt:35:5:35:34 | /** Medium is in the middle */ |
| comments.kt:37:5:37:23 | /** This is high */ |
| comments.kt:42:5:44:7 | /**\n * A variable.\n */ |
| comments.kt:95:1:95:163 | // Diagnostic Matches: % Couldn't get owner of KDoc. The comment is extracted without an owner. ...while extracting a file (comments.kt) at %comments.kt:1:1:96:0% |
| comments.kt:48:1:50:3 | /**\n * A type alias comment\n */ |
| comments.kt:54:5:56:7 | /**\n * An init block comment\n */ |
| comments.kt:71:9:73:11 | /**\n * An anonymous function comment\n */ |
commentSections
| comments.kt:1:1:1:25 | /** Kdoc with no owner */ | Kdoc with no owner |
| comments.kt:4:1:11:3 | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ | A group of *members*.\n\nThis class has no useful logic; it's just a documentation example.\n\n |
| comments.kt:4:1:11:3 | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ | Creates an empty group. |
| comments.kt:4:1:11:3 | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ | the name of this group. |
| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | Members of this group. |
| comments.kt:19:5:22:7 | /**\n * Adds a [member] to this group.\n * @return the new size of the group.\n */ | Adds a [member] to this group.\n |
| comments.kt:35:5:35:34 | /** Medium is in the middle */ | Medium is in the middle |
| comments.kt:37:5:37:23 | /** This is high */ | This is high |
| comments.kt:42:5:44:7 | /**\n * A variable.\n */ | A variable. |
| comments.kt:48:1:50:3 | /**\n * A type alias comment\n */ | A type alias comment |
| comments.kt:54:5:56:7 | /**\n * An init block comment\n */ | An init block comment |
| comments.kt:61:5:63:7 | /**\n * A prop comment\n */ | A prop comment |
| comments.kt:65:9:67:11 | /**\n * An accessor comment\n */ | An accessor comment |
| comments.kt:71:9:73:11 | /**\n * An anonymous function comment\n */ | An anonymous function comment |
| comments.kt:79:9:81:11 | /**\n * A local function comment\n */ | A local function comment |
| comments.kt:88:10:90:11 | /**\n * An anonymous object comment\n */ | An anonymous object comment |
commentSectionContents
| A group of *members*.\n\nThis class has no useful logic; it's just a documentation example.\n\n | A group of *members*.\n\nThis class has no useful logic; it's just a documentation example.\n\n |
| A local function comment | A local function comment |
| A prop comment | A prop comment |
| A type alias comment | A type alias comment |
| A variable. | A variable. |
| Adds a [member] to this group.\n | Adds a [member] to this group.\n |
| An accessor comment | An accessor comment |
| An anonymous function comment | An anonymous function comment |
| An anonymous object comment | An anonymous object comment |
| An init block comment | An init block comment |
| Creates an empty group. | Creates an empty group. |
| Kdoc with no owner | Kdoc with no owner |
| Medium is in the middle | Medium is in the middle |
| Members of this group. | Members of this group. |
| This is high | This is high |
| the name of this group. | the name of this group. |
commentSectionNames
| Creates an empty group. | constructor |
| the name of this group. | property |
commentSectionSubjectNames
| the name of this group. | name |

View File

@@ -91,5 +91,3 @@ class XX {
X() {
}
}
// Diagnostic Matches: % Couldn't get owner of KDoc. The comment is extracted without an owner. ...while extracting a file (comments.kt) at %comments.kt:1:1:96:0%

View File

@@ -942,14 +942,28 @@ public class Test {
// "androidx.core.app;NotificationCompat$BigPictureStyle;true;bigLargeIcon;;;Argument[this];ReturnValue;value;manual"
NotificationCompat.BigPictureStyle out = null;
NotificationCompat.BigPictureStyle in = (NotificationCompat.BigPictureStyle) source();
out = in.bigLargeIcon(null);
out = in.bigLargeIcon((Bitmap)null);
sink(out); // $ hasValueFlow
}
{
// "androidx.core.app;NotificationCompat$BigPictureStyle;true;bigLargeIcon;;;Argument[this];ReturnValue;value;manual"
NotificationCompat.BigPictureStyle out = null;
NotificationCompat.BigPictureStyle in = (NotificationCompat.BigPictureStyle) source();
out = in.bigLargeIcon((Icon)null);
sink(out); // $ hasValueFlow
}
{
// "androidx.core.app;NotificationCompat$BigPictureStyle;true;bigPicture;;;Argument[this];ReturnValue;value;manual"
NotificationCompat.BigPictureStyle out = null;
NotificationCompat.BigPictureStyle in = (NotificationCompat.BigPictureStyle) source();
out = in.bigPicture(null);
out = in.bigPicture((Bitmap)null);
sink(out); // $ hasValueFlow
}
{
// "androidx.core.app;NotificationCompat$BigPictureStyle;true;bigPicture;;;Argument[this];ReturnValue;value;manual"
NotificationCompat.BigPictureStyle out = null;
NotificationCompat.BigPictureStyle in = (NotificationCompat.BigPictureStyle) source();
out = in.bigPicture((Icon)null);
sink(out); // $ hasValueFlow
}
{
@@ -1040,7 +1054,14 @@ public class Test {
// "androidx.core.app;NotificationCompat$Builder;true;addPerson;;;Argument[this];ReturnValue;value;manual"
NotificationCompat.Builder out = null;
NotificationCompat.Builder in = (NotificationCompat.Builder) source();
out = in.addPerson(null);
out = in.addPerson((androidx.core.app.Person)null);
sink(out); // $ hasValueFlow
}
{
// "androidx.core.app;NotificationCompat$Builder;true;addPerson;;;Argument[this];ReturnValue;value;manual"
NotificationCompat.Builder out = null;
NotificationCompat.Builder in = (NotificationCompat.Builder) source();
out = in.addPerson((String)null);
sink(out); // $ hasValueFlow
}
{
@@ -1252,7 +1273,14 @@ public class Test {
// "androidx.core.app;NotificationCompat$Builder;true;setLargeIcon;;;Argument[this];ReturnValue;value;manual"
NotificationCompat.Builder out = null;
NotificationCompat.Builder in = (NotificationCompat.Builder) source();
out = in.setLargeIcon(null);
out = in.setLargeIcon((Bitmap)null);
sink(out); // $ hasValueFlow
}
{
// "androidx.core.app;NotificationCompat$Builder;true;setLargeIcon;;;Argument[this];ReturnValue;value;manual"
NotificationCompat.Builder out = null;
NotificationCompat.Builder in = (NotificationCompat.Builder) source();
out = in.setLargeIcon((Icon)null);
sink(out); // $ hasValueFlow
}
{

View File

@@ -1,9 +1,14 @@
edges
| TaintedPath.java:11:38:11:110 | new BufferedReader(...) : BufferedReader | TaintedPath.java:12:24:12:37 | filenameReader : BufferedReader |
| TaintedPath.java:11:57:11:109 | new InputStreamReader(...) : InputStreamReader | TaintedPath.java:11:38:11:110 | new BufferedReader(...) : BufferedReader |
| TaintedPath.java:11:79:11:99 | getInputStream(...) : InputStream | TaintedPath.java:11:57:11:109 | new InputStreamReader(...) : InputStreamReader |
| TaintedPath.java:12:24:12:37 | filenameReader : BufferedReader | TaintedPath.java:12:24:12:48 | readLine(...) : String |
| TaintedPath.java:12:24:12:48 | readLine(...) : String | TaintedPath.java:14:68:14:75 | filename |
| TaintedPath.java:12:38:12:110 | new BufferedReader(...) : BufferedReader | TaintedPath.java:13:24:13:37 | filenameReader : BufferedReader |
| TaintedPath.java:12:57:12:109 | new InputStreamReader(...) : InputStreamReader | TaintedPath.java:12:38:12:110 | new BufferedReader(...) : BufferedReader |
| TaintedPath.java:12:79:12:99 | getInputStream(...) : InputStream | TaintedPath.java:12:57:12:109 | new InputStreamReader(...) : InputStreamReader |
| TaintedPath.java:13:24:13:37 | filenameReader : BufferedReader | TaintedPath.java:13:24:13:48 | readLine(...) : String |
| TaintedPath.java:13:24:13:48 | readLine(...) : String | TaintedPath.java:15:68:15:75 | filename |
| TaintedPath.java:38:41:39:70 | new BufferedReader(...) : BufferedReader | TaintedPath.java:40:27:40:40 | filenameReader : BufferedReader |
| TaintedPath.java:39:17:39:69 | new InputStreamReader(...) : InputStreamReader | TaintedPath.java:38:41:39:70 | new BufferedReader(...) : BufferedReader |
| TaintedPath.java:39:39:39:59 | getInputStream(...) : InputStream | TaintedPath.java:39:17:39:69 | new InputStreamReader(...) : InputStreamReader |
| TaintedPath.java:40:27:40:40 | filenameReader : BufferedReader | TaintedPath.java:40:27:40:51 | readLine(...) : String |
| TaintedPath.java:40:27:40:51 | readLine(...) : String | TaintedPath.java:43:46:43:53 | filename |
| Test.java:19:18:19:38 | getHostName(...) : String | Test.java:24:20:24:23 | temp |
| Test.java:19:18:19:38 | getHostName(...) : String | Test.java:27:21:27:24 | temp |
| Test.java:19:18:19:38 | getHostName(...) : String | Test.java:30:44:30:47 | temp |
@@ -189,12 +194,18 @@ edges
| mad/Test.java:221:26:221:33 | source(...) : String | mad/Test.java:221:19:221:33 | (...)... |
| mad/Test.java:226:29:226:36 | source(...) : String | mad/Test.java:226:20:226:36 | (...)... |
nodes
| TaintedPath.java:11:38:11:110 | new BufferedReader(...) : BufferedReader | semmle.label | new BufferedReader(...) : BufferedReader |
| TaintedPath.java:11:57:11:109 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
| TaintedPath.java:11:79:11:99 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| TaintedPath.java:12:24:12:37 | filenameReader : BufferedReader | semmle.label | filenameReader : BufferedReader |
| TaintedPath.java:12:24:12:48 | readLine(...) : String | semmle.label | readLine(...) : String |
| TaintedPath.java:14:68:14:75 | filename | semmle.label | filename |
| TaintedPath.java:12:38:12:110 | new BufferedReader(...) : BufferedReader | semmle.label | new BufferedReader(...) : BufferedReader |
| TaintedPath.java:12:57:12:109 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
| TaintedPath.java:12:79:12:99 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| TaintedPath.java:13:24:13:37 | filenameReader : BufferedReader | semmle.label | filenameReader : BufferedReader |
| TaintedPath.java:13:24:13:48 | readLine(...) : String | semmle.label | readLine(...) : String |
| TaintedPath.java:15:68:15:75 | filename | semmle.label | filename |
| TaintedPath.java:38:41:39:70 | new BufferedReader(...) : BufferedReader | semmle.label | new BufferedReader(...) : BufferedReader |
| TaintedPath.java:39:17:39:69 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
| TaintedPath.java:39:39:39:59 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| TaintedPath.java:40:27:40:40 | filenameReader : BufferedReader | semmle.label | filenameReader : BufferedReader |
| TaintedPath.java:40:27:40:51 | readLine(...) : String | semmle.label | readLine(...) : String |
| TaintedPath.java:43:46:43:53 | filename | semmle.label | filename |
| Test.java:19:18:19:38 | getHostName(...) : String | semmle.label | getHostName(...) : String |
| Test.java:24:20:24:23 | temp | semmle.label | temp |
| Test.java:27:21:27:24 | temp | semmle.label | temp |
@@ -386,7 +397,8 @@ nodes
| mad/Test.java:226:29:226:36 | source(...) : String | semmle.label | source(...) : String |
subpaths
#select
| TaintedPath.java:14:53:14:76 | new FileReader(...) | TaintedPath.java:11:79:11:99 | getInputStream(...) : InputStream | TaintedPath.java:14:68:14:75 | filename | This path depends on a $@. | TaintedPath.java:11:79:11:99 | getInputStream(...) | user-provided value |
| TaintedPath.java:15:53:15:76 | new FileReader(...) | TaintedPath.java:12:79:12:99 | getInputStream(...) : InputStream | TaintedPath.java:15:68:15:75 | filename | This path depends on a $@. | TaintedPath.java:12:79:12:99 | getInputStream(...) | user-provided value |
| TaintedPath.java:43:25:43:54 | resolve(...) | TaintedPath.java:39:39:39:59 | getInputStream(...) : InputStream | TaintedPath.java:43:46:43:53 | filename | This path depends on a $@. | TaintedPath.java:39:39:39:59 | getInputStream(...) | user-provided value |
| Test.java:24:11:24:24 | new File(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:24:20:24:23 | temp | This path depends on a $@. | Test.java:19:18:19:38 | getHostName(...) | user-provided value |
| Test.java:27:11:27:25 | get(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:27:21:27:24 | temp | This path depends on a $@. | Test.java:19:18:19:38 | getHostName(...) | user-provided value |
| Test.java:30:11:30:48 | getPath(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:30:44:30:47 | temp | This path depends on a $@. | Test.java:19:18:19:38 | getHostName(...) | user-provided value |

View File

@@ -1,10 +1,11 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;
public class TaintedPath {
public void sendUserFile(Socket sock, String user) throws IOException {
@@ -32,4 +33,40 @@ public class TaintedPath {
}
}
}
public void sendUserFileGood2(Socket sock, String user) throws Exception {
BufferedReader filenameReader = new BufferedReader(
new InputStreamReader(sock.getInputStream(), "UTF-8"));
String filename = filenameReader.readLine();
Path publicFolder = Paths.get("/home/" + user + "/public").normalize().toAbsolutePath();
Path filePath = publicFolder.resolve(filename).normalize().toAbsolutePath(); // FP until the path-injection sinks are reworked
// GOOD: ensure that the path stays within the public folder
if (!filePath.startsWith(publicFolder + File.separator)) {
throw new IllegalArgumentException("Invalid filename");
}
BufferedReader fileReader = new BufferedReader(new FileReader(filePath.toString()));
String fileLine = fileReader.readLine();
while(fileLine != null) {
sock.getOutputStream().write(fileLine.getBytes());
fileLine = fileReader.readLine();
}
}
public void sendUserFileGood3(Socket sock, String user) throws Exception {
BufferedReader filenameReader = new BufferedReader(
new InputStreamReader(sock.getInputStream(), "UTF-8"));
String filename = filenameReader.readLine();
// GOOD: ensure that the filename has no path separators or parent directory references
if (filename.contains("..") || filename.contains("/") || filename.contains("\\")) {
throw new IllegalArgumentException("Invalid filename");
}
BufferedReader fileReader = new BufferedReader(new FileReader(filename));
String fileLine = fileReader.readLine();
while(fileLine != null) {
sock.getOutputStream().write(fileLine.getBytes());
fileLine = fileReader.readLine();
}
}
}

View File

@@ -0,0 +1,129 @@
import android.app.Activity;
import android.app.Notification;
import androidx.core.app.NotificationCompat;
import android.content.Intent;
import android.app.PendingIntent;
import android.widget.RemoteViews;
class Test extends Activity {
void test(String password) {
Notification.Builder builder = new Notification.Builder(this, "");
builder.setContentText(password); // $sensitive-notification
builder.setContentTitle(password); // $sensitive-notification
builder.setContentInfo(password); // $sensitive-notification
Intent intent = new Intent();
intent.putExtra("a", password);
builder.addExtras(intent.getExtras()); // $sensitive-notification
builder.setCategory(password); // $sensitive-notification
builder.setChannelId(password); // $sensitive-notification
builder.setGroup(password); // $sensitive-notification
builder.setExtras(intent.getExtras()); // $sensitive-notification
builder.setGroup(password); // $sensitive-notification
builder.setSortKey(password); // $sensitive-notification
builder.setSettingsText(password); // $sensitive-notification
builder.setRemoteInputHistory(new CharSequence[] { password }); // $sensitive-notification
builder.setTicker(password); // $sensitive-notification
builder.setTicker(password, null); // $sensitive-notification
builder.setStyle(new Notification.BigPictureStyle()
.setContentDescription(password) // $sensitive-notification
.setSummaryText(password) // $sensitive-notification
.setBigContentTitle(password)); // $sensitive-notification
builder.setStyle(new Notification.BigTextStyle()
.bigText(password) // $sensitive-notification
.setSummaryText(password) // $sensitive-notification
.setBigContentTitle(password)); // $sensitive-notification
builder.setStyle(new Notification.InboxStyle()
.addLine(password) // $sensitive-notification
.setBigContentTitle(password) // $sensitive-notification
.setSummaryText(password)); // $sensitive-notification
builder.setStyle(new Notification.MediaStyle()
.setRemotePlaybackInfo(password, 0, null)); // $sensitive-notification
builder.setStyle(
new Notification.MessagingStyle(password) // $sensitive-notification
.setConversationTitle(password) // $sensitive-notification
.addMessage(password, 0, "") // $sensitive-notification
.addMessage(password, 0, (android.app.Person)null) // $sensitive-notification
.addMessage("", 0, password) // $sensitive-notification
.addMessage(new Notification.MessagingStyle.Message(password, 0, "")) // $sensitive-notification
.addMessage(new Notification.MessagingStyle.Message(password, 0, (android.app.Person)null)) // $sensitive-notification
.addMessage(new Notification.MessagingStyle.Message("", 0, password)) // $sensitive-notification
);
builder.addAction(0, password, null); // $sensitive-notification
builder.addAction(new Notification.Action(0, password, null)); // $sensitive-notification
builder.addAction(new Notification.Action.Builder(0, password, null) // $sensitive-notification
.addExtras(intent.getExtras()) // $sensitive-notification
.build());
builder.addAction(new Notification.Action.Builder(null, password, null).build()); // $sensitive-notification
builder.setStyle(Notification.CallStyle.forScreeningCall(null, null, null)
.setVerificationText(password)); // $sensitive-notification
}
void test2(RemoteViews passwordView) {
Notification.Builder builder = new Notification.Builder(this, "");
builder.setContent(passwordView); // $sensitive-notification
builder.setCustomBigContentView(passwordView); // $sensitive-notification
builder.setCustomContentView(passwordView); // $sensitive-notification
builder.setCustomHeadsUpContentView(passwordView); // $sensitive-notification
builder.setTicker("", passwordView); // $sensitive-notification
}
void test3(String password) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "");
builder.setContentText(password); // $sensitive-notification
builder.setContentTitle(password); // $sensitive-notification
builder.setContentInfo(password); // $sensitive-notification
Intent intent = new Intent();
intent.putExtra("a", password);
builder.addExtras(intent.getExtras()); // $sensitive-notification
builder.setCategory(password); // $sensitive-notification
builder.setChannelId(password); // $sensitive-notification
builder.setGroup(password); // $sensitive-notification
builder.setExtras(intent.getExtras()); // $sensitive-notification
builder.setGroup(password); // $sensitive-notification
builder.setSortKey(password); // $sensitive-notification
builder.setSettingsText(password); // $sensitive-notification
builder.setRemoteInputHistory(new CharSequence[] { password }); // $sensitive-notification
builder.setTicker(password); // $sensitive-notification
builder.setTicker(password, null); // $sensitive-notification
builder.setStyle(new NotificationCompat.BigPictureStyle()
.setContentDescription(password) // $sensitive-notification
.setSummaryText(password) // $sensitive-notification
.setBigContentTitle(password)); // $sensitive-notification
builder.setStyle(new NotificationCompat.BigTextStyle()
.bigText(password) // $sensitive-notification
.setSummaryText(password) // $sensitive-notification
.setBigContentTitle(password)); // $sensitive-notification
builder.setStyle(new NotificationCompat.InboxStyle()
.addLine(password) // $sensitive-notification
.setBigContentTitle(password) // $sensitive-notification
.setSummaryText(password)); // $sensitive-notification
builder.setStyle(
new NotificationCompat.MessagingStyle(password) // $sensitive-notification
.setConversationTitle(password) // $sensitive-notification
.addMessage(password, 0, "") // $sensitive-notification
.addMessage(password, 0, (androidx.core.app.Person)null) // $sensitive-notification
.addMessage("", 0, password) // $sensitive-notification
.addMessage(new NotificationCompat.MessagingStyle.Message(password, 0, "")) // $sensitive-notification
.addMessage(new NotificationCompat.MessagingStyle.Message(password, 0, (androidx.core.app.Person)null)) // $sensitive-notification
.addMessage(new NotificationCompat.MessagingStyle.Message("", 0, password)) // $sensitive-notification
);
builder.addAction(0, password, null); // $sensitive-notification
builder.addAction(new NotificationCompat.Action(0, password, null)); // $sensitive-notification
builder.addAction(new NotificationCompat.Action.Builder(0, password, null) // $sensitive-notification
.addExtras(intent.getExtras()) // $sensitive-notification
.build());
builder.addAction(new NotificationCompat.Action.Builder(null, password, null).build()); // $sensitive-notification
builder.setStyle(NotificationCompat.CallStyle.forScreeningCall(null, null, null)
.setVerificationText(password)); // $sensitive-notification
}
}

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../../stubs/google-android-9.0.0

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -0,0 +1,19 @@
import java
import TestUtilities.InlineExpectationsTest
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.security.SensitiveUiQuery
module SensitiveNotifTest implements TestSig {
string getARelevantTag() { result = "sensitive-notification" }
predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "sensitive-notification" and
exists(DataFlow::Node sink | NotificationTracking::flowTo(sink) |
sink.getLocation() = location and
element = sink.toString() and
value = ""
)
}
}
import MakeTest<SensitiveNotifTest>

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../../stubs/apache-commons-lang3-3.7/

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../../stubs/google-android-9.0.0

View File

@@ -1 +0,0 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/apache-commons-lang3-3.7/:${testdir}/../../../../../stubs/google-android-9.0.0

View File

@@ -358,6 +358,17 @@ public class Notification implements Parcelable
public RemoteViews createHeadsUpContentView(){ return null; }
public static Notification.Builder recoverBuilder(Context p0, Notification p1){ return null; }
}
static public class CallStyle extends Notification.Style
{
public Notification.CallStyle setAnswerButtonColorHint(int p0){ return null; }
public Notification.CallStyle setDeclineButtonColorHint(int p0){ return null; }
public Notification.CallStyle setIsVideo(boolean p0){ return null; }
public Notification.CallStyle setVerificationIcon(Icon p0){ return null; }
public Notification.CallStyle setVerificationText(CharSequence p0){ return null; }
public static Notification.CallStyle forIncomingCall(Person p0, PendingIntent p1, PendingIntent p2){ return null; }
public static Notification.CallStyle forOngoingCall(Person p0, PendingIntent p1){ return null; }
public static Notification.CallStyle forScreeningCall(Person p0, PendingIntent p1, PendingIntent p2){ return null; }
}
static public class InboxStyle extends Notification.Style
{
public InboxStyle(){}
@@ -371,8 +382,43 @@ public class Notification implements Parcelable
public MediaStyle(){}
public MediaStyle(Notification.Builder p0){}
public Notification.MediaStyle setMediaSession(MediaSession.Token p0){ return null; }
public Notification.MediaStyle setRemotePlaybackInfo(CharSequence p0, int p1, PendingIntent p2){ return null; } // added manually
public Notification.MediaStyle setShowActionsInCompactView(int... p0){ return null; }
}
static public class MessagingStyle extends Notification.Style
{
protected MessagingStyle() {}
public CharSequence getConversationTitle(){ return null; }
public CharSequence getUserDisplayName(){ return null; }
public List<Notification.MessagingStyle.Message> getHistoricMessages(){ return null; }
public List<Notification.MessagingStyle.Message> getMessages(){ return null; }
public MessagingStyle(CharSequence p0){}
public MessagingStyle(Person p0){}
public Notification.MessagingStyle addHistoricMessage(Notification.MessagingStyle.Message p0){ return null; }
public Notification.MessagingStyle addMessage(CharSequence p0, long p1, CharSequence p2){ return null; }
public Notification.MessagingStyle addMessage(CharSequence p0, long p1, Person p2){ return null; }
public Notification.MessagingStyle addMessage(Notification.MessagingStyle.Message p0){ return null; }
public Notification.MessagingStyle setConversationTitle(CharSequence p0){ return null; }
public Notification.MessagingStyle setGroupConversation(boolean p0){ return null; }
public Person getUser(){ return null; }
public boolean isGroupConversation(){ return false; }
public static int MAXIMUM_RETAINED_MESSAGES = 0;
static public class Message
{
protected Message() {}
public Bundle getExtras(){ return null; }
public CharSequence getSender(){ return null; }
public CharSequence getText(){ return null; }
public Message(CharSequence p0, long p1, CharSequence p2){}
public Message(CharSequence p0, long p1, Person p2){}
public Notification.MessagingStyle.Message setData(String p0, Uri p1){ return null; }
public Person getSenderPerson(){ return null; }
public String getDataMimeType(){ return null; }
public Uri getDataUri(){ return null; }
public long getTimestamp(){ return 0; }
public static List<Notification.MessagingStyle.Message> getMessagesFromBundleArray(Parcelable[] p0){ return null; }
}
}
static public interface Extender
{
Notification.Builder extend(Notification.Builder p0);

View File

@@ -0,0 +1,51 @@
// Generated automatically from android.content.pm.ShortcutInfo for testing purposes
package android.content.pm;
import android.content.ComponentName;
import android.content.Intent;
import android.content.LocusId;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.UserHandle;
import java.util.Set;
public class ShortcutInfo implements Parcelable
{
public CharSequence getDisabledMessage(){ return null; }
public CharSequence getLongLabel(){ return null; }
public CharSequence getShortLabel(){ return null; }
public ComponentName getActivity(){ return null; }
public Intent getIntent(){ return null; }
public Intent[] getIntents(){ return null; }
public LocusId getLocusId(){ return null; }
public PersistableBundle getExtras(){ return null; }
public Set<String> getCategories(){ return null; }
public String getId(){ return null; }
public String getPackage(){ return null; }
public String toString(){ return null; }
public UserHandle getUserHandle(){ return null; }
public boolean hasKeyFieldsOnly(){ return false; }
public boolean isCached(){ return false; }
public boolean isDeclaredInManifest(){ return false; }
public boolean isDynamic(){ return false; }
public boolean isEnabled(){ return false; }
public boolean isImmutable(){ return false; }
public boolean isPinned(){ return false; }
public int describeContents(){ return 0; }
public int getDisabledReason(){ return 0; }
public int getRank(){ return 0; }
public long getLastChangedTimestamp(){ return 0; }
public static Parcelable.Creator<ShortcutInfo> CREATOR = null;
public static String SHORTCUT_CATEGORY_CONVERSATION = null;
public static int DISABLED_REASON_APP_CHANGED = 0;
public static int DISABLED_REASON_BACKUP_NOT_SUPPORTED = 0;
public static int DISABLED_REASON_BY_APP = 0;
public static int DISABLED_REASON_NOT_DISABLED = 0;
public static int DISABLED_REASON_OTHER_RESTORE_ISSUE = 0;
public static int DISABLED_REASON_SIGNATURE_MISMATCH = 0;
public static int DISABLED_REASON_UNKNOWN = 0;
public static int DISABLED_REASON_VERSION_LOWER = 0;
public void writeToParcel(Parcel p0, int p1){}
}

View File

@@ -0,0 +1,37 @@
// Generated automatically from androidx.core.app.Person for testing purposes
package androidx.core.app;
import android.os.Bundle;
import android.os.PersistableBundle;
import androidx.core.graphics.drawable.IconCompat;
public class Person
{
protected Person() {}
public Bundle toBundle(){ return null; }
public CharSequence getName(){ return null; }
public IconCompat getIcon(){ return null; }
public PersistableBundle toPersistableBundle(){ return null; }
public String getKey(){ return null; }
public String getUri(){ return null; }
public String resolveToLegacyUri(){ return null; }
public android.app.Person toAndroidPerson(){ return null; }
public androidx.core.app.Person.Builder toBuilder(){ return null; }
public boolean isBot(){ return false; }
public boolean isImportant(){ return false; }
public static androidx.core.app.Person fromAndroidPerson(android.app.Person p0){ return null; }
public static androidx.core.app.Person fromBundle(Bundle p0){ return null; }
public static androidx.core.app.Person fromPersistableBundle(PersistableBundle p0){ return null; }
static public class Builder
{
public Builder(){}
public androidx.core.app.Person build(){ return null; }
public androidx.core.app.Person.Builder setBot(boolean p0){ return null; }
public androidx.core.app.Person.Builder setIcon(IconCompat p0){ return null; }
public androidx.core.app.Person.Builder setImportant(boolean p0){ return null; }
public androidx.core.app.Person.Builder setKey(String p0){ return null; }
public androidx.core.app.Person.Builder setName(CharSequence p0){ return null; }
public androidx.core.app.Person.Builder setUri(String p0){ return null; }
}
}

View File

@@ -0,0 +1,17 @@
// Generated automatically from androidx.core.content.LocusIdCompat for testing purposes
package androidx.core.content;
import android.content.LocusId;
public class LocusIdCompat
{
protected LocusIdCompat() {}
public LocusId toLocusId(){ return null; }
public LocusIdCompat(String p0){}
public String getId(){ return null; }
public String toString(){ return null; }
public boolean equals(Object p0){ return false; }
public int hashCode(){ return 0; }
public static LocusIdCompat toLocusIdCompat(LocusId p0){ return null; }
}

View File

@@ -0,0 +1,45 @@
// Generated automatically from androidx.core.content.pm.ShortcutInfoCompat for testing purposes
package androidx.core.content.pm;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.UserHandle;
import androidx.core.content.LocusIdCompat;
import androidx.core.graphics.drawable.IconCompat;
import java.util.Set;
public class ShortcutInfoCompat
{
public Bundle getTransientExtras(){ return null; }
public CharSequence getDisabledMessage(){ return null; }
public CharSequence getLongLabel(){ return null; }
public CharSequence getShortLabel(){ return null; }
public ComponentName getActivity(){ return null; }
public IconCompat getIcon(){ return null; }
public Intent getIntent(){ return null; }
public Intent[] getIntents(){ return null; }
public LocusIdCompat getLocusId(){ return null; }
public PersistableBundle getExtras(){ return null; }
public Set<String> getCategories(){ return null; }
public ShortcutInfo toShortcutInfo(){ return null; }
public String getId(){ return null; }
public String getPackage(){ return null; }
public UserHandle getUserHandle(){ return null; }
public boolean hasKeyFieldsOnly(){ return false; }
public boolean isCached(){ return false; }
public boolean isDeclaredInManifest(){ return false; }
public boolean isDynamic(){ return false; }
public boolean isEnabled(){ return false; }
public boolean isExcludedFromSurfaces(int p0){ return false; }
public boolean isImmutable(){ return false; }
public boolean isPinned(){ return false; }
public int getDisabledReason(){ return 0; }
public int getExcludedFromSurfaces(){ return 0; }
public int getRank(){ return 0; }
public long getLastChangedTimestamp(){ return 0; }
public static int SURFACE_LAUNCHER = 0;
}