Mobile App Security skill for Codex

Treat the app binary as attacker-owned. Codex reviews a mobile app group by group against OWASP MASVS v2, reading both source and the built release artifact with jadx, apktool and plist tooling. It flags tokens in plaintext storage, exported components, ATS and cleartext exceptions, WebView bridges and bundled secrets, maps each finding to a control, and pushes authorization fixes to the server instead of the client.

  • Security
  • $mobile-app-security
  • 136 lines
  • 2 files
  • ~1,950 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- mobile-app-security

Installs to ~/.agents/skills/mobile-app-security/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $mobile-app-security, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Reviews iOS and Android apps against OWASP MASVS v2 and the MASTG - insecure local storage, Keychain and Keystore use, cleartext traffic and pinning, exported components, deep links, WebViews, hardcoded secrets and release build settings - using source review plus static tools like MobSF, jadx and apktool. Use when auditing a React Native, Flutter, Swift or Kotlin app before release, when a pentest report cites MASVS, or when asked whether tokens, files or network calls in a mobile app are handled safely.

Use it when

  • A mobile app is about to ship and needs a security pass
  • You need to know whether tokens, keys or PII are stored safely on device
  • A pentest or store review cites MASVS or MASTG controls
  • You suspect API secrets are bundled into the app binary

Use something else when

How to check the result

Rerun manifest, plist and MobSF checks against the rebuilt release artifact, and confirm on an emulator that tokens are absent from app storage and a proxied release build refuses user-installed CAs.

The mistake it prevents

Treating root detection, pinning or obfuscation as the fix for an authorization flaw that only the server can close.

~/.agents/skills/mobile-app-security/SKILL.md

--- name: mobile-app-security description: Reviews iOS and Android apps against OWASP MASVS v2 and the MASTG - insecure local storage, Keychain and Keystore use, cleartext traffic and pinning,… license: MIT ---

Done well means: every MASVS control group was checked against the code and the built artifact (not only one of them), each finding names the file or manifest line plus the MASVS control it maps to, and the client is never trusted to enforce anything the server must enforce. A mobile app is a binary the attacker owns; review it with that assumption.

Scope and setup

  • Confirm what is in scope: source repo, a release build (.ipa/.aab/.apk), backend APIs. Dynamic testing (Frida, objection, proxying a device) requires written authorization from the app owner and a test device or emulator, never a user’s phone.
  • Read AGENTS.md, then identify the stack: native (Swift/Kotlin), React Native, Flutter, Capacitor. Cross-platform apps have two layers to review: the JS/Dart code and the native config (Info.plist, AndroidManifest.xml, entitlements, Gradle).
  • Pick the MAS testing profile with the owner: L1 (baseline, every app), L2 (apps handling sensitive data such as health, finance, identity), R (resilience against reverse engineering, only when the business model needs it).

Tools (installs need network; ask for approval in the Codex sandbox):

