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", "" + | "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.(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.(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.(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.(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.(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.(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.(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.(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.(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)]