Skip to content

Commit 3176595

Browse files
authored
Infrastructure to expand 3rd-Party Tracker Loading Protection (duckduckgo#2143)
Task/Issue URL: https://app.asana.com/0/1179596784783755/1202464473778543/f Description This PR adds the infrastructure we need to expand our 3rd-Party Tracker Loading Protection to include Microsoft, such that Microsoft scripts on our list are blocked from loading except for requests from one domain (bat.bing.com) loading on some advertiser websites following DuckDuckGo ad clicks. For more information, please see https://help.duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/#3rd-party-tracker-loading-protection
1 parent eec2359 commit 3176595

File tree

159 files changed

+6973
-1334
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

159 files changed

+6973
-1334
lines changed

ad-click/ad-click-api/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

ad-click/ad-click-api/build.gradle

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (c) 2022 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
plugins {
18+
id 'java-library'
19+
id 'kotlin'
20+
}
21+
22+
apply from: "$rootProject.projectDir/spotless.gradle"
23+
24+
java {
25+
sourceCompatibility = JavaVersion.VERSION_1_8
26+
targetCompatibility = JavaVersion.VERSION_1_8
27+
}
28+
29+
dependencies {
30+
implementation Kotlin.stdlib.jdk7
31+
implementation Google.dagger
32+
implementation KotlinX.coroutines.core
33+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (c) 2022 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.adclick.api
18+
19+
enum class AdClickFeatureName(val value: String) {
20+
AdClickAttributionFeatureName("adClickAttribution"),
21+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (c) 2022 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.adclick.api
18+
19+
/** Public interface for the Ad Click feature which helps to measure ad conversions only when they are required */
20+
interface AdClickManager {
21+
22+
/**
23+
* Sets the active tab. It takes as parameters:
24+
* mandatory [tabId] - The id of the current visible tab.
25+
* optional [url] - The url loaded in the current tab, null if no url was loaded (empty tab).
26+
* optional [sourceTabId] - The id of the tab from which the current tab was opened, null if a new tab option was used.
27+
* optional [sourceTabUrl] - The url loaded in the tab from which the current tab was opened, null if a new tab option was used.
28+
*/
29+
fun setActiveTabId(tabId: String, url: String? = null, sourceTabId: String? = null, sourceTabUrl: String? = null)
30+
31+
/**
32+
* Detects and registers the eTLD+1 if an ad link was clicked. It takes as parameters:
33+
* optional [url] - The requested url, null if no url was requested.
34+
* mandatory [isMainFrame] - True if the request is for mainframe, false otherwise.
35+
*/
36+
fun detectAdClick(url: String?, isMainFrame: Boolean)
37+
38+
/**
39+
* Detects and saves in memory the ad eTLD+1 domain from the url. It takes as parameters:
40+
* mandatory [url] - The requested url.
41+
*/
42+
fun detectAdDomain(url: String)
43+
44+
/**
45+
* Removes any data kept in memory for the specified tab. It takes as parameters:
46+
* mandatory [tabId] - The id of the active tab.
47+
*/
48+
fun clearTabId(tabId: String)
49+
50+
/**
51+
* Removes any data related to ad management that is kept in memory.
52+
*/
53+
fun clearAll()
54+
55+
/**
56+
* Removes any data related to ad management that is kept in memory. This is used asynchronously.
57+
*/
58+
fun clearAllExpiredAsync()
59+
60+
/**
61+
* Detects if there is an existing exemption based on the document url and the url requested. It takes as parameters:
62+
* mandatory [documentUrl] - The initially requested url, potentially leading to the advertiser page.
63+
* mandatory [url] - The requested url, potentially a tracker used for ad attribution.
64+
* @return `true` if there is an existing exemption for this combination of [documentUrl] and [url], false otherwise.
65+
*/
66+
fun isExemption(documentUrl: String, url: String): Boolean
67+
}

ad-click/ad-click-impl/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright (c) 2022 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
plugins {
18+
id 'com.android.library'
19+
id 'kotlin-android'
20+
id 'com.squareup.anvil'
21+
id 'kotlin-android-extensions'
22+
}
23+
24+
apply from: "$rootProject.projectDir/gradle/android-library.gradle"
25+
26+
android {
27+
anvil {
28+
generateDaggerFactories = true // default is false
29+
}
30+
namespace 'com.duckduckgo.adclick.impl'
31+
}
32+
33+
dependencies {
34+
anvil project(path: ':anvil-compiler')
35+
implementation project(path: ':anvil-annotations')
36+
implementation project(path: ':ad-click-api')
37+
implementation project(path: ':di')
38+
implementation project(path: ':common')
39+
implementation project(path: ':statistics')
40+
implementation project(path: ':privacy-config-api')
41+
implementation project(path: ':feature-toggles-api')
42+
implementation project(path: ':app-build-config-api')
43+
implementation project(path: ':ad-click-store')
44+
45+
implementation Kotlin.stdlib.jdk7
46+
47+
implementation AndroidX.core.ktx
48+
49+
implementation AndroidX.appCompat
50+
implementation Google.android.material
51+
implementation AndroidX.constraintLayout
52+
53+
implementation AndroidX.lifecycle.viewModelKtx
54+
implementation AndroidX.lifecycle.runtimeKtx
55+
implementation AndroidX.lifecycle.commonJava8
56+
implementation AndroidX.lifecycle.liveDataKtx
57+
58+
// WorkManager
59+
implementation AndroidX.work.runtimeKtx
60+
testImplementation AndroidX.work.testing
61+
implementation AndroidX.work.rxJava2
62+
63+
// Room
64+
implementation AndroidX.room.runtime
65+
implementation AndroidX.room.rxJava2
66+
implementation AndroidX.room.ktx
67+
testImplementation AndroidX.room.testing
68+
69+
// Dagger
70+
implementation Google.dagger
71+
72+
implementation KotlinX.coroutines.core
73+
74+
testImplementation(KotlinX.coroutines.test) {
75+
// https://github.com/Kotlin/kotlinx.coroutines/issues/2023
76+
// conflicts with mockito due to direct inclusion of byte buddy
77+
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug"
78+
}
79+
80+
implementation "com.jakewharton.threetenabp:threetenabp:_"
81+
testImplementation "org.threeten:threetenbp:_"
82+
83+
implementation JakeWharton.timber
84+
85+
implementation Square.retrofit2.converter.moshi
86+
implementation Square.okHttp3.okHttp
87+
88+
// Testing dependencies
89+
testImplementation "org.mockito.kotlin:mockito-kotlin:_"
90+
testImplementation Testing.junit4
91+
testImplementation AndroidX.test.ext.junit
92+
testImplementation AndroidX.archCore.testing
93+
testImplementation 'app.cash.turbine:turbine:_'
94+
testImplementation Testing.robolectric
95+
96+
testImplementation project(path: ':common-test')
97+
}
98+
99+
fulladleModuleConfig {
100+
maxTestShards.set(1)
101+
}
102+
103+
tasks.register('androidTestsBuild') {
104+
dependsOn 'assembleDebugAndroidTest'
105+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright (c) 2022 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.adclick.impl
18+
19+
data class AdClickAttributionFeature(
20+
val state: String,
21+
val settings: AdClickAttributionSettings,
22+
val exceptions: List<AdClickAttributionException>
23+
)
24+
25+
data class AdClickAttributionSettings(
26+
val linkFormats: List<AdClickAttributionLinkFormat>,
27+
val allowlist: List<AdClickAttributionAllowlist>,
28+
val navigationExpiration: Long,
29+
val totalExpiration: Long,
30+
val heuristicDetection: String?,
31+
val domainDetection: String?
32+
)
33+
34+
data class AdClickAttributionLinkFormat(
35+
val url: String,
36+
val parameterName: String?,
37+
val parameterValue: String?,
38+
val adDomainParameterName: String?
39+
)
40+
41+
data class AdClickAttributionAllowlist(
42+
val blocklistEntry: String?,
43+
val host: String?
44+
)
45+
46+
data class AdClickAttributionException(val domain: String, val reason: String)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright (c) 2022 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.adclick.impl
18+
19+
import com.duckduckgo.adclick.api.AdClickFeatureName
20+
import com.duckduckgo.adclick.store.AdClickAttributionAllowlistEntity
21+
import com.duckduckgo.adclick.store.AdClickAttributionDetectionEntity
22+
import com.duckduckgo.adclick.store.AdClickAttributionExpirationEntity
23+
import com.duckduckgo.adclick.store.AdClickAttributionLinkFormatEntity
24+
import com.duckduckgo.adclick.store.AdClickAttributionRepository
25+
import com.duckduckgo.adclick.store.AdClickFeatureToggleRepository
26+
import com.duckduckgo.adclick.store.AdClickFeatureToggles
27+
import com.duckduckgo.di.scopes.AppScope
28+
import com.duckduckgo.privacy.config.api.PrivacyFeaturePlugin
29+
import com.squareup.anvil.annotations.ContributesMultibinding
30+
import com.squareup.moshi.JsonAdapter
31+
import com.squareup.moshi.Moshi
32+
import javax.inject.Inject
33+
34+
@ContributesMultibinding(AppScope::class)
35+
class AdClickAttributionPlugin @Inject constructor(
36+
private val adClickAttributionRepository: AdClickAttributionRepository,
37+
private val adClickFeatureTogglesRepository: AdClickFeatureToggleRepository
38+
) : PrivacyFeaturePlugin {
39+
40+
override fun store(featureName: String, jsonString: String): Boolean {
41+
val adClickFeature = adClickFeatureValueOf(featureName) ?: return false
42+
if (adClickFeature.value == this.featureName) {
43+
val moshi = Moshi.Builder().build()
44+
val jsonAdapter: JsonAdapter<AdClickAttributionFeature> =
45+
moshi.adapter(AdClickAttributionFeature::class.java)
46+
47+
val linkFormats = mutableListOf<AdClickAttributionLinkFormatEntity>()
48+
val allowList = mutableListOf<AdClickAttributionAllowlistEntity>()
49+
val expirations = mutableListOf<AdClickAttributionExpirationEntity>()
50+
val detections = mutableListOf<AdClickAttributionDetectionEntity>()
51+
52+
val adClickAttributionFeature: AdClickAttributionFeature? = jsonAdapter.fromJson(jsonString)
53+
54+
adClickAttributionFeature?.settings?.linkFormats?.map {
55+
linkFormats.add(
56+
AdClickAttributionLinkFormatEntity(
57+
url = it.url,
58+
parameterName = it.parameterName.orEmpty(),
59+
parameterValue = it.parameterValue.orEmpty(),
60+
adDomainParameterName = it.adDomainParameterName.orEmpty()
61+
)
62+
)
63+
}
64+
65+
adClickAttributionFeature?.settings?.allowlist?.map {
66+
if (it.blocklistEntry != null && it.host != null) {
67+
allowList.add(AdClickAttributionAllowlistEntity(blocklistEntry = it.blocklistEntry, host = it.host))
68+
}
69+
}
70+
71+
adClickAttributionFeature?.settings?.let {
72+
expirations.add(
73+
AdClickAttributionExpirationEntity(
74+
navigationExpiration = it.navigationExpiration,
75+
totalExpiration = it.totalExpiration
76+
)
77+
)
78+
detections.add(
79+
AdClickAttributionDetectionEntity(
80+
heuristicDetection = it.heuristicDetection.orEmpty(),
81+
domainDetection = it.domainDetection.orEmpty()
82+
)
83+
)
84+
}
85+
86+
adClickAttributionRepository.updateAll(linkFormats, allowList, expirations, detections)
87+
val isEnabled = adClickAttributionFeature?.state == "enabled"
88+
adClickFeatureTogglesRepository.insert(AdClickFeatureToggles(adClickFeature, isEnabled, null))
89+
return true
90+
}
91+
return false
92+
}
93+
94+
override val featureName: String = AdClickFeatureName.AdClickAttributionFeatureName.value
95+
}

0 commit comments

Comments
 (0)