Skip to content

Commit b07f038

Browse files
Add actionable fix suggestions to security findings
- Add suggested fixes to MISSING_LIBRARY_RULES with exact ProGuard rules - Add suggested fixes to NO_KEEP_CLASS_MEMBERS with example rules - Add suggested fixes to MISSING_PROGUARD_RULES with template - Add suggested fixes to MISSING_NETWORK_SECURITY_CONFIG with XML config - Add suggested fixes to INSECURE_HTTP_URL with HTTPS replacement - Add suggested fixes to EXPORTED_SERVICE/RECEIVER/PROVIDER - Add suggested fixes to ALLOW_BACKUP_ENABLED - Add suggested fixes to MANIFEST_DEBUGGABLE - Add guidance for DANGEROUS_PERMISSION based on risk level Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5fb0a34 commit b07f038

1 file changed

Lines changed: 66 additions & 13 deletions

File tree

src/main/kotlin/com/davideagostini/analyzer/tasks/SecurityCheckTask.kt

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ open class SecurityCheckTask : DefaultTask() {
270270
SecurityFinding(
271271
type = SecurityIssueType.MANIFEST_DEBUGGABLE,
272272
severity = Severity.HIGH,
273-
message = "Manifest has android:debuggable=\"true\" which allows debugging the app.",
273+
message = "Manifest has android:debuggable=\"true\" which allows debugging the app.\nSuggested fix:\nRemove android:debuggable attribute or set to \"false\" in AndroidManifest.xml",
274274
location = "AndroidManifest.xml (<application>)",
275275
buildType = "all"
276276
)
@@ -284,7 +284,7 @@ open class SecurityCheckTask : DefaultTask() {
284284
SecurityFinding(
285285
type = SecurityIssueType.ALLOW_BACKUP_ENABLED,
286286
severity = Severity.MEDIUM,
287-
message = "Manifest has android:allowBackup=\"true\" which allows app data backup.",
287+
message = "Manifest has android:allowBackup=\"true\" which allows app data backup.\nSuggested fix:\nSet android:allowBackup=\"false\" in AndroidManifest.xml or use android:fullBackupContent=\"@xml/backup_rules\" to control what gets backed up",
288288
location = "AndroidManifest.xml (<application>)",
289289
buildType = "all"
290290
)
@@ -375,11 +375,16 @@ open class SecurityCheckTask : DefaultTask() {
375375
if (content.contains("android.permission.$permission")) {
376376
// Determine severity: HIGH for high-risk permissions, MEDIUM for others
377377
val severity = if (permission in HIGH_RISK_PERMISSIONS) Severity.HIGH else Severity.MEDIUM
378+
val fixSuggestion = if (permission in HIGH_RISK_PERMISSIONS) {
379+
"\nThis is a high-risk permission. Ensure you have a legitimate need and proper user consent flow."
380+
} else {
381+
"\nConsider if this permission is absolutely necessary. Request at runtime on Android 6.0+."
382+
}
378383
findings.add(
379384
SecurityFinding(
380385
type = SecurityIssueType.DANGEROUS_PERMISSION,
381386
severity = severity,
382-
message = "Uses dangerous permission: $permission - Review if absolutely necessary",
387+
message = "Uses dangerous permission: $permission - Review if absolutely necessary$fixSuggestion",
383388
location = "AndroidManifest.xml (uses-permission)",
384389
buildType = "all"
385390
)
@@ -449,7 +454,7 @@ open class SecurityCheckTask : DefaultTask() {
449454
SecurityFinding(
450455
type = SecurityIssueType.EXPORTED_SERVICE,
451456
severity = Severity.MEDIUM,
452-
message = "Exported service '$componentName' has no permission protection",
457+
message = "Exported service '$componentName' has no permission protection\nSuggested fix:\nAdd android:permission=\"your.package.permission.NAME\" or set android:exported=\"false\" if not needed",
453458
location = "AndroidManifest.xml ($componentName)",
454459
buildType = "all"
455460
)
@@ -472,7 +477,7 @@ open class SecurityCheckTask : DefaultTask() {
472477
SecurityFinding(
473478
type = SecurityIssueType.EXPORTED_RECEIVER,
474479
severity = Severity.MEDIUM,
475-
message = "Exported broadcast receiver '$componentName' has no permission protection",
480+
message = "Exported broadcast receiver '$componentName' has no permission protection\nSuggested fix:\nAdd android:permission=\"your.package.permission.NAME\" or set android:exported=\"false\" if not needed",
476481
location = "AndroidManifest.xml ($componentName)",
477482
buildType = "all"
478483
)
@@ -496,7 +501,7 @@ open class SecurityCheckTask : DefaultTask() {
496501
SecurityFinding(
497502
type = SecurityIssueType.EXPORTED_PROVIDER,
498503
severity = Severity.HIGH,
499-
message = "Exported content provider '$componentName' has no permission protection",
504+
message = "Exported content provider '$componentName' has no permission protection\nSuggested fix:\nAdd android:permission=\"your.package.permission.NAME\" or android:grantUriPermissions=\"true\" or set android:exported=\"false\" if not needed",
500505
location = "AndroidManifest.xml ($componentName)",
501506
buildType = "all"
502507
)
@@ -566,11 +571,19 @@ open class SecurityCheckTask : DefaultTask() {
566571
val hasNetworkSecurityConfig = networkSecurityConfigPattern.containsMatchIn(content)
567572

568573
if (!hasNetworkSecurityConfig) {
574+
val suggestedNetworkSecurityConfig = """<?xml version="1.0" encoding="utf-8"?>
575+
<network-security-config>
576+
<base-config cleartextTrafficPermitted="false">
577+
<trust-anchors>
578+
<certificates src="system" />
579+
</trust-anchors>
580+
</base-config>
581+
</network-security-config>"""
569582
findings.add(
570583
SecurityFinding(
571584
type = SecurityIssueType.MISSING_NETWORK_SECURITY_CONFIG,
572585
severity = Severity.MEDIUM,
573-
message = "Missing Network Security Config - consider adding one to enforce HTTPS",
586+
message = "Missing Network Security Config - consider adding one to enforce HTTPS\nSuggested fix:\n1. Create res/xml/network_security_config.xml:\n$suggestedNetworkSecurityConfig\n\n2. Add to AndroidManifest.xml:\nandroid:networkSecurityConfig=\"@xml/network_security_config\"",
574587
location = "AndroidManifest.xml (<application>)",
575588
buildType = "all"
576589
)
@@ -585,7 +598,7 @@ open class SecurityCheckTask : DefaultTask() {
585598
SecurityFinding(
586599
type = SecurityIssueType.CLEAR_TEXT_HTTP_URL,
587600
severity = Severity.MEDIUM,
588-
message = "Cleartext traffic (HTTP) is allowed - this can be intercepted",
601+
message = "Cleartext traffic (HTTP) is allowed - this can be intercepted\nSuggested fix:\nSet android:usesCleartextTraffic=\"false\" in AndroidManifest.xml or create a network security config to enforce HTTPS",
589602
location = "AndroidManifest.xml (<application>)",
590603
buildType = "all"
591604
)
@@ -628,11 +641,12 @@ open class SecurityCheckTask : DefaultTask() {
628641
val url = match.value
629642
// Flag insecure HTTP URLs (not HTTPS)
630643
if (url.startsWith("http://")) {
644+
val secureUrl = url.replace("http://", "https://")
631645
findings.add(
632646
SecurityFinding(
633647
type = SecurityIssueType.INSECURE_HTTP_URL,
634648
severity = Severity.MEDIUM,
635-
message = "Insecure HTTP URL found: $url",
649+
message = "Insecure HTTP URL found: $url\nSuggested fix: Replace with HTTPS URL:\n$secureUrl",
636650
location = "${file.relativeTo(project.rootDir)}",
637651
buildType = "all"
638652
)
@@ -814,13 +828,23 @@ open class SecurityCheckTask : DefaultTask() {
814828
val proguardRulePattern = Regex("""^\s*-(dontwarn|keep|warn)\s+.*\b$library\b""", RegexOption.MULTILINE)
815829
val hasRule = proguardRulePattern.containsMatchIn(rulesContent)
816830

831+
// Get suggested rules for this library
832+
val suggestedRule = when (library) {
833+
"okhttp" -> "-dontwarn okhttp3.**\n-dontwarn okio.**"
834+
"retrofit" -> "-dontwarn retrofit2.**\n-keepattributes Signature"
835+
"gson" -> "-keepattributes Signature\n-keep class com.google.gson.** { *; }"
836+
"rxjava" -> "-dontwarn rxjava.**\n-dontwarn rxplugins.**"
837+
"commons-io" -> "-dontwarn org.apache.commons.io.**"
838+
else -> ""
839+
}
840+
817841
// If library is used but no proper rule exists, add a finding
818842
if (hasLibrary && !hasRule) {
819843
findings.add(
820844
SecurityFinding(
821845
type = SecurityIssueType.MISSING_LIBRARY_RULES,
822846
severity = Severity.LOW,
823-
message = "Library '$library' may need additional rules",
847+
message = "Library '$library' may need additional rules\nSuggested fix:\n$suggestedRule",
824848
location = rulesFile.relativeTo(project.rootDir).path,
825849
buildType = "release"
826850
)
@@ -837,23 +861,52 @@ open class SecurityCheckTask : DefaultTask() {
837861
val hasKeepClassMembers = keepClassMembersPattern.containsMatchIn(rulesContent)
838862

839863
if (!hasKeepClassMembers) {
864+
val suggestedKeepClassMembers = """
865+
# Keep class members (for model classes with serialization)
866+
-keepclassmembers class * {
867+
@com.google.gson.annotations.SerializedName <fields>;
868+
}
869+
-keepclassmembers class com.example.yourpackage.model.** {
870+
*;
871+
}
872+
""".trimIndent()
840873
findings.add(
841874
SecurityFinding(
842875
type = SecurityIssueType.NO_KEEP_CLASS_MEMBERS,
843876
severity = Severity.LOW,
844-
message = "No -keepclassmembers rules found - consider adding for model classes",
877+
message = "No -keepclassmembers rules found - consider adding for model classes\nSuggested fix:\n$suggestedKeepClassMembers",
845878
location = rulesFile.relativeTo(project.rootDir).path,
846879
buildType = "release"
847880
)
848881
)
849882
}
850883
} else {
851-
// No ProGuard rules file found
884+
// No ProGuard rules file found - suggest basic template
885+
val suggestedProGuardRules = """
886+
# Android ProGuard Rules
887+
-keepattributes SourceFile,LineNumberTable
888+
-renamesourcefileattribute SourceFile
889+
890+
# Keep application class
891+
-keep class your.app.package.MyApplication { *; }
892+
893+
# Keep model classes
894+
-keep class your.app.package.model.** { *; }
895+
896+
# OkHttp
897+
-dontwarn okhttp3.**
898+
-dontwarn okio.**
899+
900+
# Gson
901+
-keepattributes Signature
902+
-keepattributes *Annotation*
903+
-keep class com.google.gson.** { *; }
904+
""".trimIndent()
852905
findings.add(
853906
SecurityFinding(
854907
type = SecurityIssueType.MISSING_PROGUARD_RULES,
855908
severity = Severity.MEDIUM,
856-
message = "ProGuard rules file not found - create one for better obfuscation",
909+
message = "ProGuard rules file not found - create one for better obfuscation\nSuggested fix:\nCreate proguard-rules.pro with:\n$suggestedProGuardRules",
857910
location = "proguard-rules.pro",
858911
buildType = "release"
859912
)

0 commit comments

Comments
 (0)