diff --git a/CHANGELOG.md b/CHANGELOG.md index 6838d4d7..7ba6c326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Rule EC533 google ads consent + ### Changed - SPRI004: Add rule to avoid use of Tracking Id using TelephonyManager#getDeviceId() - The embedded Groovy language analyzer was reconfigured to scan only `.gradle` files since it is the files we are interested in for diff --git a/android-plugin/README.md b/android-plugin/README.md index a46b90e8..d580c61a 100644 --- a/android-plugin/README.md +++ b/android-plugin/README.md @@ -51,6 +51,11 @@ cd /codenarc-converter/CodeNarc ./gradlew build -x test ``` +```sh + cd .\codenarc-converter\CodeNarc\ + +``` + Add custom CodeNarc to Maven dependencies: ```sh diff --git a/android-plugin/src/main/java/io/ecocode/java/JavaCheckList.java b/android-plugin/src/main/java/io/ecocode/java/JavaCheckList.java index cc6cd6fb..ac6e6c90 100644 --- a/android-plugin/src/main/java/io/ecocode/java/JavaCheckList.java +++ b/android-plugin/src/main/java/io/ecocode/java/JavaCheckList.java @@ -29,6 +29,7 @@ import io.ecocode.java.checks.environment.power.ChargeAwarenessRule; import io.ecocode.java.checks.environment.power.SaveModeAwarenessRule; import io.ecocode.java.checks.environment.sobriety.*; +import io.ecocode.java.checks.social.gdpr.GoogleAndroidAdsConsentRule; import io.ecocode.java.checks.social.privacy.GoogleTrackerRule; import io.ecocode.java.checks.social.privacy.TrackingIdRule; import org.sonar.plugins.java.api.JavaCheck; @@ -54,7 +55,8 @@ public static List> getChecks() { public static List> getJavaSocialChecks() { return Collections.unmodifiableList(Arrays.asList( GoogleTrackerRule.class, - TrackingIdRule.class + TrackingIdRule.class, + GoogleAndroidAdsConsentRule.class )); } diff --git a/android-plugin/src/main/java/io/ecocode/java/checks/social/gdpr/GoogleAndroidAdsConsentRule.java b/android-plugin/src/main/java/io/ecocode/java/checks/social/gdpr/GoogleAndroidAdsConsentRule.java new file mode 100644 index 00000000..598c4265 --- /dev/null +++ b/android-plugin/src/main/java/io/ecocode/java/checks/social/gdpr/GoogleAndroidAdsConsentRule.java @@ -0,0 +1,69 @@ +package io.ecocode.java.checks.social.gdpr; + +import io.ecocode.java.checks.helpers.TreeHelper; +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.JavaFileScanner; +import org.sonar.plugins.java.api.JavaFileScannerContext; +import org.sonar.plugins.java.api.tree.*; +import org.sonarsource.analyzer.commons.annotations.DeprecatedRuleKey; + +/** + * Check the presence of the import com.google.android.ads.consent + * or com.google.android.ump:user-messaging-platform + *

+ * To support publishers in meeting their duties under the EU User Consent Policy, Google offers a Consent SDK. + * Hence, importing classes from com.google.android.ads.consent or com.google.android.ump:user-messaging-platform is considered as a good practice. + */ +@Rule(key = "EC533", name = "GDPR: Google Consent") +@DeprecatedRuleKey(repositoryKey = "ecoCode-java", ruleKey = "SGDP001") +public class GoogleAndroidAdsConsentRule extends BaseTreeVisitor implements JavaFileScanner { + + private static final String INFO_MESSAGE_GOOGLE_ANDROID_ADS_CONSENT_USED = "Good Smell : User Consent"; + + @Override + public void scanFile(JavaFileScannerContext context) { + CompilationUnitTree cut = context.getTree(); + + GoogleAndroidAdsConsentImports googleTrackerImport = new GoogleAndroidAdsConsentImports(); + + boolean isGoogleAdsConsentImported = false; + for (ImportClauseTree importClauseTree : cut.imports()) { + ImportTree importTree = null; + + if (importClauseTree.is(Tree.Kind.IMPORT)) { + importTree = (ImportTree) importClauseTree; + } + + if (importTree == null) { + continue; + } + + if (googleTrackerImport.verifyImportGoogleAdsConsent(importTree)) { + isGoogleAdsConsentImported = true; + break; + } + } + + if (isGoogleAdsConsentImported) { + context.addIssueOnProject(this, INFO_MESSAGE_GOOGLE_ANDROID_ADS_CONSENT_USED); + } + + scan(cut); + } + + + /** + * Deprecated : com.google.android.ads.consent + *

+ * Android 5.0 (API 21) : com.google.android.ump:user-messaging-platform + */ + private static class GoogleAndroidAdsConsentImports { + private static final String STR_GOOGLE_ADS_CONSENT_IMPORT = "com.google.android.ads.consent"; + private static final String STR_GOOGLE_ADS_UMP_CONSENT_IMPORT = "com.google.android.ump"; + + public boolean verifyImportGoogleAdsConsent(ImportTree importTree) { + String importName = TreeHelper.fullQualifiedName(importTree.qualifiedIdentifier()); + return importName.startsWith(STR_GOOGLE_ADS_CONSENT_IMPORT) || importName.startsWith(STR_GOOGLE_ADS_UMP_CONSENT_IMPORT); + } + } +} diff --git a/android-plugin/src/main/resources/io/ecocode/android/java/ecocode_java_profile.json b/android-plugin/src/main/resources/io/ecocode/android/java/ecocode_java_profile.json index 128c33ad..9e1b91c5 100644 --- a/android-plugin/src/main/resources/io/ecocode/android/java/ecocode_java_profile.json +++ b/android-plugin/src/main/resources/io/ecocode/android/java/ecocode_java_profile.json @@ -34,6 +34,7 @@ "EC530", "EC531", "EC532", + "EC533", "EC534" ] } diff --git a/android-plugin/src/main/resources/io/ecocode/rules/java/EC533.html b/android-plugin/src/main/resources/io/ecocode/rules/java/EC533.html new file mode 100644 index 00000000..1feaa0f8 --- /dev/null +++ b/android-plugin/src/main/resources/io/ecocode/rules/java/EC533.html @@ -0,0 +1,10 @@ + +

