diff --git a/app/build.gradle b/app/build.gradle index 73b0892762e6..6dfdeba02996 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -215,6 +215,8 @@ fladle { } dependencies { + implementation project(":malicious-site-protection-impl") + implementation project(":malicious-site-protection-api") implementation project(":custom-tabs-impl") implementation project(":custom-tabs-api") implementation project(":duckplayer-impl") diff --git a/malicious-site-protection/malicious-site-protection-api/.gitignore b/malicious-site-protection/malicious-site-protection-api/.gitignore new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/malicious-site-protection/malicious-site-protection-api/build.gradle b/malicious-site-protection/malicious-site-protection-api/build.gradle new file mode 100644 index 000000000000..c552e49e7c43 --- /dev/null +++ b/malicious-site-protection/malicious-site-protection-api/build.gradle @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'com.android.library' + id 'kotlin-android' +} + +apply from: "$rootProject.projectDir/gradle/android-library.gradle" + +android { + namespace 'com.duckduckgo.malicioussiteprotection.api' +} + +dependencies { + implementation Kotlin.stdlib.jdk7 +} diff --git a/malicious-site-protection/malicious-site-protection-api/src/main/kotlin/com/duckduckgo/malicioussiteprotection/api/MaliciousSiteProtection.kt b/malicious-site-protection/malicious-site-protection-api/src/main/kotlin/com/duckduckgo/malicioussiteprotection/api/MaliciousSiteProtection.kt new file mode 100644 index 000000000000..ac25c78c3083 --- /dev/null +++ b/malicious-site-protection/malicious-site-protection-api/src/main/kotlin/com/duckduckgo/malicioussiteprotection/api/MaliciousSiteProtection.kt @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.duckduckgo.malicioussiteprotection.api + +interface MaliciousSiteProtection diff --git a/malicious-site-protection/malicious-site-protection-impl/build.gradle b/malicious-site-protection/malicious-site-protection-impl/build.gradle new file mode 100644 index 000000000000..8e5767047da1 --- /dev/null +++ b/malicious-site-protection/malicious-site-protection-impl/build.gradle @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'com.squareup.anvil' + id 'com.google.devtools.ksp' +} + +apply from: "$rootProject.projectDir/gradle/android-library.gradle" + +dependencies { + implementation project(":malicious-site-protection-api") + + anvil project(path: ':anvil-compiler') + implementation project(path: ':anvil-annotations') + implementation project(path: ':di') + ksp AndroidX.room.compiler + + implementation KotlinX.coroutines.android + implementation AndroidX.core.ktx + implementation Google.dagger + + implementation project(path: ':common-utils') + + implementation "com.squareup.logcat:logcat:_" + implementation JakeWharton.timber + + implementation Google.android.material + + testImplementation Testing.junit4 + testImplementation "org.mockito.kotlin:mockito-kotlin:_" + testImplementation project(path: ':common-test') + testImplementation CashApp.turbine + testImplementation Testing.robolectric + testImplementation(KotlinX.coroutines.test) { + // https://github.com/Kotlin/kotlinx.coroutines/issues/2023 + // conflicts with mockito due to direct inclusion of byte buddy + exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug" + } + + coreLibraryDesugaring Android.tools.desugarJdkLibs +} + +android { + namespace "com.duckduckgo.malicioussiteprotection.impl" + anvil { + generateDaggerFactories = true // default is false + } + lint { + baseline file("lint-baseline.xml") + } + testOptions { + unitTests { + includeAndroidResources = true + } + } + compileOptions { + coreLibraryDesugaringEnabled = true + } +} + diff --git a/malicious-site-protection/malicious-site-protection-impl/lint-baseline.xml b/malicious-site-protection/malicious-site-protection-impl/lint-baseline.xml new file mode 100644 index 000000000000..c584e1295716 --- /dev/null +++ b/malicious-site-protection/malicious-site-protection-impl/lint-baseline.xml @@ -0,0 +1,4 @@ + + + + diff --git a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/MaliciousSiteProtectionFeature.kt b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/MaliciousSiteProtectionFeature.kt new file mode 100644 index 000000000000..bf3b6892c6cf --- /dev/null +++ b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/MaliciousSiteProtectionFeature.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.duckduckgo.malicioussiteprotection.impl + +import com.duckduckgo.anvil.annotations.ContributesRemoteFeature +import com.duckduckgo.di.scopes.AppScope +import com.duckduckgo.feature.toggles.api.Toggle + +@ContributesRemoteFeature( + scope = AppScope::class, + featureName = "maliciousSiteProtection", +) +/** + * This is the class that represents the maliciousSiteProtection feature flags + */ +interface MaliciousSiteProtectionFeature { + /** + * @return `true` when the remote config has the global "maliciousSiteProtection" feature flag enabled + * If the remote feature is not present defaults to `false` + */ + @Toggle.InternalAlwaysEnabled + @Toggle.DefaultValue(false) + fun self(): Toggle +} diff --git a/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/RealMaliciousSiteProtection.kt b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/RealMaliciousSiteProtection.kt new file mode 100644 index 000000000000..3013d6862251 --- /dev/null +++ b/malicious-site-protection/malicious-site-protection-impl/src/main/kotlin/com/duckduckgo/malicioussiteprotection/impl/RealMaliciousSiteProtection.kt @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.duckduckgo.malicioussiteprotection.impl + +import com.duckduckgo.app.di.AppCoroutineScope +import com.duckduckgo.app.di.IsMainProcess +import com.duckduckgo.common.utils.DispatcherProvider +import com.duckduckgo.di.scopes.AppScope +import com.duckduckgo.malicioussiteprotection.api.MaliciousSiteProtection +import com.duckduckgo.privacy.config.api.PrivacyConfigCallbackPlugin +import com.squareup.anvil.annotations.ContributesBinding +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import org.json.JSONObject + +@ContributesBinding(AppScope::class, MaliciousSiteProtection::class) +@ContributesBinding(AppScope::class, PrivacyConfigCallbackPlugin::class) +class RealMaliciousSiteProtection @Inject constructor( + private val dispatchers: DispatcherProvider, + private val maliciousSiteProtectionFeature: MaliciousSiteProtectionFeature, + @IsMainProcess private val isMainProcess: Boolean, + @AppCoroutineScope private val appCoroutineScope: CoroutineScope, +) : MaliciousSiteProtection, PrivacyConfigCallbackPlugin { + + private var isFeatureEnabled = false + private var hashPrefixUpdateFrequency = 20L + private var filterSetUpdateFrequency = 720L + + init { + if (isMainProcess) { + loadToMemory() + } + } + + override fun onPrivacyConfigDownloaded() { + loadToMemory() + } + + private fun loadToMemory() { + appCoroutineScope.launch(dispatchers.io()) { + isFeatureEnabled = maliciousSiteProtectionFeature.self().isEnabled() + maliciousSiteProtectionFeature.self().getSettings()?.let { + JSONObject(it).let { settings -> + hashPrefixUpdateFrequency = settings.getLong("hashPrefixUpdateFrequency") + filterSetUpdateFrequency = settings.getLong("filterSetUpdateFrequency") + } + } + } + } +} diff --git a/malicious-site-protection/readme.md b/malicious-site-protection/readme.md new file mode 100644 index 000000000000..6fdea2dd377c --- /dev/null +++ b/malicious-site-protection/readme.md @@ -0,0 +1,9 @@ +# Feature Name + +In-browser feature to detect phishing and malware sites. + +## Who can help you better understand this feature? +- ❓ Cris Barreiro + +## More information +- [Asana: feature documentation](https://app.asana.com/0/1208717418466383/1199621914667995/f) \ No newline at end of file