Static Analysis Tools
Static Analysis Tools
Static analysis tools examine source code, bytecode, or binary code to identify security vulnerabilities without executing the application.
Popular SAST Tools for Mobile:
// iOS - Integrating SwiftLint for security checks
// .swiftlint.yml configuration
let swiftlintConfig = """
included:
- Sources
- Tests
excluded:
- Carthage
- Pods
opt_in_rules:
- force_unwrapping
- implicitly_unwrapped_optional
- weak_delegate
- private_outlet
- prohibited_super_call
custom_rules:
hardcoded_secret:
name: "Hardcoded Secret"
regex: '(api_key|apikey|password|secret|token)\s*=\s*"[^"]+"'
message: "Hardcoded secrets should not be committed"
severity: error
insecure_random:
name: "Insecure Random"
regex: 'arc4random\(\)|rand\(\)|random\(\)'
message: "Use SecRandomCopyBytes for cryptographic randomness"
severity: warning
disabled_ats:
name: "Disabled ATS"
regex: 'NSAllowsArbitraryLoads\s*</key>\s*<true/>'
message: "App Transport Security should not be disabled"
severity: error
"""
// Custom security analyzer
class SecurityAnalyzer {
func analyzeProject(at path: String) -> AnalysisReport {
var vulnerabilities: [Vulnerability] = []
// Analyze Info.plist for security issues
vulnerabilities.append(contentsOf: analyzeInfoPlist(at: path))
// Analyze source code
vulnerabilities.append(contentsOf: analyzeSourceCode(at: path))
// Analyze project configuration
vulnerabilities.append(contentsOf: analyzeProjectConfig(at: path))
return AnalysisReport(
projectPath: path,
vulnerabilities: vulnerabilities,
timestamp: Date()
)
}
private func analyzeInfoPlist(at path: String) -> [Vulnerability] {
var vulnerabilities: [Vulnerability] = []
let plistPath = "\(path)/Info.plist"
guard let plistData = try? Data(contentsOf: URL(fileURLWithPath: plistPath)),
let plist = try? PropertyListSerialization.propertyList(from: plistData, format: nil) as? [String: Any] else {
return vulnerabilities
}
// Check ATS configuration
if let ats = plist["NSAppTransportSecurity"] as? [String: Any] {
if ats["NSAllowsArbitraryLoads"] as? Bool == true {
vulnerabilities.append(Vulnerability(
type: .configuration,
severity: .high,
description: "App Transport Security is disabled",
file: plistPath,
recommendation: "Enable ATS and use exception domains for specific requirements"
))
}
}
// Check privacy permissions
let privacyKeys = [
"NSCameraUsageDescription",
"NSMicrophoneUsageDescription",
"NSLocationWhenInUseUsageDescription"
]
for key in privacyKeys {
if plist[key] == nil {
vulnerabilities.append(Vulnerability(
type: .configuration,
severity: .medium,
description: "Missing privacy usage description for \(key)",
file: plistPath,
recommendation: "Add appropriate usage description for \(key)"
))
}
}
return vulnerabilities
}
}
Android Static Analysis Integration:
// Android - Custom Lint checks for security
package com.example.security.lint
import com.android.tools.lint.detector.api.*
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression
class SecurityLintRegistry : IssueRegistry() {
override val issues = listOf(
HardcodedSecretDetector.ISSUE,
InsecureRandomDetector.ISSUE,
WeakCryptoDetector.ISSUE,
InsecureStorageDetector.ISSUE
)
override val api = CURRENT_API
}
class HardcodedSecretDetector : Detector(), SourceCodeScanner {
companion object {
val ISSUE = Issue.create(
id = "HardcodedSecret",
briefDescription = "Hardcoded secret detected",
explanation = "Hardcoded secrets in source code can be extracted from the APK",
category = Category.SECURITY,
priority = 9,
severity = Severity.ERROR,
implementation = Implementation(
HardcodedSecretDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
}
override fun getApplicableMethodNames() = listOf(
"putString",
"getString",
"setText"
)
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
val arguments = node.valueArguments
for (arg in arguments) {
val value = arg.evaluate()?.toString() ?: continue
if (looksLikeSecret(value)) {
context.report(
ISSUE,
node,
context.getLocation(arg),
"Potential hardcoded secret: ${value.take(10)}..."
)
}
}
}
private fun looksLikeSecret(value: String): Boolean {
val patterns = listOf(
Regex(".*[Kk]ey.*=.*"),
Regex(".*[Pp]assword.*=.*"),
Regex(".*[Tt]oken.*=.*"),
Regex(".*[Ss]ecret.*=.*"),
Regex("[a-zA-Z0-9]{32,}") // Long random strings
)
return patterns.any { it.matches(value) }
}
}
// Gradle integration
class SecurityGradlePlugin : Plugin<Project> {
override fun apply(project: Project) {
project.dependencies {
add("lintChecks", project(":security-lint"))
}
project.android {
lintOptions {
isAbortOnError = true
isWarningsAsErrors = true
enable("HardcodedSecret")
enable("InsecureRandom")
enable("WeakCrypto")
// Custom lint report
htmlOutput = File("${project.buildDir}/reports/lint-security.html")
}
}
// Add security check task
project.tasks.register("securityCheck") {
dependsOn("lint")
doLast {
println("Running security checks...")
runAdditionalSecurityChecks(project)
}
}
}
}