From 20aff78b3620dd9182ed18c7be4225ca3af98f50 Mon Sep 17 00:00:00 2001
From: Sam Blackshear <shb@fb.com>
Date: Fri, 21 Apr 2017 13:37:09 -0700
Subject: [PATCH] [quandary] ContentProvider Uri's as sources/files as sinks

Reviewed By: mburman

Differential Revision: D4917047

fbshipit-source-id: 994891b
---
 infer/src/quandary/JavaTrace.ml               | 40 ++++++++--
 .../java/quandary/ContentProviders.java       | 77 +++++++++++++++++++
 .../codetoanalyze/java/quandary/Files.java    | 47 +++++++++++
 .../codetoanalyze/java/quandary/issues.exp    | 13 ++++
 4 files changed, 169 insertions(+), 8 deletions(-)
 create mode 100644 infer/tests/codetoanalyze/java/quandary/ContentProviders.java
 create mode 100644 infer/tests/codetoanalyze/java/quandary/Files.java

diff --git a/infer/src/quandary/JavaTrace.ml b/infer/src/quandary/JavaTrace.ml
index 96b360918..21e8c9ac4 100644
--- a/infer/src/quandary/JavaTrace.ml
+++ b/infer/src/quandary/JavaTrace.ml
@@ -17,7 +17,7 @@ module SourceKind = struct
     | Intent (** external Intent or a value read from one *)
     | Other (** for testing or uncategorized sources *)
     | PrivateData (** private user or device-specific data *)
-    | WebviewUrl (** external URL passed to a WebView *)
+    | UserControlledURI (** resource locator controller by user *)
     | Unknown
   [@@deriving compare]
 
@@ -26,7 +26,7 @@ module SourceKind = struct
   let of_string = function
     | "Intent" -> Intent
     | "PrivateData" -> PrivateData
-    | "WebviewUrl" -> WebviewUrl
+    | "UserControlledURI" -> UserControlledURI
     | _ -> Other
 
   let external_sources = QuandaryConfig.Source.of_json Config.quandary_sources
@@ -120,16 +120,28 @@ module SourceKind = struct
                     Some (taint_formals_with_types ["android.content.Intent"] Intent formals)
                 | "android.content.BroadcastReceiver", "onReceive" ->
                     Some (taint_formals_with_types ["android.content.Intent"] Intent formals)
+                | "android.content.ContentProvider",
+                  ("bulkInsert" |
+                   "delete" |
+                   "insert" |
+                   "openAssetFile" |
+                   "openFile" |
+                   "openPipeHelper" |
+                   "openTypedAssetFile" |
+                   "query" |
+                   "refresh" |
+                   "update") ->
+                    Some (taint_formals_with_types ["android.net.Uri"] UserControlledURI formals)
                 | "android.webkit.WebViewClient",
                   ("onLoadResource" | "shouldInterceptRequest" | "shouldOverrideUrlLoading") ->
                     Some
                       (taint_formals_with_types
                          ["android.webkit.WebResourceRequest"; "java.lang.String"]
-                         WebviewUrl
+                         UserControlledURI
                          formals)
                 | "android.webkit.WebChromeClient",
                   ("onJsAlert" | "onJsBeforeUnload" | "onJsConfirm" | "onJsPrompt") ->
-                    Some (taint_formals_with_types ["java.lang.String"] WebviewUrl formals)
+                    Some (taint_formals_with_types ["java.lang.String"] UserControlledURI formals)
                 | _ ->
                     None in
               begin
@@ -151,7 +163,7 @@ module SourceKind = struct
     F.fprintf fmt
       (match kind with
        | Intent -> "Intent"
-       | WebviewUrl -> "WebviewUrl"
+       | UserControlledURI -> "UserControlledURI"
        | PrivateData -> "PrivateData"
        | Other -> "Other"
        | Unknown -> "Unknown")
@@ -161,6 +173,7 @@ module JavaSource = Source.Make(SourceKind)
 
 module SinkKind = struct
   type t =
