Skip to content

Commit

Permalink
add cookie debug screen, diagnostic report generation and optimise co…
Browse files Browse the repository at this point in the history
…okie deletion (#913)
  • Loading branch information
brindy authored Jul 1, 2021
1 parent b9aae13 commit 73ebe2c
Show file tree
Hide file tree
Showing 8 changed files with 459 additions and 43 deletions.
2 changes: 1 addition & 1 deletion Core/ContentBlockerRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public class ContentBlockerRequest: ContentBlockerRemoteDataSource {
case success(etag: String?, data: Data)
}

public enum Configuration: String {
public enum Configuration: String, CaseIterable {
case httpsBloomFilterSpec
case httpsBloomFilter
case httpsExcludedDomains
Expand Down
16 changes: 5 additions & 11 deletions Core/WebCacheManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,10 @@ public class WebCacheManager {
cookieStore.getAllCookies { cookies in
let group = DispatchGroup()
cookies.forEach { cookie in
domains.forEach { domain in

if self.isDuckDuckGoOrAllowedDomain(cookie: cookie, domain: domain) {
group.enter()
cookieStore.delete(cookie) {
group.leave()
}

// don't try to delete the cookie twice as it doesn't always work (esecially on the simulator)
return
if domains.contains(where: { self.isCookie(cookie, matchingDomain: $0)}) {
group.enter()
cookieStore.delete(cookie) {
group.leave()
}
}
}
Expand Down Expand Up @@ -168,7 +162,7 @@ public class WebCacheManager {
/// The Fire Button does not delete the user's DuckDuckGo search settings, which are saved as cookies. Removing these cookies would reset them and have undesired
/// consequences, i.e. changing the theme, default language, etc. These cookies are not stored in a personally identifiable way. For example, the large size setting
/// is stored as 's=l.' More info in https://duckduckgo.com/privacy
private func isDuckDuckGoOrAllowedDomain(cookie: HTTPCookie, domain: String) -> Bool {
private func isCookie(_ cookie: HTTPCookie, matchingDomain domain: String) -> Bool {
return cookie.domain == domain || (cookie.domain.hasPrefix(".") && domain.hasSuffix(cookie.domain))
}

Expand Down
14 changes: 13 additions & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,10 @@
8588026624E420BD00C24AB6 /* LargeOmniBarStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8588026424E4209900C24AB6 /* LargeOmniBarStateTests.swift */; };
8588026A24E424EE00C24AB6 /* AppWidthObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8588026824E424AF00C24AB6 /* AppWidthObserverTests.swift */; };
8589CF2624AE075F006C6457 /* UIImageViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850365F223DE087800D0F787 /* UIImageViewExtension.swift */; };
8590CB612684D0600089F6BF /* CookieDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590CB602684D0600089F6BF /* CookieDebugViewController.swift */; };
8590CB632684F10F0089F6BF /* ContentBlockerProtectionStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590CB622684F10F0089F6BF /* ContentBlockerProtectionStoreTests.swift */; };
8590CB67268A2E520089F6BF /* RootDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590CB66268A2E520089F6BF /* RootDebugViewController.swift */; };
8590CB69268A4E190089F6BF /* DebugEtagStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590CB68268A4E190089F6BF /* DebugEtagStorage.swift */; };
85923C401FD2F8D10097204B /* PrivacyProtectionTrackerNetworksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85923C3F1FD2F8D10097204B /* PrivacyProtectionTrackerNetworksTests.swift */; };
8598F67B2405EB8D00FBC70C /* KeyboardSettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8598F6792405EB8600FBC70C /* KeyboardSettingsTests.swift */; };
859C5FB82147E6CE00E2A43D /* grade-cases.json in Resources */ = {isa = PBXBuildFile; fileRef = 859C5FB72147E6CE00E2A43D /* grade-cases.json */; };
Expand Down Expand Up @@ -933,7 +936,10 @@
8586A10F24CCCD040049720E /* TabsBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsBarViewController.swift; sourceTree = "<group>"; };
8588026424E4209900C24AB6 /* LargeOmniBarStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeOmniBarStateTests.swift; sourceTree = "<group>"; };
8588026824E424AF00C24AB6 /* AppWidthObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppWidthObserverTests.swift; sourceTree = "<group>"; };
8590CB602684D0600089F6BF /* CookieDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieDebugViewController.swift; sourceTree = "<group>"; };
8590CB622684F10F0089F6BF /* ContentBlockerProtectionStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBlockerProtectionStoreTests.swift; sourceTree = "<group>"; };
8590CB66268A2E520089F6BF /* RootDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootDebugViewController.swift; sourceTree = "<group>"; };
8590CB68268A4E190089F6BF /* DebugEtagStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugEtagStorage.swift; sourceTree = "<group>"; };
85923C3F1FD2F8D10097204B /* PrivacyProtectionTrackerNetworksTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyProtectionTrackerNetworksTests.swift; sourceTree = "<group>"; };
8598F6792405EB8600FBC70C /* KeyboardSettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardSettingsTests.swift; sourceTree = "<group>"; };
859C5FB72147E6CE00E2A43D /* grade-cases.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "grade-cases.json"; path = "submodules/privacy-grade/test/data/grade-cases.json"; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -2652,8 +2658,11 @@
isa = PBXGroup;
children = (
858566E7252E4F56007501B8 /* Debug.storyboard */,
858566FA252E55D6007501B8 /* ImageCacheDebugViewController.swift */,
8590CB602684D0600089F6BF /* CookieDebugViewController.swift */,
4B0295182537BC6700E00CEF /* ConfigurationDebugViewController.swift */,
858566FA252E55D6007501B8 /* ImageCacheDebugViewController.swift */,
8590CB66268A2E520089F6BF /* RootDebugViewController.swift */,
8590CB68268A4E190089F6BF /* DebugEtagStorage.swift */,
);
name = Debug;
sourceTree = "<group>";
Expand Down Expand Up @@ -4720,6 +4729,7 @@
9881439C23326DC200573F7C /* ThemeSettingsViewController.swift in Sources */,
8540BD5623D9E9C20057FDD2 /* PreserveLoginsSettingsViewController.swift in Sources */,
F1668BCE1E798081008CBA04 /* BookmarksViewController.swift in Sources */,
8590CB69268A4E190089F6BF /* DebugEtagStorage.swift in Sources */,
F1CA3C371F045878005FADB3 /* PrivacyStore.swift in Sources */,
F4E1936625AF722F001D2666 /* HighlightCutOutView.swift in Sources */,
9874F9EE2187AFCE00CAF33D /* Themable.swift in Sources */,
Expand All @@ -4728,6 +4738,7 @@
984D035824ACCC6F0066CFB8 /* TabViewListCell.swift in Sources */,
AA3D854923DA1DFB00788410 /* AppIcon.swift in Sources */,
857D1DC9223A9E7D00833940 /* MockAppSettings.swift in Sources */,
8590CB612684D0600089F6BF /* CookieDebugViewController.swift in Sources */,
83134D7D20E2D725006CE65D /* FeedbackSender.swift in Sources */,
855D914D2063EF6A00C4B448 /* TabSwitcherTransition.swift in Sources */,
8539D9F71FA756AD00BE8746 /* PrivacyProtectionController.swift in Sources */,
Expand Down Expand Up @@ -4891,6 +4902,7 @@
AA3D854723D9E88E00788410 /* AppIconSettingsCell.swift in Sources */,
9838059F2228208E00385F1A /* PositiveFeedbackViewController.swift in Sources */,
F1AB2B421E3F7D5C00868554 /* SettingsViewController.swift in Sources */,
8590CB67268A2E520089F6BF /* RootDebugViewController.swift in Sources */,
85200F911FBA38E2001AF290 /* PrivacyProtectionScoreCardController.swift in Sources */,
F16390821E648B7A005B4550 /* HomeViewController.swift in Sources */,
98F3A1DA217B37200011A0D4 /* LightTheme.swift in Sources */,
Expand Down
19 changes: 3 additions & 16 deletions DuckDuckGo/ConfigurationDebugViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class ConfigurationDebugViewController: UITableViewController {
@UserDefaultsWrapper(key: .lastConfigurationRefreshDate, defaultValue: .distantPast)
private var lastConfigurationRefreshDate: Date
private var queuedTasks: [BGTaskRequest] = []
private let etagStorage = DebugEtagStorage()

private let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
Expand Down Expand Up @@ -123,7 +124,7 @@ class ConfigurationDebugViewController: UITableViewController {
let row = ETagRows.allCases[indexPath.row]
cell.textLabel?.text = row.rawValue

if let etag = etag(for: row) {
if let etag = etagStorage.etag(for: row.rawValue) {
cell.detailTextLabel?.text = etag
} else {
cell.detailTextLabel?.text = row.showDetail ? "None" : nil
Expand Down Expand Up @@ -156,26 +157,12 @@ class ConfigurationDebugViewController: UITableViewController {
case .etags:
switch ETagRows.allCases[indexPath.row] {
case .resetEtags:
resetETags()
etagStorage.resetAll()
tableView.reloadData()
default: break
}
default: break
}
}

// MARK: - ETag User Defaults Wrapper

lazy var defaults = UserDefaults(suiteName: "com.duckduckgo.blocker-list.etags")

private func etag(for config: ETagRows) -> String? {
return defaults?.string(forKey: config.rawValue)
}

private func resetETags() {
for row in ETagRows.allCases {
defaults?.removeObject(forKey: row.rawValue)
}
}

}
98 changes: 98 additions & 0 deletions DuckDuckGo/CookieDebugViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//
// CookieDebugViewController.swift
// DuckDuckGo
//
// Copyright © 2021 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 UIKit
import WebKit
import Core

class CookieDebugViewController: UITableViewController {

struct DomainCookies {

let domain: String
let cookies: [HTTPCookie]

}

var cookies = [DomainCookies]() {
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}

var loaded = false

override func viewDidLoad() {
super.viewDidLoad()
fetchCookies()
}

private func fetchCookies() {
WKWebsiteDataStore.default().cookieStore?.getAllCookies(displayCookies)
}

private func displayCookies(cookies: [HTTPCookie]) {
self.loaded = true

var tmp = [DomainCookies]()
let domains = Set<String>(cookies.map { $0.domain })
for domain in domains.sorted(by: { String($0.reversed()) < String($1.reversed()) }) {
let domainCookies = [HTTPCookie](cookies
.filter({ $0.domain == domain })
.sorted(by: { $0.name.lowercased() < $1.name.lowercased() })
.sorted(by: { $0.path.lowercased() < $1.path.lowercased() })
.reversed())

let domainName = domain +
(PreserveLogins.shared.isAllowed(cookieDomain: domain) ? " 👩‍🚒" : "")

tmp.append(DomainCookies(domain: domainName, cookies: domainCookies))
}
self.cookies = tmp
}

override func numberOfSections(in tableView: UITableView) -> Int {
return cookies.isEmpty ? 1 : cookies.count
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cookies.isEmpty ? 1 : cookies[section].cookies.count
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return cookies.isEmpty ? "" : cookies[section].domain
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell
if cookies.isEmpty {
cell = tableView.dequeueReusableCell(withIdentifier: "Info")!
cell.textLabel?.text = loaded ? "No cookies" : "Loading"
} else {
let cookie = cookies[indexPath.section].cookies[indexPath.row]
cell = tableView.dequeueReusableCell(withIdentifier: "Cookie")!
cell.textLabel?.text = cookie.path + " " + cookie.name + "=" + cookie.value
cell.detailTextLabel?.text = cookie.expiresDate == nil ? nil : String(describing: cookie.expiresDate!)
}
return cell
}

}
Loading

0 comments on commit 73ebe2c

Please sign in to comment.