Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Malicious site protection navigation detection #3707

Open
wants to merge 15 commits into
base: alessandro/malicious-site-protection
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -763,14 +763,15 @@
9F254ACE2CF5D3540063B308 /* SpecialErrorPageNavigationHandlerIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254ACD2CF5D3540063B308 /* SpecialErrorPageNavigationHandlerIntegrationTests.swift */; };
9F254AD22CF5D3A80063B308 /* MockSpecialErrorWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254AD12CF5D3A20063B308 /* MockSpecialErrorWebView.swift */; };
9F254AD32CF5D3A80063B308 /* MockSpecialErrorWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254AD12CF5D3A20063B308 /* MockSpecialErrorWebView.swift */; };
9F254AD52CF5E5B10063B308 /* DummyMaliciousSiteProtectionNavigationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254AD42CF5E5B10063B308 /* DummyMaliciousSiteProtectionNavigationHandler.swift */; };
9F254AD62CF5E5B10063B308 /* DummyMaliciousSiteProtectionNavigationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254AD42CF5E5B10063B308 /* DummyMaliciousSiteProtectionNavigationHandler.swift */; };
9F254AD52CF5E5B10063B308 /* MockMaliciousSiteProtectionNavigationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254AD42CF5E5B10063B308 /* MockMaliciousSiteProtectionNavigationHandler.swift */; };
9F254AD62CF5E5B10063B308 /* MockMaliciousSiteProtectionNavigationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254AD42CF5E5B10063B308 /* MockMaliciousSiteProtectionNavigationHandler.swift */; };
9F254AD82CF605310063B308 /* MockSSLErrorPageNavigationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254AD72CF605310063B308 /* MockSSLErrorPageNavigationHandler.swift */; };
9F254AD92CF605310063B308 /* MockSSLErrorPageNavigationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254AD72CF605310063B308 /* MockSSLErrorPageNavigationHandler.swift */; };
9F254ADB2CF6120E0063B308 /* MockSpecialErrorPageNavigationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254ADA2CF6120E0063B308 /* MockSpecialErrorPageNavigationDelegate.swift */; };
9F254ADC2CF6120E0063B308 /* MockSpecialErrorPageNavigationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254ADA2CF6120E0063B308 /* MockSpecialErrorPageNavigationDelegate.swift */; };
9F254ADE2CF636CF0063B308 /* DummyWKNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254ADD2CF636CF0063B308 /* DummyWKNavigation.swift */; };
9F254ADF2CF636CF0063B308 /* DummyWKNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254ADD2CF636CF0063B308 /* DummyWKNavigation.swift */; };
9F254AF12CF8D5250063B308 /* MaliciousSiteProtection in Frameworks */ = {isa = PBXBuildFile; productRef = 9F254AF02CF8D5250063B308 /* MaliciousSiteProtection */; };
9F254AFF2CF9FA1B0063B308 /* WebViewNavigationHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254AFE2CF9FA1B0063B308 /* WebViewNavigationHandling.swift */; };
9F254B012CF9FA8D0063B308 /* SpecialErrorPageActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254B002CF9FA8D0063B308 /* SpecialErrorPageActionHandler.swift */; };
9F254B032CF9FB2E0063B308 /* SpecialErrorPageNavigationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F254B022CF9FB2E0063B308 /* SpecialErrorPageNavigationDelegate.swift */; };
Expand Down Expand Up @@ -821,6 +822,9 @@
9FB027192C26BC29009EA190 /* BrowsersComparisonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB027182C26BC29009EA190 /* BrowsersComparisonModel.swift */; };
9FB0271B2C2927D0009EA190 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB0271A2C2927D0009EA190 /* OnboardingView.swift */; };
9FB0271D2C293619009EA190 /* OnboardingIntroViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB0271C2C293619009EA190 /* OnboardingIntroViewModel.swift */; };
9FBC76672CFE33B5008B21E7 /* MaliciousSiteProtectionNavigationHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBC76662CFE33B5008B21E7 /* MaliciousSiteProtectionNavigationHandlerTests.swift */; };
9FBC766A2CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBC76692CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift */; };
9FBC766B2CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBC76692CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift */; };
9FCFCD802C6AF56D006EB7A0 /* LaunchOptionsHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FCFCD7F2C6AF56D006EB7A0 /* LaunchOptionsHandlerTests.swift */; };
9FCFCD812C6B020D006EB7A0 /* LaunchOptionsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FCFCD7D2C6AF52A006EB7A0 /* LaunchOptionsHandler.swift */; };
9FCFCD852C75C91A006EB7A0 /* ProgressBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FCFCD842C75C91A006EB7A0 /* ProgressBarView.swift */; };
Expand Down Expand Up @@ -2626,7 +2630,7 @@
9F254ACA2CF5CDC60063B308 /* SpecialErrorPageNavigationHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpecialErrorPageNavigationHandlerTests.swift; sourceTree = "<group>"; };
9F254ACD2CF5D3540063B308 /* SpecialErrorPageNavigationHandlerIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpecialErrorPageNavigationHandlerIntegrationTests.swift; sourceTree = "<group>"; };
9F254AD12CF5D3A20063B308 /* MockSpecialErrorWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSpecialErrorWebView.swift; sourceTree = "<group>"; };
9F254AD42CF5E5B10063B308 /* DummyMaliciousSiteProtectionNavigationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyMaliciousSiteProtectionNavigationHandler.swift; sourceTree = "<group>"; };
9F254AD42CF5E5B10063B308 /* MockMaliciousSiteProtectionNavigationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMaliciousSiteProtectionNavigationHandler.swift; sourceTree = "<group>"; };
9F254AD72CF605310063B308 /* MockSSLErrorPageNavigationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSSLErrorPageNavigationHandler.swift; sourceTree = "<group>"; };
9F254ADA2CF6120E0063B308 /* MockSpecialErrorPageNavigationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSpecialErrorPageNavigationDelegate.swift; sourceTree = "<group>"; };
9F254ADD2CF636CF0063B308 /* DummyWKNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyWKNavigation.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2678,6 +2682,8 @@
9FB027182C26BC29009EA190 /* BrowsersComparisonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowsersComparisonModel.swift; sourceTree = "<group>"; };
9FB0271A2C2927D0009EA190 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
9FB0271C2C293619009EA190 /* OnboardingIntroViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingIntroViewModel.swift; sourceTree = "<group>"; };
9FBC76662CFE33B5008B21E7 /* MaliciousSiteProtectionNavigationHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionNavigationHandlerTests.swift; sourceTree = "<group>"; };
9FBC76692CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMaliciousSiteProtectionManager.swift; sourceTree = "<group>"; };
9FCFCD7D2C6AF52A006EB7A0 /* LaunchOptionsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LaunchOptionsHandler.swift; path = ../DuckDuckGo/LaunchOptionsHandler.swift; sourceTree = "<group>"; };
9FCFCD7F2C6AF56D006EB7A0 /* LaunchOptionsHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchOptionsHandlerTests.swift; sourceTree = "<group>"; };
9FCFCD842C75C91A006EB7A0 /* ProgressBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBarView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3172,6 +3178,7 @@
31E69A63280F4CB600478327 /* DuckUI in Frameworks */,
CB941A6E2B96AB08000F9E7A /* PrivacyDashboard in Frameworks */,
F42D541D29DCA40B004C4FF1 /* DesignResourcesKit in Frameworks */,
9F254AF12CF8D5250063B308 /* MaliciousSiteProtection in Frameworks */,
1E5918472CA422A7008ED2B3 /* Navigation in Frameworks */,
85875B6129912A9900115F05 /* SyncUI in Frameworks */,
F4D7F634298C00C3006C3AE9 /* FindInPageIOSJSSupport in Frameworks */,
Expand Down Expand Up @@ -5037,6 +5044,7 @@
CBC88EE22C7F8B1700F0F8C5 /* SSLErrorPageNavigationHandlerTests.swift */,
9F254ACA2CF5CDC60063B308 /* SpecialErrorPageNavigationHandlerTests.swift */,
9F254ACD2CF5D3540063B308 /* SpecialErrorPageNavigationHandlerIntegrationTests.swift */,
9FBC76662CFE33B5008B21E7 /* MaliciousSiteProtectionNavigationHandlerTests.swift */,
);
path = SpecialErrorPage;
sourceTree = "<group>";
Expand All @@ -5045,10 +5053,11 @@
isa = PBXGroup;
children = (
9F254AD12CF5D3A20063B308 /* MockSpecialErrorWebView.swift */,
9F254AD42CF5E5B10063B308 /* DummyMaliciousSiteProtectionNavigationHandler.swift */,
9F254AD42CF5E5B10063B308 /* MockMaliciousSiteProtectionNavigationHandler.swift */,
9F254AD72CF605310063B308 /* MockSSLErrorPageNavigationHandler.swift */,
9F254ADA2CF6120E0063B308 /* MockSpecialErrorPageNavigationDelegate.swift */,
9F254ADD2CF636CF0063B308 /* DummyWKNavigation.swift */,
9FBC76692CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift */,
);
path = TestDoubles;
sourceTree = "<group>";
Expand Down Expand Up @@ -6896,6 +6905,7 @@
9F96F73A2C9144D5009E45D5 /* Onboarding */,
1E5918462CA422A7008ED2B3 /* Navigation */,
315C77812CFA41A400699683 /* AIChat */,
9F254AF02CF8D5250063B308 /* MaliciousSiteProtection */,
);
productName = DuckDuckGo;
productReference = 84E341921E2F7EFB00BDBA6F /* DuckDuckGo.app */;
Expand Down Expand Up @@ -8379,7 +8389,7 @@
569437342BE4E41500C0881B /* SyncErrorHandlerSyncErrorsAlertsTests.swift in Sources */,
CBC88EE32C7F8B1700F0F8C5 /* SSLErrorPageNavigationHandlerTests.swift in Sources */,
85C11E4120904BBE00BFFEB4 /* VariantManagerTests.swift in Sources */,
9F254AD52CF5E5B10063B308 /* DummyMaliciousSiteProtectionNavigationHandler.swift in Sources */,
9F254AD52CF5E5B10063B308 /* MockMaliciousSiteProtectionNavigationHandler.swift in Sources */,
F1134ECE1F40EA9C00B73467 /* AtbParserTests.swift in Sources */,
F189AEE41F18FDAF001EBAE1 /* LinkTests.swift in Sources */,
6F7FB8E12C660B3E00867DA7 /* NewTabPageFavoritesModelTests.swift in Sources */,
Expand Down Expand Up @@ -8419,6 +8429,7 @@
9F254ADF2CF636CF0063B308 /* DummyWKNavigation.swift in Sources */,
6F7BACD42CEE084B00F561D8 /* OmniBarEqualityCheckTests.swift in Sources */,
6F7FB8E72C66197E00867DA7 /* NewTabPageSectionsSettingsModelTests.swift in Sources */,
9FBC766A2CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift in Sources */,
851CD674244D7E6000331B98 /* UserDefaultsExtension.swift in Sources */,
569437362BE5160600C0881B /* SyncSettingsViewControllerErrorTests.swift in Sources */,
EEC02C162B065BE00045CE11 /* NetworkProtectionVPNLocationViewModelTests.swift in Sources */,
Expand Down Expand Up @@ -8492,6 +8503,7 @@
9F4CC51F2C48D758006A96EB /* ContextualDaxDialogsFactoryTests.swift in Sources */,
8521FDE6238D414B00A44CC3 /* FileStoreTests.swift in Sources */,
F14E491F1E391CE900DC037C /* URLExtensionTests.swift in Sources */,
9FBC76672CFE33B5008B21E7 /* MaliciousSiteProtectionNavigationHandlerTests.swift in Sources */,
9F23B8062C2BE22700950875 /* OnboardingIntroViewModelTests.swift in Sources */,
9F69331D2C5A191400CD6A5D /* MockTutorialSettings.swift in Sources */,
85D2187424BF25CD004373D2 /* FaviconsTests.swift in Sources */,
Expand Down Expand Up @@ -8593,8 +8605,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9F254AD62CF5E5B10063B308 /* DummyMaliciousSiteProtectionNavigationHandler.swift in Sources */,
9F254AD62CF5E5B10063B308 /* MockMaliciousSiteProtectionNavigationHandler.swift in Sources */,
85F21DB0210F5E32002631A6 /* AtbIntegrationTests.swift in Sources */,
9FBC766B2CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift in Sources */,
9F254AD22CF5D3A80063B308 /* MockSpecialErrorWebView.swift in Sources */,
9F254ADE2CF636CF0063B308 /* DummyWKNavigation.swift in Sources */,
8551912724746EDC0010FDD0 /* SnapshotHelper.swift in Sources */,
Expand Down Expand Up @@ -11965,6 +11978,10 @@
package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */;
productName = Bookmarks;
};
9F254AF02CF8D5250063B308 /* MaliciousSiteProtection */ = {
isa = XCSwiftPackageProductDependency;
productName = MaliciousSiteProtection;
};
9F8FE9482BAE50E50071E372 /* Lottie */ = {
isa = XCSwiftPackageProductDependency;
package = 9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,20 @@
//

import Foundation
import MaliciousSiteProtection

final class MaliciousSiteProtectionManager: MaliciousSiteDetecting {

func evaluate(_ url: URL) async -> ThreatKind? {
try? await Task.sleep(interval: 0.3)
return .none
}

}

// MARK: - To Remove

// These entities are copied from BSK and they will be used to mock the library
import SpecialErrorPages

protocol MaliciousSiteDetecting {
func evaluate(_ url: URL) async -> ThreatKind?
}

public enum ThreatKind: String, CaseIterable, CustomStringConvertible {
public var description: String { rawValue }

case phishing
case malware
}

public extension ThreatKind {

var errorPageType: SpecialErrorKind {
switch self {
case .malware: .phishing // WIP in BSK
case .phishing: .phishing
switch url.absoluteString {
case "http://privacy-test-pages.site/security/badware/phishing.html":
return .phishing
case "http://privacy-test-pages.site/security/badware/malware.html":
return .malware
default:
return .none
}
}

Expand Down
6 changes: 6 additions & 0 deletions DuckDuckGo/SpecialErrorPage/Model/SpecialErrorModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import Foundation
import SpecialErrorPages
import WebKit

struct SpecialErrorModel: Equatable {
let url: URL
Expand All @@ -29,3 +30,8 @@ struct SSLSpecialError {
let type: SSLErrorType
let error: SpecialErrorModel
}

struct MaliciousSiteDetectionNavigationResponse: Equatable {
let navigationAction: WKNavigationAction
let errorData: SpecialErrorData
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
//

import Foundation
import SpecialErrorPages

/// A type that defines actions for handling special error pages.
///
Expand All @@ -26,11 +27,14 @@ import Foundation
/// advanced information related to the error.
protocol SpecialErrorPageActionHandler {
/// Handles the action of navigating to the site associated with the error page
func visitSite()
@MainActor
func visitSite(url: URL, errorData: SpecialErrorData)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m a bit on the fence with this.
The idea was to have a generic protocol that all the sub-special error handlers should conform to.
For MaliciousSiteProtectionNavigationHandler I need to pass the URL and SpecialErrorData but I don’t need them for the SSL handler. So I feel I’m violating the Interface Segregation principle. what do you think?


/// Handles the action of leaving the site associated with the error page
@MainActor
func leaveSite()

/// Handles the action of requesting more detailed information about the error
@MainActor
func advancedInfoPresented()
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ protocol SpecialErrorPageContextHandling: AnyObject {
/// The URL that failed to load, if any.
var failedURL: URL? { get }

/// A boolean value indicating whether the WebView request requires showing a special error page.
var isSpecialErrorPageRequest: Bool { get }

/// Attaches a web view to the special error page handling.
func attachWebView(_ webView: WKWebView)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import Foundation
import WebKit
import MaliciousSiteProtection

// MARK: - WebViewNavigation

Expand All @@ -41,15 +42,24 @@ protocol WebViewNavigationHandling: AnyObject {
/// - Parameters:
/// - navigationAction: Details about the action that triggered the navigation request.
/// - webView: The web view from which the navigation request began.
/// - Returns: A Boolean value that indicates whether the navigation action was handled.
func handleSpecialErrorNavigation(navigationAction: WKNavigationAction, webView: WKWebView) async -> Bool
@MainActor
func handleDecidePolicyFor(navigationAction: WKNavigationAction, webView: WKWebView)

/// Decides whether to to navigate to new content after the response to the navigation request is known or cancel the navigation and show a special error page based on the specified action information.
/// - Parameters:
/// - navigationResponse: Descriptive information about the navigation response.
/// - webView: The web view from which the navigation request began.
/// - Returns: A Boolean value that indicates whether to cancel or allow the navigation.
@MainActor
func handleDecidePolicyfor(navigationResponse: WKNavigationResponse, webView: WKWebView) async -> Bool

/// Handles authentication challenges received by the web view.
///
/// - Parameters:
/// - webView: The web view that receives the authentication challenge.
/// - challenge: The authentication challenge.
/// - completionHandler: A completion handler block to execute with the response.
@MainActor
func handleWebView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)

/// Handles failures during provisional navigation.
Expand All @@ -58,12 +68,14 @@ protocol WebViewNavigationHandling: AnyObject {
/// - webView: The `WKWebView` instance that failed the navigation.
/// - navigation: The navigation object for the operation.
/// - error: The error that occurred.
@MainActor
func handleWebView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WebViewNavigation, withError error: NSError)

/// Handles the successful completion of a navigation in the web view.
///
/// - Parameters:
/// - webView: The web view that loaded the content.
/// - navigation: The navigation object that finished.
@MainActor
func handleWebView(_ webView: WKWebView, didFinish navigation: WebViewNavigation)
}
Loading
Loading