Skip to content
Merged
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
37 changes: 33 additions & 4 deletions BrowserKit/Sources/WebEngine/Utilities/URLFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public final class DefaultURLFormatter: URLFormatter {
// Handle the entry if it has a scheme, make sure it's safe before browsing to it
private func handleWithScheme(with entry: String) -> URL? {
// Check if the URL includes a scheme
guard let url = URL(string: entry),
guard let url = handleURL(with: entry),
url.scheme != nil,
entry.range(of: "\\b:[0-9]{1,5}", options: .regularExpression) == nil else {
return nil
Expand All @@ -73,17 +73,46 @@ public final class DefaultURLFormatter: URLFormatter {
}
}

guard let url = URL(string: entry) else { return nil }

// Only allow this URL if it's safe
let browsingContext = BrowsingContext(type: .internalNavigation, url: url)
if securityManager.canNavigateWith(browsingContext: browsingContext) == .allowed {
return URL(string: entry)
return url
} else {
return nil
}
}

private func handleURL(with entry: String) -> URL? {
// Per Apple docs, for apps linked on or after iOS 17 and aligned OS versions, URL parsing has updated
// from the obsolete RFC 1738/1808 parsing to the same RFC 3986 parsing as URLComponents.
// iOS 17+, URL automatically percent- and IDNA-encodes invalid characters to help create a valid URL.

if #available(iOS 17, *) {
return URL(string: entry)
} else {
// We only want to do percent encoding on our other schemes
let hasHTTPScheme = entry.hasPrefix("http://") || entry.hasPrefix("https://")
guard !hasHTTPScheme else { return URL(string: entry) }

// FXIOS-14375 - Manually add percent encoding since it's not handled by URL(string:) in < iOS 17
guard let escapedURL = entry.addingPercentEncoding(withAllowedCharacters: urlAllowed) else {
return nil
}
guard let finalURL = URL(string: escapedURL), schemeIsValid(for: finalURL) else { return nil }
return finalURL
}
}

/**
Returns whether the URL's scheme is one of those listed on the official list of URI schemes.
This only accepts permanent schemes: historical and provisional schemes are not accepted.
*/
func schemeIsValid(for url: URL) -> Bool {
guard let scheme = url.scheme else { return false }
return SchemesDefinition.permanentURISchemes.contains(scheme.lowercased())
&& url.absoluteString.lowercased() != scheme + ":"
}

// Handle the entry if it has no scheme
private func handleNoScheme(with entry: String) -> URL? {
// First trim white spaces
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,7 @@ final class URLFormatterTests: XCTestCase {
XCTAssertEqual(result?.absoluteString, initialUrl)
}

func testGetURLGivenAboutConfigSpaceURLThenValidEscapedURL_foriOS17AndBelow() throws {
if #available(iOS 17, *) {
throw XCTSkip("Skipping since test does not apply for iOS 17 and above versions")
} else {
let initialUrl = "about:%20config"
let subject = DefaultURLFormatter()
let result = subject.getURL(entry: initialUrl)
XCTAssertEqual(result?.absoluteString, "about:%20config")
}
}

func testGetURLGivenAboutConfigSpaceURLThenValidEscapedURL_foriOS17AndAbove() throws {
guard #available(iOS 17, *) else {
throw XCTSkip("Skipping unless iOS 17 and above versions")
}
func testGetURLGivenAboutConfigSpaceURLThenValidEscapedURL() throws {
let initialUrl = "about: config"
let subject = DefaultURLFormatter()
let result = subject.getURL(entry: initialUrl)
Expand Down