diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml
index 39fe3bc0d..995cd1d24 100644
--- a/infer/src/base/Config.ml
+++ b/infer/src/base/Config.ml
@@ -889,7 +889,7 @@ and load_average =
 
 and load_results =
   CLOpt.mk_path_opt ~deprecated:["load_results"] ~long:"load-results"
-    ~exes:CLOpt.[Print]
+    ~exes:CLOpt.[Analyze]
     ~meta:"file.iar" "Load analysis results from Infer Analysis Results file file.iar"
 
 (** name of the makefile to create with clusters and dependencies *)
@@ -1006,6 +1006,9 @@ and progress_bar =
     ~exes:CLOpt.[Driver]
     "Show a progress bar"
 
+and quandary_sources =
+  CLOpt.mk_json ~long:"quandary-sources" "Specify custom sources for Quandary"
+
 and quiet =
   CLOpt.mk_bool ~long:"quiet" ~short:"q" ~default:(current_exe <> CLOpt.Print)
     ~exes:CLOpt.[Print]
@@ -1475,6 +1478,7 @@ and print_using_diff = !print_using_diff
 and procs_csv = !procs_csv
 and procs_xml = !procs_xml
 and quandary = !quandary
+and quandary_sources = !quandary_sources
 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 e1257bbd2..2c89b2583 100644
--- a/infer/src/base/Config.mli
+++ b/infer/src/base/Config.mli
@@ -242,6 +242,7 @@ val procs_csv : string option
 val procs_xml : string option
 val project_root : string
 val quandary : bool
+val quandary_sources : Yojson.Basic.json
 val quiet : bool
 val reactive_mode : bool
 val reactive_capture : bool
diff --git a/infer/src/checkers/Source.ml b/infer/src/checkers/Source.ml
index 6dd483968..7652a5240 100644
--- a/infer/src/checkers/Source.ml
+++ b/infer/src/checkers/Source.ml
@@ -46,7 +46,7 @@ module Make (Kind : Kind) = struct
   type kind =
     | Normal of Kind.t (** known source returned directly or transitively from a callee *)
     | Footprint of AccessPath.t (** unknown source read from the environment *)
-  [@@ deriving compare]
+  [@@deriving compare]
 
   let pp_kind fmt = function
     | Normal kind -> Kind.pp fmt kind
diff --git a/infer/src/quandary/JavaTrace.ml b/infer/src/quandary/JavaTrace.ml
index da052830a..40feddcd4 100644
--- a/infer/src/quandary/JavaTrace.ml
+++ b/infer/src/quandary/JavaTrace.ml
@@ -22,6 +22,13 @@ module SourceKind = struct
 
   let unknown = Unknown
 
+  let of_string = function
+    | "PrivateData" -> PrivateData
+    | "Intent" -> Intent
+    | _ -> Other
+
+  let external_sources = QuandaryConfig.Source.of_json Config.quandary_sources
+
   let get pname tenv = match pname with
     | Procname.Java pname ->
         begin
@@ -49,10 +56,24 @@ module SourceKind = struct
                     Some PrivateData
                 | _ ->
                     None in
-              PatternMatch.supertype_find_map_opt
-                tenv
-                taint_matching_supertype
-                (Typename.Java.from_string class_name)
+              let kind_opt =
+                PatternMatch.supertype_find_map_opt
+                  tenv
+                  taint_matching_supertype
+                  (Typename.Java.from_string class_name) in
+              begin
+                match kind_opt with
+                | Some _ -> kind_opt
+                | None ->
+                    (* check the list of externally specified sources *)
+                    let procedure = class_name ^ "." ^ method_name in
+                    IList.find_map_opt
+                      (fun (source_spec : QuandaryConfig.Source.t) ->
+                         if String.equal source_spec.procedure procedure
+                         then Some (of_string source_spec.kind)
+                         else None)
+                      external_sources
+              end
         end
     | pname when BuiltinDecl.is_declared pname -> None
     | pname -> failwithf "Non-Java procname %a in Java analysis@." Procname.pp pname