+    | CreateFile (** sink that creates a file *)
     | CreateIntent (** sink that creates an Intent *)
     | JavaScript (** sink that passes its arguments to untrusted JS code *)
     | Logging (** sink that logs one or more of its arguments *)
@@ -169,6 +182,8 @@ module SinkKind = struct
   [@@deriving compare]
 
   let of_string = function
+    | "CreateFile" -> CreateFile
+    | "CreateIntent" -> CreateIntent
     | "JavaScript" -> JavaScript
     | "Logging" -> Logging
     | "StartComponent" -> StartComponent
@@ -194,9 +209,14 @@ module SinkKind = struct
     match pname with
     | Typ.Procname.Java java_pname ->
         begin
-          match Typ.Procname.java_get_class_name java_pname, Typ.Procname.java_get_method java_pname with
+          match Typ.Procname.java_get_class_name java_pname,
+                Typ.Procname.java_get_method java_pname with
           | "android.util.Log", ("e" | "println" | "w" | "wtf") ->
               taint_all Logging ~report_reachable:true
+          | "java.io.File", "<init>"
+          | "java.nio.file.FileSystem", "getPath"
+          | "java.nio.file.Paths", "get" ->
+              taint_all CreateFile ~report_reachable:true
           | "com.facebook.infer.builtins.InferTaint", "inferSensitiveSink" ->
               [Other, 0, false]
           | class_name, method_name ->
@@ -284,6 +304,7 @@ module SinkKind = struct
   let pp fmt kind =
     F.fprintf fmt
       (match kind with
+       | CreateFile -> "CreateFile"
        | CreateIntent -> "CreateIntent"
        | JavaScript -> "JavaScript"
        | Logging -> "Logging"
@@ -304,9 +325,12 @@ include
       | Intent, StartComponent (* intent reuse issue *)
       | Intent, CreateIntent (* intent configured with external values issue *)
       | Intent, JavaScript (* external data flows into JS: remote code execution risk *)
-      | WebviewUrl, (CreateIntent | StartComponent)
-      (* create intent/launch component from external URL *)
       | PrivateData, JavaScript (* leaking private data into JS *)
+      | UserControlledURI, (CreateIntent | StartComponent)
+      (* create intent/launch component from user-controlled URI *)
+      | UserControlledURI, CreateFile ->
+          (* create file from user-controller URI; potential path-traversal vulnerability *)
+          true
       | Other, _ | _, Other -> (* for testing purposes, Other matches everything *)
           true
       | _ ->
diff --git a/infer/tests/codetoanalyze/java/quandary/ContentProviders.java b/infer/tests/codetoanalyze/java/quandary/ContentProviders.java
new file mode 100644
index 000000000..eba212cda
--- /dev/null
+++ b/infer/tests/codetoanalyze/java/quandary/ContentProviders.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017 - present Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package codetoanalyze.java.quandary;
+
+import java.io.File;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+
+public abstract class ContentProviders extends ContentProvider {
+
+  File mFile;
+
+  @Override
+  public int bulkInsert(Uri uri, ContentValues[] values) {
+    mFile = new File(uri.toString());
+    return 0;
+  }
+
+  @Override
+  public int delete(Uri uri, String selection, String[] selectionArgs) {
+    mFile = new File(uri.toString());
+    return 0;
+  }
+
+  @Override
+  public Uri insert(Uri uri, ContentValues values) {
+    mFile = new File(uri.toString());
+    return null;
+  }
+
+  @Override
+  public AssetFileDescriptor openAssetFile(Uri uri, String mode, CancellationSignal signal) {
+    mFile = new File(uri.toString());
+    return null;
+  }
+
+  @Override
+  public ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal) {
+     mFile = new File(uri.toString());
+     return null;
+  }
+
+   @Override
+   public AssetFileDescriptor openTypedAssetFile(
+       Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal) {
+     mFile = new File(uri.toString());
+     return null;
+  }
+
+  @Override
+  public Cursor query(
+      Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+    mFile = new File(uri.toString());
+    return null;
+  }
+
+  @Override
+  public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+    mFile = new File(uri.toString());
+    return 0;
+  }
+
+}
diff --git a/infer/tests/codetoanalyze/java/quandary/Files.java b/infer/tests/codetoanalyze/java/quandary/Files.java
new file mode 100644
index 000000000..6754897b2
--- /dev/null
+++ b/infer/tests/codetoanalyze/java/quandary/Files.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017 - present Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package codetoanalyze.java.quandary;
+
+import com.facebook.infer.builtins.InferTaint;
+import java.io.File;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class Files {
+
+  public File fileConstructorSinkBad() {
+    String taintedString = (String) InferTaint.inferSecretSource();
+    return new File(taintedString);
+  }
+
+  public Path fileSystemConstructorSinkBad1() {
+    String taintedString = (String) InferTaint.inferSecretSource();
+    return FileSystems.getDefault().getPath(taintedString);
+  }
+
+  // testing varags
+  public Path fileSystemConstructorSinkBad2() {
+    String taintedString = (String) InferTaint.inferSecretSource();
+    return FileSystems.getDefault().getPath("", taintedString);
+  }
+
+  public Path pathsSinkBad1() {
+    String taintedString = (String) InferTaint.inferSecretSource();
+    return Paths.get(taintedString);
+  }
+
+  // testing varags
+  public Path pathsSinkBad2() {
+    String taintedString = (String) InferTaint.inferSecretSource();
+    return Paths.get("", taintedString);
+  }
+
+}
diff --git a/infer/tests/codetoanalyze/java/quandary/issues.exp b/infer/tests/codetoanalyze/java/quandary/issues.exp
index 4ea6ff1b6..c5b38f97c 100644
--- a/infer/tests/codetoanalyze/java/quandary/issues.exp
+++ b/infer/tests/codetoanalyze/java/quandary/issues.exp
@@ -25,6 +25,14 @@ codetoanalyze/java/quandary/Basics.java, void Basics.viaVarBad2(), 3, QUANDARY_T
 codetoanalyze/java/quandary/Basics.java, void Basics.viaVarBad3(), 4, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void InferTaint.inferSensitiveSink(Object)]
 codetoanalyze/java/quandary/Basics.java, void Basics.whileBad1(int), 3, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void InferTaint.inferSensitiveSink(Object)]
 codetoanalyze/java/quandary/Basics.java, void Basics.whileBad2(int), 6, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void InferTaint.inferSensitiveSink(Object)]
