# AppShark pins you to JDK 11 and hands you source-to-sink traces as JSON

> AppShark is a Kotlin static taint analysis tool for Android APKs that builds into a single fat jar, finds source-to-sink paths, and writes them into results.json. The catch is the runtime: JDK 11 only, and documentation that lives under doc/zh.

**bytedance/appshark** — Appshark is a static taint analysis platform to scan vulnerabilities in an Android app.

- Repository: https://github.com/bytedance/appshark
- Stars: 1,755 · Forks: 181
- Language: Kotlin
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/bytedance-appshark

## A fat jar and a config file, which is the whole interface

AppShark is a static taint analysis platform for scanning vulnerabilities in an Android app. The interface is narrow enough to describe in one sentence: you build one jar, you hand it a JSON config that names an APK, and you read a JSON report.

The build is Gradle from the repository root, and the README skips the tests:

```shell
./gradlew build  -x test 
```

That produces AppShark-0.1.2-all.jar in build/libs. The project is Kotlin, with build.gradle.kts and settings.gradle.kts at the top level, plus build.sh and run.sh for convenience.

Running it takes the jar and the config path:

```shell
java -jar build/libs/AppShark-0.1.2-all.jar  config/config.json5
```

The config is JSON5, which is worth noticing because the extension and the parser are not the same thing in every toolchain. The minimal config the README shows is a single apkPath, and everything else has a default. If the config sets out in the project root, the report lands at out/results.json.

That is the entire command surface. There is no watch mode, no incremental analysis, and no documented exit code that distinguishes a clean APK from a failed run, which matters when you put it in a pipeline.

## JDK 11 is a hard requirement, not a recommendation

The prerequisites section is two sentences and it is the most consequential thing in the README. AppShark requires JDK 11 specifically. After testing, it does not work on other LTS versions, and JDK 8 and JDK 16 are named as failing due to a dependency compatibility issue.

For an Android shop that means your standard toolchain, which usually spans several JDK versions across build agents, has to carry an exception for this tool. A scan job pinned to JDK 11 needs its own container image, and any upgrade cycle that moves agents to a newer LTS breaks the scan until someone reinstalls 11.

The report also says the UI and server are an auxiliary testing tool that should not be used in production environments and can only be deployed for local testing. Combined with the JDK constraint, the picture is of a research or internal assessment tool rather than a productised scanner, which is consistent with the release history: 0.1 on 2022-09-02, v0.1.1 on 2022-09-16 and v0.1.2 on 2022-09-22, with the last push on 2026-09-16 and no tag after 0.1.2.

AppShark is licensed under Apache-2.0.

## Every finding is a trace from entry method to sink

The value of a taint tool is in the path it can prove, and AppShark's output is explicit about it. A finding under FileRisk for unZipSlip carries category, detail, model, name, possibility, a wiki field that can be empty, a deobfApk path, and a list of vulners entries.

Each entry inside vulners is where the engineering value sits: position, Sink, entryMethod, Source, target, a url pointing at an HTML page for that specific finding, a hash, and a possibility score. In the sample, Source and Sink are both annotated expressions on the same method signature, and target is an ordered list of nodes between them, including a synthetic node that carries an object reference with a line number.

Two fields deserve attention. The url points to a rendered per-finding HTML page, so the JSON is a manifest for a browsable trail rather than the end of the audit. And the possibility value is a string, not a number, with the sample carrying "4" on both the finding and the entry, which suggests a coarse severity scale you should calibrate against your own policy rather than read as a probability.

The results document is also a general app inventory, not only findings. AppInfo gives AppName, PackageName, min_sdk, target_sdk, versionCode and versionName, which is the block to check first to confirm you analysed the APK you meant to. Then there are top-level arrays for DeepLinkInfo, HTTP_API, JsBridgeInfo, UsePermissions and DefinePermissions, plus BasicInfo with ComponentsInfo and JSNativeInterface, and a Profile path pointing at a profiler JSON file.

## Six config fields decide what gets scanned

