Setting Up a Mobile Testing Environment

Setting Up a Mobile Testing Environment

Creating a comprehensive testing environment is essential for thorough security assessment.

iOS Testing Environment Setup:

// iOS - Security testing helper class
import Foundation
import Network

class SecurityTestingHelper {
    
    // Network monitoring for security testing
    class NetworkMonitor {
        private let monitor = NWPathMonitor()
        private let queue = DispatchQueue(label: "SecurityTesting")
        
        func startMonitoring() {
            monitor.pathUpdateHandler = { path in
                self.logNetworkActivity(path)
                self.detectProxyUsage(path)
            }
            
            monitor.start(queue: queue)
        }
        
        private func detectProxyUsage(_ path: NWPath) {
            // Check for proxy configuration (useful for testing)
            if let proxySettings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() as? [String: Any] {
                if let httpProxy = proxySettings[kCFNetworkProxiesHTTPProxy as String] as? String,
                   !httpProxy.isEmpty {
                    print("HTTP Proxy detected: \(httpProxy)")
                }
                
                if let httpsProxy = proxySettings[kCFNetworkProxiesHTTPSProxy as String] as? String,
                   !httpsProxy.isEmpty {
                    print("HTTPS Proxy detected: \(httpsProxy)")
                }
            }
        }
    }
    
    // Certificate validation testing
    func testCertificatePinning(for host: String) -> Bool {
        var result = false
        let semaphore = DispatchSemaphore(value: 0)
        
        let url = URL(string: "https://\(host)")!
        let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
        
        let task = session.dataTask(with: url) { _, _, error in
            result = error == nil
            semaphore.signal()
        }
        
        task.resume()
        semaphore.wait()
        
        return result
    }
    
    // Test data protection levels
    func testDataProtection() -> [String: Bool] {
        var results: [String: Bool] = [:]
        
        let testData = "Sensitive test data".data(using: .utf8)!
        let documentsPath = FileManager.default.urls(for: .documentDirectory, 
                                                     in: .userDomainMask).first!
        
        // Test different protection levels
        let protectionLevels: [(String, FileProtectionType)] = [
            ("Complete", .complete),
            ("CompleteUnlessOpen", .completeUnlessOpen),
            ("CompleteUntilFirstUserAuthentication", .completeUntilFirstUserAuthentication),
            ("None", .none)
        ]
        
        for (name, protection) in protectionLevels {
            let fileURL = documentsPath.appendingPathComponent("test_\(name).dat")
            
            do {
                try testData.write(to: fileURL)
                try FileManager.default.setAttributes(
                    [.protectionKey: protection],
                    ofItemAtPath: fileURL.path
                )
                
                // Verify protection level
                let attributes = try FileManager.default.attributesOfItem(atPath: fileURL.path)
                let actualProtection = attributes[.protectionKey] as? FileProtectionType
                
                results[name] = actualProtection == protection
                
                // Clean up
                try FileManager.default.removeItem(at: fileURL)
            } catch {
                results[name] = false
            }
        }
        
        return results
    }
}

Android Testing Environment:

// Android - Security testing utilities
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import java.security.MessageDigest
import java.security.cert.Certificate
import java.util.concurrent.TimeUnit
import okhttp3.*

class SecurityTestingUtils(private val context: Context) {
    
    // Test certificate pinning implementation
    fun testCertificatePinning(url: String, expectedPins: List<String>): TestResult {
        val client = OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .build()
        
        val request = Request.Builder()
            .url(url)
            .build()
        
        return try {
            val response = client.newCall(request).execute()
            val certificates = response.handshake?.peerCertificates
            
            val actualPins = certificates?.map { cert ->
                val publicKey = cert.publicKey
                val publicKeyBytes = publicKey.encoded
                val digest = MessageDigest.getInstance("SHA-256").digest(publicKeyBytes)
                "sha256/" + android.util.Base64.encodeToString(digest, android.util.Base64.NO_WRAP)
            } ?: emptyList()
            
            val pinningValid = actualPins.any { it in expectedPins }
            
            TestResult(
                passed = pinningValid,
                message = if (pinningValid) "Certificate pinning valid" else "Certificate pinning failed",
                details = mapOf(
                    "expected_pins" to expectedPins,
                    "actual_pins" to actualPins
                )
            )
        } catch (e: Exception) {
            TestResult(
                passed = false,
                message = "Connection failed: ${e.message}",
                details = mapOf("error" to e.toString())
            )
        }
    }
    
    // Test for insecure data storage
    fun testDataStorage(): List<TestResult> {
        val results = mutableListOf<TestResult>()
        
        // Check SharedPreferences encryption
        results.add(testSharedPreferencesEncryption())
        
        // Check database encryption
        results.add(testDatabaseEncryption())
        
        // Check external storage usage
        results.add(testExternalStorageUsage())
        
        // Check for sensitive data in logs
        results.add(testLogLeakage())
        
        return results
    }
    
    private fun testSharedPreferencesEncryption(): TestResult {
        val prefs = context.getSharedPreferences("test_prefs", Context.MODE_PRIVATE)
        val testKey = "test_sensitive_data"
        val testValue = "sensitive_value_12345"
        
        // Store test data
        prefs.edit().putString(testKey, testValue).apply()
        
        // Check if data is encrypted on disk
        val prefsFile = context.filesDir.parentFile?.resolve("shared_prefs/test_prefs.xml")
        val fileContent = prefsFile?.readText() ?: ""
        
        val isEncrypted = !fileContent.contains(testValue)
        
        // Clean up
        prefs.edit().remove(testKey).apply()
        
        return TestResult(
            passed = isEncrypted,
            message = if (isEncrypted) "SharedPreferences appear encrypted" else "SharedPreferences not encrypted",
            details = mapOf("file_path" to prefsFile?.absolutePath)
        )
    }
    
    // Test for debugging and development artifacts
    fun testDebuggingArtifacts(): List<TestResult> {
        val results = mutableListOf<TestResult>()
        
        // Check if debuggable
        val isDebuggable = context.applicationInfo.flags and 
                          android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE != 0
        
        results.add(TestResult(
            passed = !isDebuggable,
            message = if (!isDebuggable) "App is not debuggable" else "App is debuggable",
            details = mapOf("debuggable" to isDebuggable)
        ))
        
        // Check for backup enabled
        val allowBackup = context.applicationInfo.flags and 
                         android.content.pm.ApplicationInfo.FLAG_ALLOW_BACKUP != 0
        
        results.add(TestResult(
            passed = !allowBackup,
            message = if (!allowBackup) "Backup is disabled" else "Backup is enabled",
            details = mapOf("allow_backup" to allowBackup)
        ))
        
        // Check for test keys
        val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_SIGNING_CERTIFICATES)
                .signingInfo.apkContentsSigners
        } else {
            @Suppress("DEPRECATION")
            context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES)
                .signatures
        }
        
        val hasTestKeys = signatures.any { signature ->
            signature.toCharsString().contains("CN=Android Debug")
        }
        
        results.add(TestResult(
            passed = !hasTestKeys,
            message = if (!hasTestKeys) "Production keys used" else "Debug keys detected",
            details = mapOf("has_test_keys" to hasTestKeys)
        ))
        
        return results
    }
    
    data class TestResult(
        val passed: Boolean,
        val message: String,
        val details: Map<String, Any?> = emptyMap()
    )
}