mobilepkg inspects APK, AAB, APKS, XAPK, and IPA files and emits package metadata plus security findings as JSON, Markdown, or RDJSONL. It is intended for release triage and CI checks.
It reads the package as a zip archive, parses the manifest and DEX bytecode in-process, and finishes in seconds. No Android SDK, Xcode, or device required. Runs on Linux, Windows, and macOS (Go 1.25+).
- Manifest metadata, signing info, permissions, exported components, deep links, and network endpoints from Android packages (APK, XAPK, APKS, AAB).
- DEX bytecode scanning across base and split APKs for hardcoded secrets, cleartext HTTP URLs, dangerous API calls, and insecure WebView configuration.
- XAPK and APKS bundles read directly — no prior extraction needed.
- Markdown reports for
$GITHUB_STEP_SUMMARYand pull request comments. - Baseline diff for tracking permission, component, and version changes between releases.
- CI integration:
--fail-on warnexits non-zero when findings exceed a severity threshold.
- On well-maintained production apps, mobilepkg typically reports
allow_backup, a few exported components, and some dangerous API calls from third-party libraries. Library-originated API calls (e.g. crash reporters usingRuntime.exec) are automatically downgraded towarn/medium. - Cleartext URL findings from DEX strings may include legitimate logging or configuration endpoints that happen to use HTTP.
- iOS IPA inspection covers entitlements, ATS settings, code signing certificates, provisioning profile expiry, URL schemes, and associated domains. It does not scan compiled Swift/ObjC binaries for API calls or secrets. Android inspection is deeper.
- WebView argument tracking is lightweight: it checks the immediately preceding
const/4orconst/16instruction. Non-trivial argument flow (e.g. values loaded from fields or computed) falls back to conservative reporting.
| Format | Platform | Description |
|---|---|---|
| APK | Android | Standard Android package |
| XAPK | Android | APKPure extended package |
| APKS | Android | Bundletool APK set |
| AAB | Android | Android App Bundle |
| IPA | iOS | Standard iOS package |
go install github.com/nao1215/mobilepkg/cmd/mobilepkg@latest # CLI
go get github.com/nao1215/mobilepkg # library$ mobilepkg inspect app.apk # JSON output (default)
$ mobilepkg inspect --format markdown app.apk # Markdown summary
$ mobilepkg inspect --format markdown app.xapk # XAPK works the same way
$ mobilepkg inspect --fail-on warn app.apk # CI: exit 1 if severity >= warn
$ mobilepkg inspect --baseline prev.json app.apk # diff against previous resultThe --format markdown output leads with the most actionable information — top findings and severity counts — before expanding into component and endpoint details. It is designed for $GITHUB_STEP_SUMMARY, pull request comments, and manual review.
Example: vulnerable app (AndroGoat)
On an intentionally vulnerable app like AndroGoat, the report surfaces debug builds, hardcoded AWS keys, command injection via Runtime.exec, and unguarded exported components:
## Top Findings
> [!WARNING]
> 10 finding(s) at warning severity or above.
| ID | Severity | Confidence | Message |
|----|----------|------------|---------|
| dex.api.java.lang.Runtime.exec... | error | high | Runtime.exec() called — potential command injection risk |
| dex.secret.aws_key... | error | high | potential aws_key found in DEX string table |
| manifest.debuggable | error | high | application is debuggable |
| signing.debug_cert | error | high | signed with debug certificate |
| dex.cleartext... | warn | medium | cleartext HTTP URL found in DEX strings: demo.testfire.net |
| manifest.allow_backup | warn | high | application allows backup |
| exported.provider... | warn | high | exported provider: ...ContentProviderActivity |Example: normal app (F-Droid)
On a well-maintained production app like F-Droid, findings are less severe. Library-originated API calls are flagged at reduced severity. The deep link and endpoint sections show the app's registered URI handlers:
## Top Findings
> [!WARNING]
> 6 finding(s) at warning severity or above.
| ID | Severity | Confidence | Message |
|----|----------|------------|---------|
| dex.api...Runtime.exec...compat.FileCompat | error | high | Runtime.exec() called (in app code) |
| dex.api...Runtime.exec...acra... | warn | medium | Runtime.exec() called (in library) |
| dex.cleartext.logback.qos.ch | warn | medium | cleartext HTTP URL found in DEX strings |
| manifest.allow_backup | warn | high | application allows backup |
## Network Endpoints
| Endpoint | Source | Confidence |
|----------|--------|------------|
| f-droid.org | intent_filter | high |
| play.google.com | intent_filter | high |
| market://details | intent_filter | high |The default JSON output is a ReportFile with schema_version, tool_version, and result:
Example JSON (AndroGoat)
{
"schema_version": "1.0.0",
"tool_version": "0.1.0",
"result": {
"platform": "android",
"format": "apk",
"identity": {
"identifier": "owasp.sat.agoat",
"display_name": "AndroGoat - Insecure App (Kotlin)"
},
"version": { "marketing": "1.0", "build": "1" },
"debuggable": true,
"allow_backup": true,
"permissions": [
{ "canonical": "camera", "raw_name": "android.permission.CAMERA", "source": "manifest" },
{ "canonical": "network", "raw_name": "android.permission.INTERNET", "source": "manifest" }
],
"exported_components": [
{ "kind": "provider", "name": "owasp.sat.agoat.ContentProviderActivity", "exported": true },
{ "kind": "receiver", "name": "owasp.sat.agoat.ShowDataReceiver", "exported": true }
],
"signing": {
"scheme": "v1+v2",
"certificates": [{ "subject": "Android Debug", "issuer": "Android Debug" }]
},
"findings": [
{ "severity": "error", "id": "manifest.debuggable", "message": "application is debuggable" },
{ "severity": "error", "id": "signing.debug_cert", "message": "signed with debug certificate" },
{ "severity": "warn", "id": "manifest.allow_backup", "message": "application allows backup" }
],
"secret_candidates": [],
"diagnostics": []
}
}Save a report and use it as the baseline for the next run. The --baseline flag accepts a report.json file produced by a previous inspect run:
# Save today's result
mobilepkg inspect app.apk > baseline.json
# Compare against baseline in CI
mobilepkg inspect --baseline baseline.json --fail-on warn app.apkWhen --fail-on is specified, the output includes a verdict field with passed, reasons, and triggering_findings, and the command exits with code 1 if the policy is violated.
$ mobilepkg compare old.apk new.apkShows identity, version, and entry point changes, plus added/removed permissions, exported components, and network endpoints.
result, err := mobilepkg.InspectFile(ctx, "app.apk")
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Identity.Identifier) // "com.example.app"
fmt.Println(result.Debuggable) // true
for _, f := range result.Findings {
fmt.Printf("[%s] %s\n", f.Severity, f.Message)
}| Category | Findings |
|---|---|
| Manifest | debuggable, allowBackup, usesCleartextTraffic, testOnly, profileableByShell |
| Network security config | Base-config cleartext, per-domain cleartext, nested domain-config inheritance, debug-overrides |
| Signing | Debug certificates, expired certificates, v1-only signing, weak digest (MD5/SHA-1), weak key size (RSA<2048, ECDSA<256), self-signed test certificates |
| Provisioning (iOS) | Expired provisioning profile |
| Components | Exported activities/services/receivers/providers with intent-filters and deep links. No-permission components elevated to warn/error. Browsable activities flagged. Provider authorities, readPermission, writePermission, grantUriPermissions extracted. |
| Permissions | Dangerous permissions (CAMERA, SMS, LOCATION, etc.) |
| iOS ATS | NSAllowsArbitraryLoads, insecure exception domains (NSExceptionAllowsInsecureHTTPLoads) |
| iOS entitlements | get-task-allow (debug build), URL schemes, associated domains |
| Secrets | Regex-based scan of manifest/plist metadata and DEX string tables (AWS keys, GCP API keys, GitHub tokens, private keys, bearer tokens, Firebase URLs, generic api_key/secret/credential patterns). Both sources use the same pattern list. |
| DEX WebView | setJavaScriptEnabled(true), addJavascriptInterface, setAllowFileAccess(true), setAllowUniversalAccessFromFileURLs(true), setWebContentsDebuggingEnabled(true), setMixedContentMode, SslErrorHandler.proceed bypass, WebView.loadUrl("http://..."). Boolean argument tracking via preceding const/4/const/16. Library-originated calls reported at reduced severity. |
| DEX APIs | Runtime.exec, ProcessBuilder, DexClassLoader, PathClassLoader, Method.invoke, SmsManager.sendTextMessage, DevicePolicyManager.resetPassword, Cipher.getInstance. Library-originated calls reported at reduced severity. |
| DEX cleartext | http:// URLs in string tables (localhost/schema/spec URLs excluded). |
verdict := mobilepkg.Check(result, mobilepkg.DefaultFailPolicy())
if !verdict.Passed {
os.Exit(1)
}result, _ := mobilepkg.InspectWithBaseline(ctx, "new.apk", prevResult)
fmt.Println(result.Diff.VersionChanged)
fmt.Println(result.Diff.AddedComponents)rf := mobilepkg.NewReportFile(result, "1.0.0")
mobilepkg.WriteReportJSON(os.Stdout, rf) // JSON
mobilepkg.WriteSummaryMarkdown(os.Stdout, rf) // Markdown
mobilepkg.WriteRDJSONL(os.Stdout, result.Findings, "app.apk") // reviewdog- run: mobilepkg inspect --fail-on warn app.apk > report.json
- run: mobilepkg inspect --format markdown app.apk >> "$GITHUB_STEP_SUMMARY"Examples use AndroGoat (v2.0.1), an intentionally vulnerable Android app. The APK is under testdata/no_commit/ for local testing only and is not committed.
mkdir -p testdata/no_commit
curl -L -o testdata/no_commit/AndroGoat.apk \
https://github.com/satishpatnayak/AndroGoat/releases/download/v2.0.1/AndroGoat.apkSee CONTRIBUTING.md.
- shogo82148/androidbinary: Android binary file parser written in golang
- nao1215/deapk(already public archived): parse android package (.apk), getting meta data and more.
