Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Mobile application security skill for implementing OWASP MASVS compliance, secure storage, certificate pinning, biometric authentication, and security hardening across iOS and Android platforms.
.claude/skills/a5c-ai-mobile-security/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 2290% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 1784% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 81% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 70% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 50% | 0% |
Comprehensive mobile application security implementation for iOS and Android platforms, covering OWASP Mobile Security guidelines, secure storage, authentication, and security hardening.
This skill provides capabilities for implementing mobile security best practices, including secure data storage, network security, authentication mechanisms, and compliance with OWASP Mobile Application Security Verification Standard (MASVS).
bash# TrustKit for certificate pinning pod 'TrustKit' # Keychain wrapper pod 'KeychainAccess'
groovy// build.gradle dependencies { implementation 'androidx.security:security-crypto:1.1.0-alpha06' implementation 'androidx.biometric:biometric:1.1.0' }
bash# OWASP Mobile Security Testing Guide tools pip install objection brew install frida-tools
swiftimport Security class KeychainManager { static func save(key: String, data: Data) -> Bool { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: key, kSecValueData as String: data, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly ] SecItemDelete(query as CFDictionary) let status = SecItemAdd(query as CFDictionary, nil) return status == errSecSuccess } static func load(key: String) -> Data? { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: key, kSecReturnData as String: true, kSecMatchLimit as String: kSecMatchLimitOne ] var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) return status == errSecSuccess ? result as? Data : nil } }
kotlinimport androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey class SecureStorage(context: Context) { private val masterKey = MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() private val sharedPreferences = EncryptedSharedPreferences.create( context, "secure_prefs", masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) fun saveToken(token: String) { sharedPreferences.edit().putString("auth_token", token).apply() } fun getToken(): String? { return sharedPreferences.getString("auth_token", null) } }
swiftimport TrustKit class NetworkSecurityManager { static func configurePinning() { let trustKitConfig: [String: Any] = [ kTSKSwizzleNetworkDelegates: false, kTSKPinnedDomains: [ "api.example.com": [ kTSKEnforcePinning: true, kTSKIncludeSubdomains: true, kTSKExpirationDate: "2027-01-01", kTSKPublicKeyHashes: [ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" ] ] ] ] TrustKit.initSharedInstance(withConfiguration: trustKitConfig) } }
kotlinimport okhttp3.CertificatePinner import okhttp3.OkHttpClient val certificatePinner = CertificatePinner.Builder() .add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") .add("api.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=") .build() val client = OkHttpClient.Builder() .certificatePinner(certificatePinner) .build()
swiftimport LocalAuthentication class BiometricAuth { func authenticate(completion: @escaping (Bool, Error?) -> Void) { let context = LAContext() var error: NSError? if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) { context.evaluatePolicy( .deviceOwnerAuthenticationWithBiometrics, localizedReason: "Authenticate to access secure data" ) { success, error in DispatchQueue.main.async { completion(success, error) } } } else { completion(false, error) } } }
kotlinimport androidx.biometric.BiometricPrompt import androidx.fragment.app.FragmentActivity class BiometricAuth(private val activity: FragmentActivity) { fun authenticate(onSuccess: () -> Unit, onError: (String) -> Unit) { val executor = ContextCompat.getMainExecutor(activity) val biometricPrompt = BiometricPrompt(activity, executor, object : BiometricPrompt.AuthenticationCallback() { override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { onSuccess() } override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { onError(errString.toString()) } }) val promptInfo = BiometricPrompt.PromptInfo.Builder() .setTitle("Biometric Authentication") .setSubtitle("Authenticate to access secure data") .setNegativeButtonText("Cancel") .build() biometricPrompt.authenticate(promptInfo) } }
javascriptconst mobileSecurityTask = defineTask({ name: 'mobile-security-implementation', description: 'Implement mobile security controls', inputs: { platform: { type: 'string', required: true, enum: ['ios', 'android', 'both'] }, securityLevel: { type: 'string', required: true, enum: ['L1', 'L2'] }, features: { type: 'array', items: { type: 'string' } }, projectPath: { type: 'string', required: true } }, outputs: { implementedControls: { type: 'array' }, complianceReport: { type: 'object' }, securityAuditPath: { type: 'string' } }, async run(inputs, taskCtx) { return { kind: 'skill', title: `Implement ${inputs.securityLevel} security for ${inputs.platform}`, skill: { name: 'mobile-security', context: { operation: 'implement_security', platform: inputs.platform, securityLevel: inputs.securityLevel, features: inputs.features, projectPath: inputs.projectPath } }, io: { inputJsonPath: `tasks/${taskCtx.effectId}/input.json`, outputJsonPath: `tasks/${taskCtx.effectId}/result.json` } }; } });
json{ "mcpServers": { "owasp-mobile": { "command": "npx", "args": ["owasp-mobile-security-checker"], "env": { "PROJECT_PATH": "/path/to/mobile/project" } } } }
owasp_scan_ios - Scan iOS project for OWASP vulnerabilitiesowasp_scan_android - Scan Android project for OWASP vulnerabilitiescheck_keychain_usage - Validate iOS Keychain implementationcheck_keystore_usage - Validate Android Keystore implementationvalidate_certificate_pinning - Check certificate pinning configurationaudit_biometric_auth - Audit biometric authentication implementation| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 4,631 | 22,450 | +385% | 1 | 1 | 0% | 272 | 6,501 | +2290% | 0 | 0 | — |
case-02 | pass→pass | 16,480 | 32,044 | +94% | 1 | 1 | 0% | 1,760 | 8,337 | +374% | 0 | 0 | — |
case-03 | fail→pass | 4,391 | 21,469 | +389% | 1 | 1 | 0% | 336 | 6,331 | +1784% | 0 | 0 | — |
case-04 | pass→pass | 8,374 | 6,370 | -24% | 1 | 1 | 0% | 1,494 | 4,020 | +169% | 0 | 0 | — |
case-05 | pass→pass | 8,987 | 3,862 | -57% | 1 | 1 | 0% | 1,774 | 3,422 | +93% | 0 | 0 | — |
case-06 | pass→pass | 6,843 | 3,921 | -43% | 1 | 1 | 0% | 1,113 | 3,412 | +207% | 0 | 0 | — |
case-07 | pass→pass | 5,285 | 3,639 | -31% | 1 | 1 | 0% | 967 | 3,411 | +253% | 0 | 0 | — |
case-08 | pass→pass | 4,711 | 3,717 | -21% | 1 | 1 | 0% | 695 | 3,461 | +398% | 0 | 0 | — |
case-09 | pass→pass | 3,300 | 6,078 | +84% | 1 | 1 | 0% | 575 | 3,625 | +530% | 0 | 0 | — |
case-10 | pass→pass | 3,776 | 4,163 | +10% | 1 | 1 | 0% | 688 | 3,497 | +408% | 0 | 0 | — |
case-11 | pass→pass | 14,716 | 4,647 | -68% | 1 | 1 | 0% | 2,568 | 3,312 | +29% | 0 | 0 | — |
case-12 | fail→pass | 9,427 | 4,901 | -48% | 1 | 1 | 0% | 1,789 | 3,246 | +81% | 0 | 0 | — |
case-13 | fail→pass | 9,797 | 3,510 | -64% | 1 | 1 | 0% | 1,882 | 3,198 | +70% | 0 | 0 | — |
case-14 | fail→pass | 10,774 | 3,478 | -68% | 1 | 1 | 0% | 2,030 | 3,043 | +50% | 0 | 0 | — |
case-15 | fail→pass | 10,841 | 4,890 | -55% | 1 | 1 | 0% | 2,029 | 3,141 | +55% | 0 | 0 | — |
case-16 | pass→pass | 3,620 | 4,505 | +24% | 1 | 1 | 0% | 575 | 3,376 | +487% | 0 | 0 | — |
case-17 | pass→pass | 8,860 | 7,804 | -12% | 1 | 1 | 0% | 1,580 | 4,097 | +159% | 0 | 0 | — |
case-18 | fail→pass | 3,979 | 8,219 | +107% | 1 | 1 | 0% | 716 | 4,123 | +476% | 0 | 0 | — |
case-19 | pass→pass | 5,526 | 4,948 | -10% | 1 | 1 | 0% | 772 | 3,373 | +337% | 0 | 0 | — |
case-20 | pass→pass | 6,031 | 2,430 | -60% | 1 | 1 | 0% | 878 | 3,083 | +251% | 0 | 0 | — |
case-21 | fail→fail | 20,064 | 20,611 | +3% | 1 | 1 | 0% | 3,187 | 6,968 | +119% | 0 | 0 | — |
case-22 | fail→fail | 9,647 | 12,157 | +26% | 1 | 1 | 0% | 2,108 | 5,532 | +162% | 0 | 0 | — |
case-23 | fail→fail | 18,580 | 36,449 | +96% | 1 | 1 | 0% | 2,973 | 6,666 | +124% | 0 | 0 | — |
case-24 | pass→pass | 8,491 | 15,251 | +80% | 1 | 1 | 0% | 1,570 | 5,409 | +245% | 0 | 0 | — |
case-25 | pass→pass | 13,555 | 16,485 | +22% | 1 | 1 | 0% | 2,428 | 5,798 | +139% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted, and 23 counted toward the lift figure. The other 2 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +28 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.