+codetoanalyze/java/quandary/ContentProviders.java, AssetFileDescriptor ContentProviders.openAssetFile(Uri,String,CancellationSignal), 1, QUANDARY_TAINT_ERROR, [return from AssetFileDescriptor ContentProviders.openAssetFile(Uri,String,CancellationSignal),call to File.<init>(String)]
+codetoanalyze/java/quandary/ContentProviders.java, AssetFileDescriptor ContentProviders.openTypedAssetFile(Uri,String,Bundle,CancellationSignal), 2, QUANDARY_TAINT_ERROR, [return from AssetFileDescriptor ContentProviders.openTypedAssetFile(Uri,String,Bundle,CancellationSignal),call to File.<init>(String)]
+codetoanalyze/java/quandary/ContentProviders.java, Cursor ContentProviders.query(Uri,java.lang.String[],String,java.lang.String[],String), 2, QUANDARY_TAINT_ERROR, [return from Cursor ContentProviders.query(Uri,java.lang.String[],String,java.lang.String[],String),call to File.<init>(String)]
+codetoanalyze/java/quandary/ContentProviders.java, ParcelFileDescriptor ContentProviders.openFile(Uri,String,CancellationSignal), 1, QUANDARY_TAINT_ERROR, [return from ParcelFileDescriptor ContentProviders.openFile(Uri,String,CancellationSignal),call to File.<init>(String)]
+codetoanalyze/java/quandary/ContentProviders.java, Uri ContentProviders.insert(Uri,ContentValues), 1, QUANDARY_TAINT_ERROR, [return from Uri ContentProviders.insert(Uri,ContentValues),call to File.<init>(String)]
+codetoanalyze/java/quandary/ContentProviders.java, int ContentProviders.bulkInsert(Uri,android.content.ContentValues[]), 1, QUANDARY_TAINT_ERROR, [return from int ContentProviders.bulkInsert(Uri,android.content.ContentValues[]),call to File.<init>(String)]
+codetoanalyze/java/quandary/ContentProviders.java, int ContentProviders.delete(Uri,String,java.lang.String[]), 1, QUANDARY_TAINT_ERROR, [return from int ContentProviders.delete(Uri,String,java.lang.String[]),call to File.<init>(String)]
+codetoanalyze/java/quandary/ContentProviders.java, int ContentProviders.update(Uri,ContentValues,String,java.lang.String[]), 1, QUANDARY_TAINT_ERROR, [return from int ContentProviders.update(Uri,ContentValues,String,java.lang.String[]),call to File.<init>(String)]
 codetoanalyze/java/quandary/DynamicDispatch.java, void DynamicDispatch.FP_propagateViaConcreteTypeOk(), 4, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),return from Object DynamicDispatch$BadSubtype.returnSource(),call to void InferTaint.inferSensitiveSink(Object)]
 codetoanalyze/java/quandary/DynamicDispatch.java, void DynamicDispatch.FP_propagateViaConcreteTypeOk(), 7, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void DynamicDispatch$BadSubtype.callSink(Object),call to void InferTaint.inferSensitiveSink(Object)]
 codetoanalyze/java/quandary/DynamicDispatch.java, void DynamicDispatch.FP_propagateViaConcreteTypeOk(), 10, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),flow through Object DynamicDispatch$BadSubtype.propagate(Object),call to void InferTaint.inferSensitiveSink(Object)]