jadx -d out/jadx app-release.apk          # decompile to Java/Kotlin-ish source
apktool d app-release.apk -o out/apktool  # decoded manifest and resources
bundletool build-apks --bundle=app.aab --output=app.apks --mode=universal  # aab -> apk
apksigner verify --print-certs app-release.apk
unzip -q App.ipa -d out/ipa && plutil -p out/ipa/Payload/*.app/Info.plist
codesign -d --entitlements - out/ipa/Payload/*.app
docker run --rm -p 8000:8000 opensecurity/mobile-security-framework-mobsf  # MobSF static scan UI

Review by MASVS group

GroupCheckRed flag
STORAGEWhere tokens, PII and caches liveTokens in UserDefaults, SharedPreferences, AsyncStorage, SQLite or files without protection; sensitive data in logs or screenshots
CRYPTOKeys and algorithmsHardcoded keys or IVs, ECB, MD5/SHA-1 for security, custom crypto
AUTHSession and local authBiometric check that only gates UI (boolean) instead of unlocking a Keychain/Keystore key; long-lived tokens with no server-side revocation
NETWORKTransportNSAllowsArbitraryLoads, usesCleartextTraffic="true", user CAs trusted in release, disabled hostname verification
PLATFORMIPC, WebViews, deep linksExported activities/receivers/providers without permissions; addJavascriptInterface on remote content; deep-link params used unvalidated
CODEBuild and dependenciesdebuggable release, outdated SDKs, vulnerable third-party libraries, no minimum OS policy
RESILIENCEAnti-tampering (profile R only)Relying on root/jailbreak detection as a security control
PRIVACYData minimisationSDKs collecting more than declared in the privacy manifest or Data safety form

Grep pass over source

rg -n "UserDefaults|NSUserDefaults|kSecAttrAccessibleAlways|SharedPreferences|AsyncStorage|MMKV" src ios android lib
rg -n "allowBackup|usesCleartextTraffic|exported=\"true\"|debuggable|networkSecurityConfig" android
rg -n "NSAllowsArbitraryLoads|NSExceptionDomains|LSApplicationQueriesSchemes" ios
rg -n "setJavaScriptEnabled|addJavascriptInterface|setAllowFileAccess|WKWebView|evaluateJavascript" .
rg -n -i "api[_-]?key|secret|BEGIN (RSA|EC) PRIVATE|firebase.*key" --glob '!**/node_modules/**' .
rg -n "X509TrustManager|checkServerTrusted|ALLOW_ALL_HOSTNAME|HostnameVerifier" android

Platform rules

Android

  • Since Android 12 every component with an intent filter must declare android:exported. Anything exported="true" must validate its input and, if not meant for other apps, be set to false or protected with a signature permission.
  • Use a network_security_config.xml that trusts only system CAs in release; user CAs only inside <debug-overrides>.
  • Store secrets with keys from the Android Keystore (setUserAuthenticationRequired for biometric-gated keys). The Jetpack Security crypto library (EncryptedSharedPreferences) is deprecated; prefer Keystore-backed keys with DataStore, or Tink.
  • android:allowBackup="true" (default) copies app data into cloud and device backups. Set dataExtractionRules (Android 12+) and fullBackupContent to exclude tokens and databases.
  • FLAG_SECURE on screens that show secrets or one-time codes.

iOS

  • Keychain items use kSecAttrAccessibleWhenUnlockedThisDeviceOnly (or AfterFirstUnlock... ThisDeviceOnly for background refresh). Never kSecAttrAccessibleAlways.
  • Files with sensitive content use Data Protection .completeFileProtection.
  • ATS exceptions must be narrow (NSExceptionDomains for one host with a reason), never NSAllowsArbitraryLoads in a release build.
  • Universal links over custom URL schemes for anything carrying tokens; any app can claim a custom scheme.
  • Hide sensitive views when the app moves to background (snapshot in the app switcher).

Cross-platform

  • React Native: AsyncStorage is plaintext. Use react-native-keychain or expo-secure-store. Check that release bundles do not ship source maps or .env values (anything in EXPO_PUBLIC_* or react-native-config is in the binary).
  • Flutter: shared_preferences is plaintext; use flutter_secure_storage. Release builds with --obfuscate --split-debug-info only if profile R applies; obfuscation is not a security boundary.

Hard rules

  • Any key, API secret or signing material in the app is public. If the code needs a secret to call a third-party API, move that call behind your backend.
  • Root/jailbreak detection, pinning and obfuscation raise cost; they never replace server-side authorization. Every authorization finding is fixed on the server.
  • Certificate pinning needs a backup pin and a rollout plan. Pinning to a leaf that rotates bricks the installed base; pin to the SPKI of an intermediate or your own CA, with a remote kill switch.
  • App attestation (Play Integrity API, App Attest) is a risk signal for the backend, not a gate the client checks itself.
  • Do not publish, upload to public scanners or share the client’s binary or findings outside the engagement without explicit permission.

Verification

  • Rebuild the release variant and rerun the manifest and plist checks against the built artifact, not the source tree; build flavors often override manifests.
  • Re-scan with MobSF and compare against the first run.
  • For storage fixes, install on an emulator, log in, then inspect the sandbox (adb shell run-as <pkg> ls -R on debuggable test builds; on iOS, the simulator’s ~/Library/Developer/CoreSimulator/Devices/.../data/Containers/Data/Application/). Tokens must not appear in plaintext.
  • For network fixes, proxy a test device through an intercepting proxy with a user CA: a release build must refuse the connection.

Output template

## Mobile security review: Acme Wallet 4.2.0 (iOS 4.2.0 build 311, Android 4.2.0 vc 4200)
Profile: MAS-L2 | Scope: source @ a1b2c3d, release builds, /v2 API | Dynamic tests: emulator only

| ID | MASVS | Severity | Finding | Location | Fix |
|---|---|---|---|---|---|
| M-01 | STORAGE | High | Refresh token in AsyncStorage | src/auth/session.ts:42 | Move to react-native-keychain, WhenUnlockedThisDeviceOnly |
| M-02 | PLATFORM | Medium | Exported DeepLinkActivity passes `url` to WebView unvalidated | AndroidManifest.xml:88, DeepLinkActivity.kt:31 | Allowlist hosts; exported=false for internal routes |

Not tested: RESILIENCE (profile R out of scope); backend authz beyond /v2/wallet.

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Mobile App Security"
  short_description: "Treat the app binary as attacker-owned"
  default_prompt: "$mobile-app-security Check how our React Native app stores the auth token and whether that is safe"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem