Repository navigation
Taint analysis
AndroidAnalysis.analyzeTaint(...) runs FlowDroid synchronously and returns an immutable TaintAnalysisResult. Check its completion status before interpreting findings, especially an empty list. The result contains detached values that remain usable after the analysis session closes.
The session default is empty. Use SourceSinkConfiguration.builder(), fromFile(path), or an explicit bundledDefaults() configuration. fromFile includes exactly that file's definitions; it does not add bundled entries.
The file format is UTF-8, one TYPE|signature record per line:
# Replace these example APIs with methods relevant to your analysis.
SOURCE|<com.example.Input: java.lang.String read()>
SINK|<com.example.Output: void send(java.lang.String)>
Types are case-sensitive SOURCE or SINK. Blank lines and lines whose first nonwhitespace character is # are ignored. Constructors and array parameter types are accepted. Malformed records fail with their file and line number. Definitions are syntax-checked without resolving methods; this does not verify that the named methods exist or are reached in your APK. See SourcesSinksManager for configuration composition and legacy loading.
This complete TaintReport.java application accepts <apk> <platforms> <sources-sinks-file> <output-json>. It serializes the result after closing the session and does not discard partial findings.
The application must declare Gson 2.14.0 itself; AndroSpecter's result API does not require a JSON library. Add this dependency to the consumer POM alongside the locally installed com.jordansamhi:androspecter:2.0.0 dependency:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.14.0</version>
</dependency>import com.google.gson.GsonBuilder;
import com.jordansamhi.androspecter.analysis.AndroidAnalysis;
import com.jordansamhi.androspecter.files.SourceSinkConfiguration;
import com.jordansamhi.androspecter.result.TaintAnalysisResult;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* Command-line example that serializes detached taint results with an application-owned Gson dependency.
* The default constructor performs no analysis; invoke {@link #main(String[])} to run it.
*/
public final class TaintReport {
/**
* Runs taint analysis with explicit file definitions, then writes JSON after closing the session.
* Partial results are written with their status and diagnostics; existing output is replaced.
*
* @param args APK path, SDK platforms directory, source/sink file, and output JSON path
* @throws IllegalArgumentException if the argument count, input paths, or definitions are invalid
* @throws Exception if initialization, file loading, output-directory creation, or JSON writing fails
*/
public static void main(String[] args) throws Exception {
if (args.length != 4) {
throw new IllegalArgumentException(
"Usage: TaintReport <apk> <platforms> <sources-sinks-file> <output-json>");
}
var definitions = SourceSinkConfiguration.fromFile(Path.of(args[2]));
TaintAnalysisResult result;
try (var analysis = AndroidAnalysis.open(Path.of(args[0]), Path.of(args[1]))) {
result = analysis.analyzeTaint(definitions);
}
System.out.println("Analysis: " + result.analysisId());
System.out.println("Status: " + result.status());
System.out.println("Findings: " + result.findings().size());
result.diagnostics().forEach(System.err::println);
for (var finding : result.findings()) {
System.out.println("Source API: " + finding.source().invokedMethodSignature());
System.out.println("Sink API: " + finding.sink().invokedMethodSignature());
System.out.println("Path available: " + finding.pathAvailable());
}
Path output = Path.of(args[3]).toAbsolutePath();
Files.createDirectories(output.getParent());
var gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
Files.writeString(output, gson.toJson(result), StandardCharsets.UTF_8);
}
}The repository's AnalyzeTaint example provides a runnable consumer with this CLI. From the repository root, after mvn -B install:
mvn -B -f examples/pom.xml package dependency:copy-dependencies
java -Xmx4g -cp 'examples/target/classes:examples/target/dependency/*' \
com.jordansamhi.androspecter.examples.AnalyzeTaint \
"$APK" "$PLATFORMS" "$SOURCES_SINKS_FILE" "$OUTPUT_JSON"| Status | Interpretation |
|---|---|
SUCCESS |
Backend completed without a reported failure; the finding list may be empty |
FAILED |
Backend failure, missing backend result, reported exceptions, or incomplete result detachment; inspect diagnostics |
TIMED_OUT |
Backend data-flow or path-reconstruction timeout; findings may be partial |
CANCELLED |
Cancellation or interruption observed; findings may be partial or absent |
Findings returned by FlowDroid are retained when they can be detached, including on incomplete runs. If the backend throws before returning a result, findings are unavailable. If detachment fails partway through, findings already copied remain available. Programming/lifecycle errors such as a closed session, wrong thread, external Soot reset, or null configuration still throw; JVM errors are not converted into result statuses.
An empty list with FAILED, TIMED_OUT, or CANCELLED is not a completed no-findings result. Even SUCCESS does not prove absence of real flows or certify analysis soundness. Backend parser and typing warnings remain in the caller's logs; result diagnostics contain reported exceptions and termination information, not every log message. Retain logs alongside serialized results when those warnings matter to your evaluation.
Each finding has source(), sink(), path(), and pathAvailable().
| Endpoint field | Meaning |
|---|---|
description() |
FlowDroid's textual endpoint description |
methodSignature() |
Enclosing method's Jimple signature, or null when its owner is unavailable |
statement() |
Processed Jimple statement text, or null when unavailable |
invokedMethodSignature() |
Method reference invoked by that statement, or null for a non-invocation/unavailable endpoint |
accessPath() |
Textual tainted access path, or null when unavailable |
Use invokedMethodSignature() to filter by the API called at an endpoint; methodSignature() identifies the containing method instead. To compare safely when invocation metadata may be null, put the known string on the left of equals.
This method-scope snippet assumes TaintAnalysisResult result is available. Its only required import is com.jordansamhi.androspecter.result.TaintAnalysisResult:
String sourceApi = "<com.example.Input: java.lang.String read()>";
var matching = result.findings().stream()
.filter(finding -> sourceApi.equals(finding.source().invokedMethodSignature()))
.toList();
System.out.println(matching.size());pathAvailable() == false means the backend did not supply a reconstructed path; the flow is still retained and path() is empty. A present but empty path has pathAvailable() == true. Each path step has processed statement text and a nullable enclosing method signature. Path availability alone does not imply that the whole analysis completed. These strings describe the processed model, not original Java source.
All result collections are immutable, and records retain no live Soot objects or exception objects. JSON field names follow the record components with the chosen serializer; the library does not define a versioned exchange format. serializeNulls() in the example makes unavailable metadata explicit.
Configure taintTimeout(Duration) and pathTimeout(Duration) on the session builder before opening it. callbackTimeout(Duration) limits DEFAULT callback discovery and may yield a partial graph; FlowDroid 2.15.1's FAST callback analyzer ignores that timeout. Phase limits are not whole-operation deadlines. Cancellation is cooperative: keep the session open until the running operation returns. See Cancellation and logging.
TaintResultFormatter.format(result) returns immutable legacy report strings only for SUCCESS; it throws for incomplete results. formatFinding(finding) can render a partial individual finding, but the caller must present the containing result's status and diagnostics separately. The legacy FlowdroidUtils string-returning method also throws for incomplete analysis rather than returning an empty success-looking set.
Source: result records and formatter, SourceSinkConfiguration.
- Analysis sessions
- APK metadata
- Call graphs and inspection
- Graph export and comparison
- Taint analysis
- Instrumentation
- Cancellation and logging
- Migration and troubleshooting
- Publishing releases