@@ -54,6 +62,11 @@ codetoanalyze/java/quandary/Fields.java, void Fields.viaFieldBad2(), 3, QUANDARY
 codetoanalyze/java/quandary/Fields.java, void Fields.viaFieldBad3(), 4, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void InferTaint.inferSensitiveSink(Object)]
 codetoanalyze/java/quandary/Fields.java, void Fields.viaNestedFieldBad1(Fields$Obj), 2, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void InferTaint.inferSensitiveSink(Object)]
 codetoanalyze/java/quandary/Fields.java, void Fields.viaNestedFieldBad2(), 4, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void InferTaint.inferSensitiveSink(Object)]
+codetoanalyze/java/quandary/Files.java, File Files.fileConstructorSinkBad(), 2, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to File.<init>(String)]
+codetoanalyze/java/quandary/Files.java, Path Files.fileSystemConstructorSinkBad1(), 2, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to Path FileSystem.getPath(String,java.lang.String[])]
+codetoanalyze/java/quandary/Files.java, Path Files.fileSystemConstructorSinkBad2(), 2, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to Path FileSystem.getPath(String,java.lang.String[])]
+codetoanalyze/java/quandary/Files.java, Path Files.pathsSinkBad1(), 2, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to Path Paths.get(String,java.lang.String[])]
+codetoanalyze/java/quandary/Files.java, Path Files.pathsSinkBad2(), 2, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to Path Paths.get(String,java.lang.String[])]
 codetoanalyze/java/quandary/FlowSensitivity.java, void FlowSensitivity.callSourceAndSinkBad1(FlowSensitivity$Obj), 2, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),return from void FlowSensitivity.sourceAndSink(FlowSensitivity$Obj),call to void InferTaint.inferSensitiveSink(Object)]
 codetoanalyze/java/quandary/FlowSensitivity.java, void FlowSensitivity.callSourceAndSinkBad2(FlowSensitivity$Obj), 2, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void FlowSensitivity.sourceAndSink(FlowSensitivity$Obj),call to void InferTaint.inferSensitiveSink(Object)]
 codetoanalyze/java/quandary/FlowSensitivity.java, void FlowSensitivity.interproceduralFlowSensitivityBad(FlowSensitivity$Obj), 2, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),return from void FlowSensitivity.returnSource(FlowSensitivity$Obj),call to void FlowSensitivity.callSink(FlowSensitivity$Obj),call to void InferTaint.inferSensitiveSink(Object)]