+ Good Practice ! + User consent checked. +

+

+ To support publishers in meeting their duties under the EU User Consent Policy, Google offers a Consent SDK. + Hence, importing classes from com.google.android.ads.consent.* or com.google.android.ump.* + since Android API 21 is considered as a good practice. +

diff --git a/android-plugin/src/main/resources/io/ecocode/rules/java/EC533.json b/android-plugin/src/main/resources/io/ecocode/rules/java/EC533.json new file mode 100644 index 00000000..ecd65d10 --- /dev/null +++ b/android-plugin/src/main/resources/io/ecocode/rules/java/EC533.json @@ -0,0 +1,17 @@ +{ + "title": "GDPR: Google Consent", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "0min" + }, + "tags": [ + "gdpr", + "social", + "ecocode", + "android", + "eco-design" + ], + "defaultSeverity": "Info" +} \ No newline at end of file diff --git a/android-plugin/src/test/files/social/gdpr/GoogleAndroidAdsConsentCheck.java b/android-plugin/src/test/files/social/gdpr/GoogleAndroidAdsConsentCheck.java new file mode 100644 index 00000000..75f94ca1 --- /dev/null +++ b/android-plugin/src/test/files/social/gdpr/GoogleAndroidAdsConsentCheck.java @@ -0,0 +1,4 @@ +import com.google.android.ads.consent.Test; + +public class Test { +} \ No newline at end of file diff --git a/android-plugin/src/test/java/io/ecocode/java/JavaRulesDefinitionTest.java b/android-plugin/src/test/java/io/ecocode/java/JavaRulesDefinitionTest.java index 2f169ec9..e90f882e 100644 --- a/android-plugin/src/test/java/io/ecocode/java/JavaRulesDefinitionTest.java +++ b/android-plugin/src/test/java/io/ecocode/java/JavaRulesDefinitionTest.java @@ -58,6 +58,12 @@ private void assertSocialRuleProperties(Repository repository) { assertThat(trackIdRule.name()).isEqualTo("Privacy: Tracking Id"); assertThat(trackIdRule.debtRemediationFunction().type()).isEqualTo(Type.CONSTANT_ISSUE); assertThat(trackIdRule.type()).isEqualTo(RuleType.CODE_SMELL); + + Rule googleConsentRule = repository.rule("EC533"); + assertThat(googleConsentRule).isNotNull(); + assertThat(googleConsentRule.name()).isEqualTo("GDPR: Google Consent"); + assertThat(googleConsentRule.debtRemediationFunction().type()).isEqualTo(Type.CONSTANT_ISSUE); + assertThat(googleConsentRule.type()).isEqualTo(RuleType.CODE_SMELL); } private void assertEnergyRuleProperties(Repository repository) { diff --git a/android-plugin/src/test/java/io/ecocode/java/checks/social/gdpr/GoogleAndroidAdsConsentRuleTest.java b/android-plugin/src/test/java/io/ecocode/java/checks/social/gdpr/GoogleAndroidAdsConsentRuleTest.java new file mode 100644 index 00000000..93a0f517 --- /dev/null +++ b/android-plugin/src/test/java/io/ecocode/java/checks/social/gdpr/GoogleAndroidAdsConsentRuleTest.java @@ -0,0 +1,19 @@ +package io.ecocode.java.checks.social.gdpr; + +import org.junit.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +public class GoogleAndroidAdsConsentRuleTest { + @Test + public void verify() { + + CheckVerifier.newVerifier().onFile("src/test/files/social/privacy/GoogleTrackerGoogleCheck.java") + .withCheck(new GoogleAndroidAdsConsentRule()) + .verifyNoIssues(); + CheckVerifier.newVerifier().onFile("src/test/files/social/gdpr/GoogleAndroidAdsConsentCheck.java") + .withCheck(new GoogleAndroidAdsConsentRule()) + .verifyIssueOnProject("Good Smell : User Consent"); + + + } +} diff --git a/codenarc-converter/CodeNarc/README.md b/codenarc-converter/CodeNarc/README.md index 3876de0d..ad0b924d 100644 --- a/codenarc-converter/CodeNarc/README.md +++ b/codenarc-converter/CodeNarc/README.md @@ -28,7 +28,7 @@ CodeNarc requires ## AVAILABLE FROM MAVEN CENTRAL REPOSITORY -For projects built using Gradle or Maven, **CodeNarc** is available from the [Maven Central Repository]((https://mvnrepository.com/artifact/org.codenarc/CodeNarc)). +For projects built using Gradle or Maven, **CodeNarc** is available from the [Maven Central Repository](https://mvnrepository.com/artifact/org.codenarc/CodeNarc). - groupId = org.codenarc - artifactId = CodeNarc