diff --git a/infer/src/quandary/QuandaryConfig.ml b/infer/src/quandary/QuandaryConfig.ml
new file mode 100644
index 000000000..d7952f568
--- /dev/null
+++ b/infer/src/quandary/QuandaryConfig.ml
@@ -0,0 +1,32 @@
+(*
+ * 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.
+ *)
+
+open! IStd
+
+module F = Format
+
+(** utilities for importing JSON specifications of sources/sinks into Quandary*)
+
+module Source = struct
+  type t = { procedure : string; kind : string; }
+
+  let of_json = function
+    | `List sources ->
+        let parse_source json =
+          let open Yojson.Basic.Util in
+          let procedure = json |> member "procedure" |> to_string in
+          let kind = json |> member "kind" |> to_string in
+          { procedure; kind; } in
+        IList.map parse_source sources
+    | _ ->
+        []
+
+  let pp fmt { procedure; kind; } =
+    F.fprintf fmt "Procedure: %s Kind: %s" procedure kind
+end
diff --git a/infer/src/quandary/QuandaryConfig.mli b/infer/src/quandary/QuandaryConfig.mli
new file mode 100644
index 000000000..a0395d9bf
--- /dev/null
+++ b/infer/src/quandary/QuandaryConfig.mli
@@ -0,0 +1,20 @@
+(*
+ * Copyright (c) 2016 - 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.
+ *)
+
+open! IStd
+
+(** utilities for importing JSON specifications of sources/sinks into Quandary*)
+
+module Source : sig
+  type t = { procedure : string; kind : 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
new file mode 100644
index 000000000..0822c7ec7
--- /dev/null
+++ b/infer/tests/codetoanalyze/java/quandary/.inferconfig
@@ -0,0 +1,8 @@
+{
+  "quandary-sources": [
+    {
+      "procedure": "codetoanalyze.java.quandary.ExternalSpecs.privateDataSource",
+      "kind": "PrivateData"
+    }
+  ]
+}
diff --git a/infer/tests/codetoanalyze/java/quandary/ExternalSpecs.java b/infer/tests/codetoanalyze/java/quandary/ExternalSpecs.java
new file mode 100644
index 000000000..17bbc62ba
--- /dev/null
+++ b/infer/tests/codetoanalyze/java/quandary/ExternalSpecs.java
@@ -0,0 +1,37 @@
+/*
+ * 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 android.app.Activity;
+import android.content.Intent;
+import android.util.Log;
+
+import com.facebook.infer.builtins.InferTaint;
+
+/** Testing that sources and sinks specified in external JSON work correctly */
+
+public class ExternalSpecs {
+
+  // we specify this as a source with kind PrivateData in .inferconfig
+  private static Object privateDataSource() {
+    return new Object();
+  }
+
+  public static void logExternalSourceBad() {
+    Log.e("", (String) privateDataSource());
+  }
+
+  // we specified that this is a private data source, so passing it an intent sink like
+  // startActivity() is fine
+  public static void externalSourceAsIntentOk(Activity activity) {
+    activity.startActivity((Intent) privateDataSource());
+  }
+
+}
diff --git a/infer/tests/codetoanalyze/java/quandary/issues.exp b/infer/tests/codetoanalyze/java/quandary/issues.exp
index f6138a0c1..47ca45bfc 100644
--- a/infer/tests/codetoanalyze/java/quandary/issues.exp
+++ b/infer/tests/codetoanalyze/java/quandary/issues.exp
@@ -43,6 +43,7 @@ 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.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)]
 codetoanalyze/java/quandary/Fields.java, void Fields.viaFieldBad1(Fields$Obj), 2, QUANDARY_TAINT_ERROR, [return from Object InferTaint.inferSecretSource(),call to void InferTaint.inferSensitiveSink(Object)]