diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 995cd1d24..b8e2788d0 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -1009,6 +1009,9 @@ and progress_bar = and quandary_sources = CLOpt.mk_json ~long:"quandary-sources" "Specify custom sources for Quandary" +and quandary_sinks = + CLOpt.mk_json ~long:"quandary-sinks" "Specify custom sinks for Quandary" + and quiet = CLOpt.mk_bool ~long:"quiet" ~short:"q" ~default:(current_exe <> CLOpt.Print) ~exes:CLOpt.[Print] @@ -1479,6 +1482,7 @@ and procs_csv = !procs_csv and procs_xml = !procs_xml and quandary = !quandary and quandary_sources = !quandary_sources +and quandary_sinks = !quandary_sinks and quiet = !quiet and reactive_mode = !reactive and reactive_capture = !reactive_capture diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index 2c89b2583..eceb79f59 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -243,6 +243,7 @@ val procs_xml : string option val project_root : string val quandary : bool val quandary_sources : Yojson.Basic.json +val quandary_sinks : Yojson.Basic.json val quiet : bool val reactive_mode : bool val reactive_capture : bool diff --git a/infer/src/quandary/JavaTrace.ml b/infer/src/quandary/JavaTrace.ml index 40feddcd4..22a89ed36 100644 --- a/infer/src/quandary/JavaTrace.ml +++ b/infer/src/quandary/JavaTrace.ml @@ -139,6 +139,14 @@ module SinkKind = struct | Other (** for testing or uncategorized sinks *) [@@deriving compare] + let of_string = function + | "Intent" -> Intent + | "JavaScript" -> JavaScript + | "Logging" -> Logging + | _ -> Other + + let external_sinks = QuandaryConfig.Sink.of_json Config.quandary_sinks + let get pname actuals tenv = (* taint all the inputs of [pname]. for non-static procedures, taints the "this" parameter only if [taint_this] is true. *) @@ -223,8 +231,23 @@ module SinkKind = struct | "android.webkit.WebViewClient", ("onLoadResource" | "shouldInterceptRequest" | "shouldOverrideUrlLoading") -> Some (taint_all JavaScript ~report_reachable:true) - | _ -> - None in + | class_name, method_name -> + (* check the list of externally specified sinks *) + let procedure = class_name ^ "." ^ method_name in + IList.find_map_opt + (fun (sink_spec : QuandaryConfig.Sink.t) -> + if String.equal sink_spec.procedure procedure + then + let kind = of_string sink_spec.kind in + try + let n = int_of_string sink_spec.index in + Some (taint_nth n kind ~report_reachable:true) + with Failure _ -> + (* couldn't parse the index, just taint everything *) + Some (taint_all kind ~report_reachable:true) + else + None) + external_sinks in begin match PatternMatch.supertype_find_map_opt diff --git a/infer/src/quandary/QuandaryConfig.ml b/infer/src/quandary/QuandaryConfig.ml index d7952f568..7ee2ef75d 100644 --- a/infer/src/quandary/QuandaryConfig.ml +++ b/infer/src/quandary/QuandaryConfig.ml @@ -11,7 +11,7 @@ open! IStd module F = Format -(** utilities for importing JSON specifications of sources/sinks into Quandary*) +(** utilities for importing JSON specifications of sources/sinks into Quandary *) module Source = struct type t = { procedure : string; kind : string; } @@ -30,3 +30,22 @@ module Source = struct let pp fmt { procedure; kind; } = F.fprintf fmt "Procedure: %s Kind: %s" procedure kind end + +module Sink = struct + type t = { procedure : string; kind : string; index : string} + + let of_json = function + | `List sinks -> + let parse_sink json = + let open Yojson.Basic.Util in + let procedure = json |> member "procedure" |> to_string in + let kind = json |> member "kind" |> to_string in + let index = json |> member "index" |> to_string in + { procedure; kind; index; } in + IList.map parse_sink sinks + | _ -> + [] + + let pp fmt { procedure; kind; index; } = + F.fprintf fmt "Procedure: %s Kind: %s Index %s" procedure kind index +end diff --git a/infer/src/quandary/QuandaryConfig.mli b/infer/src/quandary/QuandaryConfig.mli index a0395d9bf..44e573490 100644 --- a/infer/src/quandary/QuandaryConfig.mli +++ b/infer/src/quandary/QuandaryConfig.mli @@ -18,3 +18,11 @@ module Source : sig val pp : Format.formatter -> t -> unit end + +module Sink : sig + type t = { procedure : string; kind : string; index : string; } + + val of_json : [> `List of Yojson.Basic.json list ] -> t list + + val pp : Format.formatter -> t -> unit +end diff --git a/infer/tests/codetoanalyze/java/quandary/.inferconfig b/infer/tests/codetoanalyze/java/quandary/.inferconfig index 0822c7ec7..d54b8916a 100644 --- a/infer/tests/codetoanalyze/java/quandary/.inferconfig +++ b/infer/tests/codetoanalyze/java/quandary/.inferconfig @@ -4,5 +4,17 @@ "procedure": "codetoanalyze.java.quandary.ExternalSpecs.privateDataSource", "kind": "PrivateData" } + ], + "quandary-sinks": [ + { + "procedure": "codetoanalyze.java.quandary.ExternalSpecs.loggingSink1", + "kind": "Logging", + "index": "1" + }, + { + "procedure": "codetoanalyze.java.quandary.ExternalSpecs.loggingSink2", + "kind": "Logging", + "index": "all" + } ] } diff --git a/infer/tests/codetoanalyze/java/quandary/ExternalSpecs.java b/infer/tests/codetoanalyze/java/quandary/ExternalSpecs.java index 17bbc62ba..409a27116 100644 --- a/infer/tests/codetoanalyze/java/quandary/ExternalSpecs.java +++ b/infer/tests/codetoanalyze/java/quandary/ExternalSpecs.java @@ -34,4 +34,37 @@ public class ExternalSpecs { activity.startActivity((Intent) privateDataSource()); } + // we specify that index 1 is an external sink with type Logging in .inferconfig + public static void loggingSink1(Object notASink, Object sink) {} + + public static void callExternalSinkBad() { + loggingSink1(null, privateDataSource()); + } + + // passing to non-tainted param + public static void callExternalSinkOk1() { + loggingSink1(privateDataSource(), null); + } + + // passing intent source to logging sink is fine + public static void callExternalSinkOk2(Activity activity) { + loggingSink1(null, activity.getIntent()); + } + + // we specify that all the indices are tainted with type Logging in .inferconfig + public static void loggingSink2(Object sink1, Object sink2) {} + + public static void callExternalSink2Bad1() { + loggingSink2(privateDataSource(), null); + } + + public static void callExternalSink2Bad2() { + loggingSink2(null, privateDataSource()); + } + + // passing intent sources to logging sink is fine + public static void callExternalSink2Ok(Activity activity) { + loggingSink2(activity.getIntent(), activity.getIntent()); + } + } diff --git a/infer/tests/codetoanalyze/java/quandary/issues.exp b/infer/tests/codetoanalyze/java/quandary/issues.exp index 47ca45bfc..f08890207 100644 --- a/infer/tests/codetoanalyze/java/quandary/issues.exp +++ b/infer/tests/codetoanalyze/java/quandary/issues.exp @@ -43,6 +43,9 @@ codetoanalyze/java/quandary/Exceptions.java, void Exceptions.sinkInCatchBad2(), codetoanalyze/java/quandary/Exceptions.java, void Exceptions.sinkInFinallyBad1(), 5, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void InferTaint.inferSensitiveSink(Object)] codetoanalyze/java/quandary/Exceptions.java, void Exceptions.sinkInFinallyBad2(), 6, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void InferTaint.inferSensitiveSink(Object)] codetoanalyze/java/quandary/Exceptions.java, void Exceptions.sinkInFinallyBad3(), 7, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void InferTaint.inferSensitiveSink(Object)] +codetoanalyze/java/quandary/ExternalSpecs.java, void ExternalSpecs.callExternalSink2Bad1(), 1, QUANDARY_TAINT_ERROR, [return from Object ExternalSpecs.privateDataSource(),call to void ExternalSpecs.loggingSink2(Object,Object)] +codetoanalyze/java/quandary/ExternalSpecs.java, void ExternalSpecs.callExternalSink2Bad2(), 1, QUANDARY_TAINT_ERROR, [return from Object ExternalSpecs.privateDataSource(),call to void ExternalSpecs.loggingSink2(Object,Object)] +codetoanalyze/java/quandary/ExternalSpecs.java, void ExternalSpecs.callExternalSinkBad(), 1, QUANDARY_TAINT_ERROR, [return from Object ExternalSpecs.privateDataSource(),call to void ExternalSpecs.loggingSink1(Object,Object)] codetoanalyze/java/quandary/ExternalSpecs.java, void ExternalSpecs.logExternalSourceBad(), 1, QUANDARY_TAINT_ERROR, [return from Object ExternalSpecs.privateDataSource(),call to int Log.e(String,String)] codetoanalyze/java/quandary/Fields.java, void Fields.instanceFieldBad(), 2, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void InferTaint.inferSensitiveSink(Object)] codetoanalyze/java/quandary/Fields.java, void Fields.staticFieldBad(), 2, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void InferTaint.inferSensitiveSink(Object)]