Skip to content
This repository was archived by the owner on Feb 24, 2025. It is now read-only.

Commit 8923a1d

Browse files
Malicious site protection (#3901)
Task/Issue URL: https://app.asana.com/0/1206329551987282/1209233394358451/f Tech Design URL: https://app.asana.com/0/481882893211075/1207273224076495/f **Description**: This PR adds Malicious Site Protection (main navigation only) to the iOS browser.
1 parent e9b7364 commit 8923a1d

File tree

96 files changed

+5477
-229
lines changed

Some content is hidden

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

96 files changed

+5477
-229
lines changed

Core/AppURLs.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public extension URL {
3838
static let apps = URL(string: AppDeepLinkSchemes.quickLink.appending("\(ddg.host!)/apps?origin=funnel_app_ios"))!
3939
static let searchSettings = URL(string: AppDeepLinkSchemes.quickLink.appending("\(ddg.host!)/settings"))!
4040
static let autofillHelpPageLink = URL(string: AppDeepLinkSchemes.quickLink.appending("\(ddg.host!)/duckduckgo-help-pages/sync-and-backup/password-manager-security/"))!
41+
static let maliciousSiteProtectionLearnMore = URL(string: AppDeepLinkSchemes.quickLink.appending("\(ddg.host!)/duckduckgo-help-pages/privacy/phishing-and-malware-protection/"))!
4142

4243
static let surrogates = URL(string: "\(staticBase)/surrogates.txt")!
4344

Core/FeatureFlag.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ public enum FeatureFlag: String {
7474
case aiChatNewTabPage
7575

7676
case testExperiment
77+
78+
/// Feature flag to enable / disable phishing and malware protection
79+
/// https://app.asana.com/0/1206329551987282/1207149365636877/f
80+
case maliciousSiteProtection
7781
}
7882

7983
extension FeatureFlag: FeatureFlagDescribing {
@@ -179,6 +183,8 @@ extension FeatureFlag: FeatureFlagDescribing {
179183
return .internalOnly()
180184
case .testExperiment:
181185
return .remoteReleasable(.subfeature(ExperimentTestSubfeatures.experimentTestAA))
186+
case .maliciousSiteProtection:
187+
return .remoteReleasable(.subfeature(MaliciousSiteProtectionSubfeature.onByDefault))
182188
}
183189
}
184190
}

Core/Pixel.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ public struct PixelParameters {
168168
public static let appEvent = "event"
169169

170170
public static let didCallWillEnterForeground = "didCallWillEnterForeground"
171+
172+
// Background Tasks
173+
public static let backgroundTaskCategory = "category"
171174
}
172175

173176
public struct PixelValues {

Core/PixelEvent.swift

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import BrowserServicesKit
2222
import Bookmarks
2323
import Configuration
2424
import DDGSync
25+
import MaliciousSiteProtection
26+
import PixelKit
2527

2628
extension Pixel {
2729

@@ -979,6 +981,9 @@ extension Pixel {
979981

980982
case tabInteractionStateFailedToRestore
981983
case tabInteractionStateRestorationTime(_ time: BucketAggregation)
984+
985+
// MARK: Malicious Site Protection
986+
case maliciousSiteProtection(event: MaliciousSiteProtectionEvent)
982987
}
983988

984989
}
@@ -1540,7 +1545,7 @@ extension Pixel.Event {
15401545
case .webKitWarmupUnexpectedDidFinish: return "m_d_webkit-warmup-unexpected-did-finish"
15411546
case .webKitWarmupUnexpectedDidTerminate: return "m_d_webkit-warmup-unexpected-did-terminate"
15421547

1543-
case .backgroundTaskSubmissionFailed: return "m_bt_rf"
1548+
case .backgroundTaskSubmissionFailed: return "m_background-task_submission-failed"
15441549

15451550
case .blankOverlayNotDismissed: return "m_d_ovs"
15461551

@@ -1961,6 +1966,9 @@ extension Pixel.Event {
19611966
case .appDidTransitionToUnexpectedState: return "m_debug_app-did-transition-to-unexpected-state-3"
19621967

19631968
case .debugBreakageExperiment: return "m_debug_breakage_experiment_u"
1969+
1970+
// MARK: Malicious Site Protection
1971+
case .maliciousSiteProtection(let event): return "m_\(event.name)"
19641972
}
19651973
}
19661974
}
@@ -2081,3 +2089,67 @@ extension Pixel.Event {
20812089
}
20822090
}
20832091
}
2092+
2093+
// This is a temporary mapper from PixelKit to Pixel events for MaliciousSiteProtection
2094+
// Malicious Site Protection BSK library depends on PixelKit which is not ready yet to be ported to iOS.
2095+
// The below code maps between `PixelKitEvent` to `Pixel.Event` in order to use `Pixel.fire` on the client.
2096+
public extension Pixel.Event {
2097+
2098+
enum MaliciousSiteProtectionEvent: Equatable {
2099+
case errorPageShown(category: ThreatKind, clientSideHit: Bool?)
2100+
case visitSite(category: ThreatKind)
2101+
case iframeLoaded(category: ThreatKind)
2102+
case settingToggled(to: Bool)
2103+
case matchesApiTimeout
2104+
case failedToDownloadInitialDataSets(category: ThreatKind, type: DataManager.StoredDataType.Kind)
2105+
2106+
public init?(_ pixelKitEvent: MaliciousSiteProtection.Event) {
2107+
switch pixelKitEvent {
2108+
case .errorPageShown(category: let category, clientSideHit: let clientSideHit):
2109+
self = .errorPageShown(category: category, clientSideHit: clientSideHit)
2110+
case .visitSite(category: let category):
2111+
self = .visitSite(category: category)
2112+
case .iframeLoaded(category: let category):
2113+
self = .iframeLoaded(category: category)
2114+
case .settingToggled(let enabled):
2115+
self = .settingToggled(to: enabled)
2116+
case .matchesApiTimeout:
2117+
self = .matchesApiTimeout
2118+
case .matchesApiFailure:
2119+
return nil
2120+
case .failedToDownloadInitialDataSets(category: let category, type: let type):
2121+
self = .failedToDownloadInitialDataSets(category: category, type: type)
2122+
}
2123+
}
2124+
2125+
private var event: PixelKitEventV2 {
2126+
switch self {
2127+
case .errorPageShown(let category, let clientSideHit):
2128+
return MaliciousSiteProtection.Event.errorPageShown(category: category, clientSideHit: clientSideHit)
2129+
case .visitSite(let category):
2130+
return MaliciousSiteProtection.Event.visitSite(category: category)
2131+
case .iframeLoaded(let category):
2132+
return MaliciousSiteProtection.Event.iframeLoaded(category: category)
2133+
case .settingToggled(let enabled):
2134+
return MaliciousSiteProtection.Event.settingToggled(to: enabled)
2135+
case .matchesApiTimeout:
2136+
return MaliciousSiteProtection.Event.matchesApiTimeout
2137+
case .failedToDownloadInitialDataSets(let category, let type):
2138+
return MaliciousSiteProtection.Event.failedToDownloadInitialDataSets(category: category, type: type)
2139+
}
2140+
}
2141+
2142+
var name: String {
2143+
switch self {
2144+
case .failedToDownloadInitialDataSets:
2145+
return "debug_\(event.name)"
2146+
default:
2147+
return event.name
2148+
}
2149+
}
2150+
2151+
public var parameters: [String: String] {
2152+
event.parameters ?? [:]
2153+
}
2154+
}
2155+
}

Core/UserDefaultsPropertyWrapper.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ public struct UserDefaultsWrapper<T> {
182182

183183
// TipKit
184184
case resetTipKitOnNextLaunch = "com.duckduckgo.ios.tipKit.resetOnNextLaunch"
185+
186+
// Malicious Site Protection
187+
case maliciousSiteProtectionEnabled = "com.duckduckgo.ios.maliciousSiteProtection.enabled"
185188
}
186189

187190
private let key: Key

0 commit comments

Comments
 (0)