You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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>
Copy file name to clipboardExpand all lines: src/main/kotlin/com/davideagostini/analyzer/tasks/SecurityCheckTask.kt
+66-13Lines changed: 66 additions & 13 deletions
Original file line number
Diff line number
Diff line change
@@ -270,7 +270,7 @@ open class SecurityCheckTask : DefaultTask() {
270
270
SecurityFinding(
271
271
type =SecurityIssueType.MANIFEST_DEBUGGABLE,
272
272
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",
274
274
location ="AndroidManifest.xml (<application>)",
275
275
buildType ="all"
276
276
)
@@ -284,7 +284,7 @@ open class SecurityCheckTask : DefaultTask() {
284
284
SecurityFinding(
285
285
type =SecurityIssueType.ALLOW_BACKUP_ENABLED,
286
286
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",
288
288
location ="AndroidManifest.xml (<application>)",
289
289
buildType ="all"
290
290
)
@@ -375,11 +375,16 @@ open class SecurityCheckTask : DefaultTask() {
375
375
if (content.contains("android.permission.$permission")) {
376
376
// Determine severity: HIGH for high-risk permissions, MEDIUM for others
377
377
val severity =if (permission inHIGH_RISK_PERMISSIONS) Severity.HIGHelseSeverity.MEDIUM
378
+
val fixSuggestion =if (permission inHIGH_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
+
}
378
383
findings.add(
379
384
SecurityFinding(
380
385
type =SecurityIssueType.DANGEROUS_PERMISSION,
381
386
severity = severity,
382
-
message ="Uses dangerous permission: $permission - Review if absolutely necessary",
387
+
message ="Uses dangerous permission: $permission - Review if absolutely necessary$fixSuggestion",
@@ -449,7 +454,7 @@ open class SecurityCheckTask : DefaultTask() {
449
454
SecurityFinding(
450
455
type =SecurityIssueType.EXPORTED_SERVICE,
451
456
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",
453
458
location ="AndroidManifest.xml ($componentName)",
454
459
buildType ="all"
455
460
)
@@ -472,7 +477,7 @@ open class SecurityCheckTask : DefaultTask() {
472
477
SecurityFinding(
473
478
type =SecurityIssueType.EXPORTED_RECEIVER,
474
479
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",
476
481
location ="AndroidManifest.xml ($componentName)",
477
482
buildType ="all"
478
483
)
@@ -496,7 +501,7 @@ open class SecurityCheckTask : DefaultTask() {
496
501
SecurityFinding(
497
502
type =SecurityIssueType.EXPORTED_PROVIDER,
498
503
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",
500
505
location ="AndroidManifest.xml ($componentName)",
501
506
buildType ="all"
502
507
)
@@ -566,11 +571,19 @@ open class SecurityCheckTask : DefaultTask() {
566
571
val hasNetworkSecurityConfig = networkSecurityConfigPattern.containsMatchIn(content)
567
572
568
573
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>"""
569
582
findings.add(
570
583
SecurityFinding(
571
584
type =SecurityIssueType.MISSING_NETWORK_SECURITY_CONFIG,
572
585
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\"",
574
587
location ="AndroidManifest.xml (<application>)",
575
588
buildType ="all"
576
589
)
@@ -585,7 +598,7 @@ open class SecurityCheckTask : DefaultTask() {
585
598
SecurityFinding(
586
599
type =SecurityIssueType.CLEAR_TEXT_HTTP_URL,
587
600
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",
589
602
location ="AndroidManifest.xml (<application>)",
590
603
buildType ="all"
591
604
)
@@ -628,11 +641,12 @@ open class SecurityCheckTask : DefaultTask() {
628
641
val url = match.value
629
642
// Flag insecure HTTP URLs (not HTTPS)
630
643
if (url.startsWith("http://")) {
644
+
val secureUrl = url.replace("http://", "https://")
// 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()
852
905
findings.add(
853
906
SecurityFinding(
854
907
type =SecurityIssueType.MISSING_PROGUARD_RULES,
855
908
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",
0 commit comments