Skip to content

Commit

Permalink
Uplift of #22849 (squashed) to release
Browse files Browse the repository at this point in the history
  • Loading branch information
kylehickinson committed Apr 3, 2024
1 parent 909f7f5 commit 6c76fe4
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ extension BrowserViewController {

// Toogle Reader Mode Activity
// If the reader mode button is occluded due to a secure content state warning add it as an activity
if let tab = tabManager.selectedTab, tab.secureContentState.shouldDisplayWarning {
if let tab = tabManager.selectedTab, tab.lastKnownSecureContentState.shouldDisplayWarning {
if tab.readerModeAvailableOrActive {
activities.append(
BasicMenuActivity(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ extension BrowserViewController: ToolbarDelegate {
let hasCertificate = (tab.webView?.serverTrust ?? (try? ErrorPageHelper.serverTrust(from: url))) != nil
let pageSecurityView = PageSecurityView(
displayURL: urlBar.locationView.urlDisplayLabel.text ?? url.absoluteDisplayString,
secureState: tab.secureContentState,
secureState: tab.lastKnownSecureContentState,
hasCertificate: hasCertificate,
presentCertificateViewer: { [weak self] in
self?.dismiss(animated: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1783,7 +1783,7 @@ public class BrowserViewController: UIViewController {
// If navigation will start from NTP, tab display url will be nil until
// didCommit is called and it will cause url bar be empty in that period
// To fix this when tab display url is empty, webview url is used
if tab.url?.displayURL == nil {
if tab === tabManager.selectedTab, tab.url?.displayURL == nil {
if let url = webView.url, !url.isLocal, !InternalURL.isValid(url: url) {
updateToolbarCurrentURL(url.displayURL)
}
Expand All @@ -1807,6 +1807,13 @@ public class BrowserViewController: UIViewController {
!InternalURL.isValid(url: url), webView.estimatedProgress > 0 {
topToolbar.updateProgressBar(Float(webView.estimatedProgress))
}

Task {
await tab.updateSecureContentState()
if self.tabManager.selectedTab === tab {
self.updateToolbarSecureContentState(tab.lastKnownSecureContentState)
}
}
case .title:
// Ensure that the tab title *actually* changed to prevent repeated calls
// to navigateInTab(tab:).
Expand All @@ -1828,149 +1835,18 @@ public class BrowserViewController: UIViewController {

navigationToolbar.updateForwardStatus(canGoForward)
case .hasOnlySecureContent:
guard let tab = tabManager[webView] else {
break
}

if tab.secureContentState == .secure, !webView.hasOnlySecureContent,
tab.url?.origin == tab.webView?.url?.origin {
if let url = tab.webView?.url, url.isReaderModeURL {
break
}

tab.secureContentState = .mixedContent
}

if let url = tab.webView?.url,
InternalURL.isValid(url: url),
let internalUrl = InternalURL(url) {

if internalUrl.isErrorPage {
if ErrorPageHelper.certificateError(for: url) != 0 {
// Cert validation takes precedence over all other errors
tab.secureContentState = .invalidCert
} else if NetworkErrorPageHandler.isNetworkError(errorCode: ErrorPageHelper.errorCode(for: url)) {
// Network error takes precedence over missing cert
// Because we cannot determine if a cert is missing yet, if we cannot connect to the server
// Our network interstitial page shows
tab.secureContentState = .localhost
} else {
// Since it's not a cert error explicitly, and it's not a network error, and the cert is missing (no serverTrust),
// then we display .missingSSL
tab.secureContentState = .missingSSL
}
} else if url.isReaderModeURL || InternalURL.isValid(url: url) {
tab.secureContentState = .localhost
}
}

if tabManager.selectedTab === tab {
updateToolbarSecureContentState(tab.secureContentState)
}
case .serverTrust:
guard let tab = tabManager[webView] else {
break
}

tab.secureContentState = .unknown

guard let url = webView.url,
let serverTrust = webView.serverTrust
else {
if let url = webView.url {
if InternalURL.isValid(url: url),
let internalUrl = InternalURL(url),
(internalUrl.isAboutURL || internalUrl.isAboutHomeURL) {

tab.secureContentState = .localhost
if tabManager.selectedTab === tab {
updateToolbarSecureContentState(.localhost)
}
break
}

if InternalURL.isValid(url: url),
let internalUrl = InternalURL(url),
internalUrl.isErrorPage {

if ErrorPageHelper.certificateError(for: url) != 0 {
// Cert validation takes precedence over all other errors
tab.secureContentState = .invalidCert
} else if NetworkErrorPageHandler.isNetworkError(errorCode: ErrorPageHelper.errorCode(for: url)) {
// Network error takes precedence over missing cert
// Because we cannot determine if a cert is missing yet, if we cannot connect to the server
// Our network interstitial page shows
tab.secureContentState = .localhost
} else {
// Since it's not a cert error explicitly, and it's not a network error, and the cert is missing (no serverTrust),
// then we display .missingSSL
tab.secureContentState = .missingSSL
}

if tabManager.selectedTab === tab {
updateToolbarSecureContentState(tab.secureContentState)
}
break
}

if url.isReaderModeURL || InternalURL.isValid(url: url) {
tab.secureContentState = .localhost
if tabManager.selectedTab === tab {
updateToolbarSecureContentState(.localhost)
}
break
}

// All our checks failed, we show the page as insecure
tab.secureContentState = .missingSSL
} else {
// When there is no URL, it's likely a new tab.
tab.secureContentState = .localhost
}

Task {
await tab.updateSecureContentState()
if tabManager.selectedTab === tab {
updateToolbarSecureContentState(tab.secureContentState)
self.updateToolbarSecureContentState(tab.lastKnownSecureContentState)
}
break
}

guard let scheme = url.scheme,
let host = url.host
else {
tab.secureContentState = .unknown
self.updateURLBar()
return
}

let port: Int
if let urlPort = url.port {
port = urlPort
} else if scheme == "https" {
port = 443
} else {
port = 80
}

Task { @MainActor in
do {
let result = await BraveCertificateUtils.verifyTrust(serverTrust, host: host, port: port)

// Cert is valid!
if result == 0 {
tab.secureContentState = .secure
} else if result == Int32.min {
// Cert is valid but should be validated by the system
// Let the system handle it and we'll show an error if the system cannot validate it
try await BraveCertificateUtils.evaluateTrust(serverTrust, for: host)
tab.secureContentState = .secure
} else {
tab.secureContentState = .invalidCert
}
} catch {
tab.secureContentState = .invalidCert
case .serverTrust:
Task {
await tab.updateSecureContentState()
if self.tabManager.selectedTab === tab {
self.updateToolbarSecureContentState(tab.lastKnownSecureContentState)
}

self.updateURLBar()
}
case ._sampledPageTopColor:
updateStatusBarOverlayColor()
Expand Down Expand Up @@ -2026,7 +1902,7 @@ public class BrowserViewController: UIViewController {

updateToolbarCurrentURL(tab.url?.displayURL)
if tabManager.selectedTab === tab {
updateToolbarSecureContentState(tab.secureContentState)
self.updateToolbarSecureContentState(tab.lastKnownSecureContentState)
}

let isPage = tab.url?.isWebPage() ?? false
Expand Down Expand Up @@ -3028,7 +2904,6 @@ extension BrowserViewController: NewTabPageDelegate {
guard let self = self else { return }

let viewRect = CGRect(origin: self.view.center, size: .zero)

self.presentActivityViewController(
url, sourceView: self.view, sourceRect: viewRect,
arrowDirection: .any)
Expand Down
74 changes: 65 additions & 9 deletions ios/brave-ios/Sources/Brave/Frontend/Browser/Tab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import Foundation
import WebKit
import Storage
import Shared
import BraveCore
import Preferences
import SwiftyJSON
import Data
import os.log
import BraveWallet
import CertificateUtilities
import Data
import Favicon
import Foundation
import Preferences
import Shared
import Storage
import SwiftyJSON
import UserAgent
import WebKit
import os.log

protocol TabContentScriptLoader {
static func loadUserScript(named: String) -> String?
Expand Down Expand Up @@ -93,7 +94,62 @@ class Tab: NSObject {
return type.isPrivate
}

var secureContentState: TabSecureContentState = .unknown
private(set) var lastKnownSecureContentState: TabSecureContentState = .unknown
func updateSecureContentState() async {
lastKnownSecureContentState = await secureContentState
}
@MainActor private var secureContentState: TabSecureContentState {
get async {
guard let webView = webView, let committedURL = self.committedURL else {
return .unknown
}
if let internalURL = InternalURL(committedURL), internalURL.isAboutHomeURL {
// New Tab Page is a special case, should be treated as `unknown` instead of `localhost`
return .unknown
}
if webView.url != committedURL {
// URL has not been committed yet, so we will not evaluate the secure status of the page yet
return lastKnownSecureContentState
}
if let internalURL = InternalURL(committedURL) {
if internalURL.isErrorPage, ErrorPageHelper.certificateError(for: committedURL) != 0 {
return .invalidCert
}
return .localhost
}
if committedURL.scheme?.lowercased() == "http" {
return .missingSSL
}
if let serverTrust = webView.serverTrust,
case let origin = committedURL.origin, !origin.isOpaque
{
let isMixedContent = !webView.hasOnlySecureContent
let result = await BraveCertificateUtils.verifyTrust(
serverTrust,
host: origin.host,
port: Int(origin.port)
)
switch result {
case 0:
// Cert is valid
return isMixedContent ? .mixedContent : .secure
case Int(Int32.min):
// Cert is valid but should be validated by the system
// Let the system handle it and we'll show an error if the system cannot validate it
do {
try await BraveCertificateUtils.evaluateTrust(serverTrust, for: origin.host)
return isMixedContent ? .mixedContent : .secure
} catch {
return .invalidCert
}
default:
return .invalidCert
}
}
return .unknown
}
}

var sslPinningError: Error?

private let _syncTab: BraveSyncTab?
Expand Down

0 comments on commit 6c76fe4

Please # to comment.