android-code-security
Skill2 starsChanged 48 days agoMITView on GitHub
Audits native Android app code in Kotlin AND Java — tokens outside EncryptedSharedPreferences/Keystore, local SQLi via execSQL/rawQuery interpolation, TLS bypass in code (empty X509TrustManager, always-true HostnameVerifier, OkHttp trust-all), WebView @JavascriptInterface methods reaching sensitive ops with external URLs, deep-link intent.getStringExtra into startActivity/Class.forName, PendingIntent without FLAG_IMMUTABLE, hardcoded SecretKeySpec, Cipher.getInstance("AES") ECB default, MD5/SHA-1, seeded SecureRandom, token Log.d leakage, committed google-services.json keys. Use when the project has app/src/main Kotlin or Java code with an Android build.gradle.
In this repository23 other files it gives its agents.
- auth-reviewSkill
- config-hardeningSkill
- container-iac-securitySkill
- course-platform-securitySkill
- crypto-reviewSkill
- cve-researchSkill
- data-exposureSkill
- dependency-vulnsSkill
- design-threat-reviewSkill
- django-securitySkill
- dotnet-securitySkill
- flow-securitySkill
- flutter-securitySkill
- graphql-securitySkill
- injection-flawsSkill
- laravel-securitySkill
- llm-securitySkill
- mobile-securitySkill
- rails-securitySkill
- secrets-detectionSkill
- security-auditSkill
- skill-forgeSkill
- spring-securitySkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 72
Always-true verifier (lambda or verify → return true) → Critical.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Android Code Security (Kotlin + Java)
App-code shapes for native Android. The manifest/platform half (exported
components, allowBackup, cleartext policy, network security config) stays
with ../mobile-security/SKILL.md — run both; this skill owns what lives in
app/src/main/**. Sibling: ../flutter-security/SKILL.md (same classes,
Dart shapes).
Kotlin and Java share every platform API — most rules fire on both with one grep; the divergent shapes (lambdas vs anonymous classes, string templates vs concatenation) get their own pattern lines below.
Step 0 — Detect
rg --files -g 'build.gradle*' -g '*.kt' -g '*.java' | head -5
rg -n "com.android.application" -g 'build.gradle*'
rg --files -g 'google-services.json'Record: security-crypto (EncryptedSharedPreferences) present? OkHttp/Retrofit? WebView? Kotlin-only or mixed with Java legacy files?
Step 1 — Storage census (persona: device thief / rooted / adb backup)
rg -n "getSharedPreferences\(" -g '*.kt' -g '*.java' app/src/
rg -n "writeText\(|FileOutputStream\(" -g '*.kt' -g '*.java' app/src/
rg -n "SQLiteOpenHelper|execSQL\(|rawQuery\(" -g '*.kt' -g '*.java' app/src/Census discipline: every hit dispositioned (credential-class → finding;
theme/flags → verified-safe). Credential-class = tokens, keys, passwords,
PII. Plaintext SharedPreferences is world-readable to anything with the
device/userdata. Safe shape: EncryptedSharedPreferences.create(...) with a
MasterKey — presence alone doesn't clean a hit of getSharedPreferences
on a DIFFERENT name; check which store the token write targets.
Step 2 — Local SQLi (the app's own DB)
rg -n 'execSQL\(.*\$' -g '*.kt' app/src/ # Kotlin string templates
rg -n 'execSQL\(.*\+' -g '*.kt' -g '*.java' app/src/ # concatenation (both langs)
rg -n 'rawQuery\(.*(\$|\+)' -g '*.kt' -g '*.java' app/src/Interpolated/concatenated values = injection; the local DB often mirrors
server data and is readable by backups. Safe: bind args —
execSQL(sql, arrayOf(v)), rawQuery(sql, selectionArgs). Note: SQL
literals legitimately contain parens (VALUES (?), WHERE (a AND b)) —
the vuln signal is $/+ reaching the statement, not parens. Trace the
value: deeplink/intent-controlled input raises severity (other-app persona).
Step 3 — TLS bypass in code
rg -n 'checkServerTrusted[^{]*\{\}' -g '*.kt' -g '*.java' app/src/
rg -n 'HostnameVerifier\s*\{[^}]*true' -g '*.kt' app/src/
rg -n -A1 "boolean verify" -g '*.java' app/src/ # Java twin is TWO lines: signature, then `return true;`
rg -n "http://" -g '*.kt' -g '*.java' app/src/- Empty-body
checkServerTrusted(Kotlinoverride fun ...() {}or Java anonymous class) → Critical when wired into a client (look forsslSocketFactory(...)nearby). The empty method IS the trust kill. - Always-true verifier (lambda or
verify → return true) → Critical. http://constants → Medium; Critical when auth headers ride the request. localhost/test-config is the common safe near-miss — disposition it.- Safe: default trust +
CertificatePinnerfor token-carrying clients.
Step 4 — WebView bridge depth
rg -n "addJavascriptInterface|@JavascriptInterface" -g '*.kt' -g '*.java' app/src/
rg -n "loadUrl\((url|target|[a-z]+Url)" -g '*.kt' -g '*.java' app/src/
rg -n "javaScriptEnabled = true|setJavaScriptEnabled\(true\)" -g '*.kt' -g '*.java' app/src/../mobile-security/SKILL.md flags bridge existence from the manifest side;
HERE you open every @JavascriptInterface method body: file reads, payment,
token access, intent launching → Critical when the URL is external
(deeplink/intent-sourced). loadUrl( with a constant https string is the
safe near-miss; a variable is the vuln shape.
Step 5 — Intent & deep-link flow (persona: any other app on the device)
rg -n "getStringExtra\(|getData\(|getParcelableExtra\(" -g '*.kt' -g '*.java' app/src/
rg -n "Class\.forName|setComponent\(|setPackage\(" -g '*.kt' -g '*.java' app/src/
rg -n "PendingIntent\.getActivity\(" -g '*.kt' -g '*.java' app/src/- External string →
Class.forName/setComponent→startActivity= the app instantiates any class another app names (private activities, side-effectful receivers) → High. PendingIntent.getActivity(ctx, req, intent, 0)— flags=0 = MUTABLE pending intent → High (hijack/rewrite by other apps pre-12 semantics). Safe:PendingIntent.FLAG_IMMUTABLEin the flags argument — same call, flags differ; check the LAST argument, not the call itself.- Route allowlist maps (
intent.data.lastPathSegment→ const map) = safe shape.
Step 6 — Crypto
rg -n "SecretKeySpec\(" -g '*.kt' -g '*.java' app/src/
rg -n 'Cipher\.getInstance\("AES"\)' -g '*.kt' -g '*.java' app/src/
rg -n 'getInstance\("(MD5|SHA-1)"\)' -g '*.kt' -g '*.java' app/src/
rg -n "setSeed\(" -g '*.kt' -g '*.java' app/src/SecretKeySpec(hardcodedBytes, "AES")→ Critical: key extractable from APK, never rotatable. Safe: AndroidKeyStoreKeyGenParameterSpec.Cipher.getInstance("AES")→ ECB default → High."AES/GCM/NoPadding"= safe.- MD5/SHA-1: severity by what it protects (fingerprint/ticket = Medium; password/token = Critical).
setSeed(constant)on SecureRandom → High — predictable OTPs/tokens.
Step 7 — Secrets artifacts & logcat
rg -n "current_key|api_key" -g 'google-services.json'
rg -n 'Log\.[dvi]\([^)]*[Tt]oken' -g '*.kt' -g '*.java' app/src/
rg -n -i "(api[_-]?key|secret|token)\s*=\s*\"[^\"]{8,}" -g '*.kt' -g '*.java' -g 'strings.xml' -g 'build.gradle*'- google-services.json
current_key(AIza…) committed → Medium baseline: check Firebase key restrictions + which APIs are enabled before rating higher; unrestricted keys are attacker-budget risks. - Token values in
Log.d/v/i→ Medium (logcat: adb, bug reports, vendor crash dumps).if (BuildConfig.DEBUG)gate + static message = safe. - Hardcoded key assignments → route via
../secrets-detection/SKILL.md.
Step 8 — Dependencies & build hygiene
build.gradle deps: dependency-vulns' osv_scan parses gradle.lockfile
(maven ecosystem) — plain implementation coordinates need manual OSV
lookup by group:artifact:version; note as limitation in the report.
minifyEnabled false in release + no resource obfuscation → informational.
Reporting
Severity table as in steps. Fixes are short: EncryptedSharedPreferences
create-call, bind-args execSQL, FLAG_IMMUTABLE, Keystore keygen. Manifest
findings → ../mobile-security/SKILL.md; Flutter equivalents →
../flutter-security/SKILL.md; dep CVEs → ../dependency-vulns/SKILL.md.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