The config file is where you scope a run. The README lists the basic fields and their defaults.

apkPath is the path of the APK to analyse and the only one you have to set. out is the output directory, and setting it in the project root is what produces out/results.json. rules is a comma separated list, defaulting to all *.json files in the rulePath directory. rulePath is the parent directory for rules, defaulting to ./config/rules. maxPointerAnalyzeTime is the timeout in seconds for analysis started from an entry point, which is the knob that bounds worst-case runtime on a pathological APK. debugRule names a single rule to turn on logging for.

For everything else the README points at the class net.bytedance.security.app.ArgumentConfig, which is where the full set lives. Reading that class is the practical next step when you need to know what a scan actually covered.

Because rules are JSON files in a directory and the default is to load all of them, the rule set is the analysis. That makes results reproducible, which is the good case, and it also means a missing rule produces a silent absence rather than an error. Nothing in the README explains how to verify that a specific rule loaded.

## Documentation lives under doc/zh and the index is the map

The README opens with a document index rather than a tutorial, and every entry points into doc/zh, which is the Chinese documentation directory. The list is: overview, startup, how to write rules, how to find compliance problems with appshark, a path traversal game, argument, engine config, result and faq.

Two of those titles tell you what the project is really for. How to write rules says rule authoring is an expected user activity, not a maintainer task, and a path traversal game suggests the intended way to learn the engine is a guided exercise rather than a reference. Engine config implies the analysis engine has its own configuration layer separate from the command line config file.

What this means in practice is that the English README covers build, run and report shape, and everything deeper is in another language. If your team cannot read it, the documentation gap, not the analysis, will be your bottleneck, and you should plan to learn the rule format by reading the rule files under config/rules instead.

The repository also carries CONTRIBUTING.md, and the README points contact through Lark, which is the only support channel described.

## Where AppShark sits against other Android scanners

The real difference AppShark offers is not analysis depth but evidence shape. A finding is a hash, an entry method, a source, a sink and an ordered target list, plus an HTML page per finding. That is what you want when a finding has to survive review and a developer has to fix it, which is why the sample report includes a url field pointing at a rendered trail.

The trade-off is everything a product would give you: pinned JDK 11, a documentation set in Chinese, a 0.1.2 release from September 2022, and a security notice that limits the UI and server to local testing. For a one-off assessment with an analyst who can write rules, those costs are acceptable. For a continuous pipeline across many apps, they are the reason to look at a maintained commercial scanner or a maintained open source one first.

## Conclusion

AppShark suits an Android security team that needs reproducible source-to-sink evidence for an APK and is willing to pin its build machines to JDK 11, since the README states it does not work on JDK 8 or JDK 16 because of dependency compatibility. It is the wrong choice for dynamic behaviour or for a CI image you cannot change, and the source is only as good as the rules you supply from config/rules. Verify first that your JDK is 11, build with ./gradlew build -x test, and check that out/results.json contains the AppInfo block for your package before you trust an empty finding list.

## FAQ

### Which Java version does AppShark need to build and run?

JDK 11. The README states that AppShark requires a specific version of JDK and that after testing it does not work on other LTS versions, with JDK 8 and JDK 16 failing due to a dependency compatibility issue.

### How do I run AppShark against an APK?

Run java -jar build/libs/AppShark-0.1.2-all.jar config/config.json5 from the project root, where the config sets apkPath to the APK you want analysed and optionally out to the output directory where results.json is written.

### Where are AppShark's analysis rules stored and how are they selected?

Rules are JSON files in the directory named by rulePath, which defaults to ./config/rules, and the rules config field selects them as a comma separated list, defaulting to all *.json files in that directory.

## Sources

- [bytedance/appshark on GitHub](https://github.com/bytedance/appshark)
- [Issues](https://github.com/bytedance/appshark/issues)
- [License: Apache-2.0](https://github.com/bytedance/appshark/blob/main/LICENSE)
- [README](https://github.com/bytedance/appshark/blob/main/README.md)
- [Releases](https://github.com/bytedance/appshark/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/bytedance-appshark
