Skip to content

Commit

Permalink
Malicious Site Protection activatable by subfeature (#3761)
Browse files Browse the repository at this point in the history
  • Loading branch information
mallexxx authored Jan 23, 2025
1 parent fd65a60 commit c727289
Show file tree
Hide file tree
Showing 17 changed files with 93 additions and 70 deletions.
2 changes: 1 addition & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15416,7 +15416,7 @@
repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 227.3.0;
version = 228.0.0;
};
};
9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/BrowserServicesKit",
"state" : {
"revision" : "b103a2a05dff415102978e0c1a2e5fbd6b86e813",
"version" : "227.3.0"
"revision" : "12227e2c97ad93f8924551e5ead831c79386b19b",
"version" : "228.0.0"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import BrowserServicesKit
final class AppPrivacyConfigurationDataProvider: EmbeddedDataProvider {

public struct Constants {
public static let embeddedDataETag = "\"0e0048c9ac6340f7434b75db358a5747\""
public static let embeddedDataSHA = "089e1c0f59404a65abdd14d9a1340e9a655d3d589960155a7c2e3855a80d30b7"
public static let embeddedDataETag = "\"62f7e11cdaf76e7e5ff50f874f6590fa\""
public static let embeddedDataSHA = "bc6abe8172a066a2e813c944a43aaaf70b2f557599e17cad3c583e6afa9b42f3"
}

var embeddedDataEtag: String {
Expand Down
51 changes: 47 additions & 4 deletions DuckDuckGo/ContentBlocker/macos-config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"readme": "https://github.com/duckduckgo/privacy-configuration",
"version": 1737129345760,
"version": 1737620176460,
"features": {
"adAttributionReporting": {
"state": "disabled",
Expand Down Expand Up @@ -70,6 +70,11 @@
"exceptions": [],
"hash": "c292bb627849854515cebbded288ef5a"
},
"addressBarTldNavOrSearch": {
"state": "disabled",
"exceptions": [],
"hash": "c292bb627849854515cebbded288ef5a"
},
"aiChat": {
"state": "enabled",
"exceptions": [],
Expand Down Expand Up @@ -653,6 +658,27 @@
{
"domain": "sueddeutsche.de"
},
{
"domain": "vital-parts.co.uk"
},
{
"domain": "lta.org.uk"
},
{
"domain": "hobbyhall.fi"
},
{
"domain": "wienmobil.at"
},
{
"domain": "memsoft.fr"
},
{
"domain": "bafin.de"
},
{
"domain": "levi.pt"
},
{
"domain": "marvel.com"
},
Expand Down Expand Up @@ -805,7 +831,14 @@
"sachsen-netze.de",
"alpharunning.co.uk",
"lecreuset.co.uk",
"sueddeutsche.de"
"sueddeutsche.de",
"vital-parts.co.uk",
"lta.org.uk",
"hobbyhall.fi",
"wienmobil.at",
"memsoft.fr",
"bafin.de",
"levi.pt"
]
},
"state": "enabled",
Expand All @@ -814,7 +847,7 @@
"state": "enabled"
}
},
"hash": "fbd77f789ae02d1795a7a001b75f2096"
"hash": "f1b13a96807f8030dc4bb31f97a1a33f"
},
"autofillBreakageReporter": {
"state": "disabled",
Expand Down Expand Up @@ -6222,11 +6255,16 @@
"domain": "broken.third-party.site"
}
],
"features": {
"onByDefault": {
"state": "enabled"
}
},
"settings": {
"hashPrefixUpdateFrequency": 20,
"filterSetUpdateFrequency": 720
},
"hash": "c5507786c7bd5326356357f2291fa536"
"hash": "3f08e9c0213c586858a618047970bbd2"
},
"marketplaceAdPostback": {
"state": "disabled",
Expand Down Expand Up @@ -10466,6 +10504,11 @@
},
"hash": "99b098daaea40136ae60aec41f52467e"
},
"webViewStateRestoration": {
"state": "disabled",
"exceptions": [],
"hash": "c292bb627849854515cebbded288ef5a"
},
"webViewBlobDownload": {
"exceptions": [],
"state": "disabled",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ public class MaliciousSiteProtectionManager: MaliciousSiteDetecting {
private let detector: MaliciousSiteDetecting
private let updateManager: MaliciousSiteProtection.UpdateManager
private let detectionPreferences: MaliciousSiteProtectionPreferences
private let featureFlagger: FeatureFlagger
private let configManager: PrivacyConfigurationManaging
private let featureFlags: MaliciousSiteProtectionFeatureFlagger

private var featureFlagsCancellable: AnyCancellable?
private var detectionPreferencesEnabledCancellable: AnyCancellable?
Expand All @@ -100,12 +99,11 @@ public class MaliciousSiteProtectionManager: MaliciousSiteDetecting {
dataManager: MaliciousSiteProtection.DataManager? = nil,
detector: MaliciousSiteProtection.MaliciousSiteDetecting? = nil,
detectionPreferences: MaliciousSiteProtectionPreferences = MaliciousSiteProtectionPreferences.shared,
featureFlagger: FeatureFlagger? = nil,
featureFlags: MaliciousSiteProtectionFeatureFlagger? = nil,
configManager: PrivacyConfigurationManaging? = nil,
updateIntervalProvider: UpdateManager.UpdateIntervalProvider? = nil
) {
self.featureFlagger = featureFlagger ?? NSApp.delegateTyped.featureFlagger
self.configManager = configManager ?? AppPrivacyFeatures.shared.contentBlocking.privacyConfigurationManager
self.featureFlags = featureFlags ?? NSApp.delegateTyped.featureFlagger.maliciousSiteProtectionFeatureFlags()

let embeddedDataProvider = embeddedDataProvider ?? EmbeddedDataProvider()
let dataManager = dataManager ?? {
Expand Down Expand Up @@ -136,23 +134,8 @@ public class MaliciousSiteProtectionManager: MaliciousSiteDetecting {
}

private func setupBindings() {
if featureFlagger.isFeatureOn(.maliciousSiteProtection) {
subscribeToDetectionPreferences()
return
}

guard let overridesHandler = featureFlagger.localOverrides?.actionHandler as? FeatureFlagOverridesPublishingHandler<FeatureFlag> else { return }
featureFlagsCancellable = overridesHandler.flagDidChangePublisher
.filter { $0.0 == .maliciousSiteProtection }
.sink { [weak self] change in
guard let self else { return }
if change.1 {
subscribeToDetectionPreferences()
} else {
detectionPreferencesEnabledCancellable = nil
stopUpdateTasks()
}
}
guard featureFlags.isMaliciousSiteProtectionEnabled else { return }
subscribeToDetectionPreferences()
}

private func subscribeToDetectionPreferences() {
Expand Down Expand Up @@ -182,10 +165,18 @@ public class MaliciousSiteProtectionManager: MaliciousSiteDetecting {
// MARK: - Public

public func evaluate(_ url: URL) async -> ThreatKind? {
guard configManager.privacyConfig.isFeature(.maliciousSiteProtection, enabledForDomain: url.host),
detectionPreferences.isEnabled else { return .none }
guard detectionPreferences.isEnabled,
featureFlags.shouldDetectMaliciousThreat(forDomain: url.host) else { return .none }

return await detector.evaluate(url)
}

}

extension FeatureFlagger {
func maliciousSiteProtectionFeatureFlags(configManager: PrivacyConfigurationManaging? = nil) -> MaliciousSiteProtectionFeatureFlags {
let configManager = configManager ?? AppPrivacyFeatures.shared.contentBlocking.privacyConfigurationManager
return .init(privacyConfigManager: configManager,
isMaliciousSiteProtectionEnabled: { self.isFeatureOn(.maliciousSiteProtection) })
}
}
2 changes: 1 addition & 1 deletion DuckDuckGo/Preferences/View/PreferencesGeneralView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ extension Preferences {
}

// SECTION 7: Phishing Detection
if featureFlagger.isFeatureOn(.maliciousSiteProtection) {
if featureFlagger.maliciousSiteProtectionFeatureFlags().isMaliciousSiteProtectionEnabled {
PreferencePaneSection(UserText.maliciousSiteDetectionHeader, spacing: 0) {
PreferencePaneSubSection {
ToggleMenuItem(UserText.maliciousSiteDetectionIsEnabled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@ class MaliciousSiteProtectionIntegrationTests: XCTestCase {
WebTrackingProtectionPreferences.shared.isGPCEnabled = false
MaliciousSiteProtectionPreferences.shared.isEnabled = true
let featureFlagger = MockFeatureFlagger()
detector = MaliciousSiteProtectionManager(featureFlagger: featureFlagger, configManager: MockPrivacyConfigurationManager(), updateIntervalProvider: { _ in nil })
let configManager = MockPrivacyConfigurationManager()
let privacyConfig = MockPrivacyConfiguration()
privacyConfig.isSubfeatureKeyEnabled = { (subfeature: any PrivacySubfeature, _: AppVersionProvider) -> Bool in
if case MaliciousSiteProtectionSubfeature.onByDefault = subfeature { true } else { false }
}
configManager.privacyConfig = privacyConfig
detector = MaliciousSiteProtectionManager(featureFlags: featureFlagger.maliciousSiteProtectionFeatureFlags(configManager: configManager), updateIntervalProvider: { _ in nil })
schemeHandler = TestSchemeHandler()
schemeHandler.middleware = [{
if $0.url!.lastPathComponent == "phishing.html" {
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/DataBrokerProtection/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ let package = Package(
targets: ["DataBrokerProtection"])
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "227.3.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "228.0.0"),
.package(path: "../SwiftUIExtensions"),
.package(path: "../AppKitExtensions"),
.package(path: "../XPCHelper"),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/FeatureFlags/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
targets: ["FeatureFlags"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "227.3.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "228.0.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ extension FeatureFlag: FeatureFlagDescribing {
case .htmlNewTabPage:
return true
case .maliciousSiteProtection:
return true
return false
case .autofillPartialFormSaves:
return true
case .autcompleteTabs:
Expand Down Expand Up @@ -98,7 +98,7 @@ extension FeatureFlag: FeatureFlagDescribing {
case .freemiumDBP:
return .remoteReleasable(.subfeature(DBPSubfeature.freemium))
case .maliciousSiteProtection:
return .remoteReleasable(.feature(.maliciousSiteProtection))
return .remoteReleasable(.subfeature(MaliciousSiteProtectionSubfeature.onByDefault))
case .contextualOnboarding:
return .remoteReleasable(.feature(.contextualOnboarding))
case .credentialsImportPromotionForExistingUsers:
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/HistoryView/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
targets: ["HistoryView"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "227.3.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "228.0.0"),
.package(path: "../WebKitExtensions"),
.package(path: "../UserScriptActionsManager"),
.package(path: "../Utilities"),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/NetworkProtectionMac/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ let package = Package(
.library(name: "VPNAppLauncher", targets: ["VPNAppLauncher"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "227.3.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "228.0.0"),
.package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.3"),
.package(path: "../AppLauncher"),
.package(path: "../UDSHelper"),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/NewTabPage/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
targets: ["NewTabPage"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "227.3.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "228.0.0"),
.package(path: "../WebKitExtensions"),
.package(path: "../UserScriptActionsManager"),
.package(path: "../Utilities"),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/SubscriptionUI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
targets: ["SubscriptionUI"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "227.3.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "228.0.0"),
.package(path: "../SwiftUIExtensions"),
.package(path: "../FeatureFlags")
],
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/UserScriptActionsManager/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ let package = Package(
targets: ["UserScriptActionsManager"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "227.3.0")
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "228.0.0")
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/WebKitExtensions/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "227.3.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "228.0.0"),
.package(path: "../AppKitExtensions")
],
targets: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@ import XCTest
@testable import DuckDuckGo_Privacy_Browser

final class MaliciousSiteProtectionTests: XCTestCase {
lazy var phishingDetection: MaliciousSiteProtectionManager! = {
MaliciousSiteProtectionManager(apiService: apiService, dataManager: dataManager, detector: MockMaliciousSiteDetector(), featureFlagger: MockFeatureFlagger())
lazy var phishingDetection: MaliciousSiteProtectionManager! = { () -> MaliciousSiteProtectionManager in
let configManager = MockPrivacyConfigurationManager()
let privacyConfig = MockPrivacyConfiguration()
privacyConfig.isSubfeatureKeyEnabled = { (subfeature: any PrivacySubfeature, _: AppVersionProvider) -> Bool in
if case MaliciousSiteProtectionSubfeature.onByDefault = subfeature { true } else { false }
}
configManager.privacyConfig = privacyConfig
return MaliciousSiteProtectionManager(apiService: apiService, dataManager: dataManager, detector: MockMaliciousSiteDetector(), featureFlags: MaliciousSiteProtectionFeatureFlags(privacyConfigManager: configManager, isMaliciousSiteProtectionEnabled: { true }))
}()
var apiService: MockAPIService!
var mockDetector: MockMaliciousSiteDetector!
Expand Down Expand Up @@ -91,26 +97,3 @@ final class MaliciousSiteProtectionTests: XCTestCase {
XCTAssertNil(isMalicious)
}
}

extension MaliciousSiteProtectionTests {
class MockFeatureFlagger: FeatureFlagger {
var internalUserDecider: InternalUserDecider = DefaultInternalUserDecider(store: MockInternalUserStoring())
var localOverrides: FeatureFlagLocalOverriding?

func isFeatureOn<Flag: FeatureFlagDescribing>(for featureFlag: Flag, allowOverride: Bool) -> Bool {
return true
}

func getCohortIfEnabled(_ subfeature: any PrivacySubfeature) -> CohortID? {
return nil
}

func getCohortIfEnabled<Flag>(for featureFlag: Flag) -> (any FlagCohort)? where Flag: FeatureFlagExperimentDescribing {
return nil
}

func getAllActiveExperiments() -> Experiments {
return [:]
}
}
}

0 comments on commit c727289

Please sign in to comment.