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

support local storage fireproofing #3612

Merged
merged 18 commits into from
Dec 3, 2024
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
72 changes: 72 additions & 0 deletions .maestro/release_tests/local-storage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# local-storage.yaml
appId: com.duckduckgo.mobile.ios
tags:
- release

---

# Set up
- runFlow:
file: ../shared/setup.yaml

# Load Site
- assertVisible:
id: "searchEntry"
- tapOn:
id: "searchEntry"
- inputText: "https://privacy-test-pages.site/features/local-storage.html"
- pressKey: Enter

# Add a cookie
- assertVisible: "Storage Counter: undefined"
- assertVisible: "Cookie Counter:"
- assertNotVisible: "Cookie Counter: 1"
- assertNotVisible: "Storage Counter: 1"
- assertVisible: "Manual Increment"
- tapOn: "Manual Increment"
- tapOn: "Manual Increment"
- assertVisible: "Cookie Counter: 2"
- assertVisible: "Storage Counter: 2"

# Fireproofing
- tapOn: "Browsing menu"
- tapOn: "Fireproof This Site"
- tapOn: "Fireproof"

# Fire button
- tapOn: "Close Tabs and Clear Data"
- tapOn: "Close Tabs and Clear Data"

- assertNotVisible: "https://privacy-test-pages.site/features/local-storage.html"

# Load Site
- assertVisible:
id: "searchEntry"
- tapOn:
id: "searchEntry"

- inputText: "https://privacy-test-pages.site/features/local-storage.html"
- pressKey: Enter
- assertVisible: "Storage Counter: 2"
- assertVisible: "Cookie Counter: 2"

# Remove Fireproofing
- tapOn: "Browsing menu"
- tapOn: "Remove Fireproofing"

# Fire button
- tapOn: "Close Tabs and Clear Data"
- tapOn: "Close Tabs and Clear Data"

- assertNotVisible: "https://privacy-test-pages.site/features/local-storage.html"

# Load Site
- assertVisible:
id: "searchEntry"
- tapOn:
id: "searchEntry"

- inputText: "https://privacy-test-pages.site/features/local-storage.html"
- pressKey: Enter
- assertVisible: "Storage Counter: undefined"
- assertVisible: "Cookie Counter:"
2 changes: 1 addition & 1 deletion .maestro/setup_ui_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ target_os="iOS-18-1"
check_maestro() {

local command_name="maestro"
local known_version="1.39.1"
local known_version="1.39.2"

if command -v $command_name > /dev/null 2>&1; then
local version_output=$($command_name -v 2>&1 | tail -n 1)
Expand Down
157 changes: 0 additions & 157 deletions Core/CookieStorage.swift

This file was deleted.

57 changes: 57 additions & 0 deletions Core/DataStoreIDManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// DataStoreIDManager.swift
// DuckDuckGo
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//


import WebKit
import Persistence

/// Supports an existing ID set in previous versions of the app, but moving forward does not allocate an ID. We have gone back to using the default
/// peristence for the webview storage so that we can fireproof types that don't have an API for accessing their data. (e.g. localStorage)
public protocol DataStoreIDManaging {

var currentID: UUID? { get }

func invalidateCurrentID()
}

public class DataStoreIDManager: DataStoreIDManaging {

enum Constants: String {
case currentWebContainerID = "com.duckduckgo.ios.webcontainer.id"
}

public static let shared = DataStoreIDManager()

private let store: KeyValueStoring
init(store: KeyValueStoring = UserDefaults.app) {
self.store = store
}

public var currentID: UUID? {
guard let uuidString = store.object(forKey: Constants.currentWebContainerID.rawValue) as? String else {
return nil
}
return UUID(uuidString: uuidString)
}

public func invalidateCurrentID() {
store.removeObject(forKey: Constants.currentWebContainerID.rawValue)
}

}
19 changes: 13 additions & 6 deletions Core/Fireproofing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
//

import Foundation
import Subscription

public protocol Fireproofing {

Expand All @@ -35,7 +36,9 @@ public protocol Fireproofing {
// This class is not final because we override allowed domains in WebCacheManagerTests
public class UserDefaultsFireproofing: Fireproofing {

public static let shared: Fireproofing = UserDefaultsFireproofing()
/// This is only here because there are some places that don't support injection at this time. DO NOT USE IT.
/// If you find you really need to use it, ping Apple Devs channel first.
public static let xshared: Fireproofing = UserDefaultsFireproofing()

public struct Notifications {
public static let loginDetectionStateChanged = Foundation.Notification.Name("com.duckduckgo.ios.PreserveLogins.loginDetectionStateChanged")
Expand All @@ -51,15 +54,19 @@ public class UserDefaultsFireproofing: Fireproofing {
}
}

private var allowedDomainsIncludingDuckDuckGo: [String] {
allowedDomains + [
URL.ddg.host ?? "",
SubscriptionCookieManager.cookieDomain
]
}

public func addToAllowed(domain: String) {
allowedDomains += [domain]
}

public func isAllowed(cookieDomain: String) -> Bool {

return allowedDomains.contains(where: { $0 == cookieDomain
|| ".\($0)" == cookieDomain
|| (cookieDomain.hasPrefix(".") && $0.hasSuffix(cookieDomain)) })
return allowedDomainsIncludingDuckDuckGo.contains(where: { HTTPCookie.cookieDomain(cookieDomain, matchesTestDomain: $0) })
}

public func remove(domain: String) {
Expand All @@ -71,7 +78,7 @@ public class UserDefaultsFireproofing: Fireproofing {
}

public func isAllowed(fireproofDomain domain: String) -> Bool {
return allowedDomains.contains(domain)
return allowedDomainsIncludingDuckDuckGo.contains(domain)
}

}
35 changes: 35 additions & 0 deletions Core/HTTPCookieExtension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// HTTPCookieExtension.swift
// DuckDuckGo
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import WebKit

extension HTTPCookie {

/// Checks that the `cookieDomain` (provided by a cookie) matches a given domain. e.g.
/// * cookie domain example.com would match a domain called example.com
/// * cookie domain `.example.com` would also match a domain called example.com and also any subdomain, e.g. `docs.example.com`
///
/// See `UserDefaultsFireproofingTests` for more examples.
static func cookieDomain(_ cookieDomain: String, matchesTestDomain testDomain: String) -> Bool {
return testDomain == cookieDomain
|| ".\(testDomain)" == cookieDomain
|| (cookieDomain.hasPrefix(".") && testDomain.hasSuffix(cookieDomain))
}
Comment on lines +29 to +33
Copy link
Contributor

Choose a reason for hiding this comment

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

could you provide a piece of documentation what do we test here for and possible e.g of what matches means for us

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added comment and note to look at some unit tests


}
Loading
Loading