Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36a8f0b97b | ||
|
|
33b9cab8a8 | ||
|
|
b88874b38b | ||
|
|
f55f3ea32d | ||
|
|
c843bd76a2 | ||
|
|
4dd2339ed8 | ||
|
|
280526bef4 | ||
|
|
34caffd4a7 | ||
|
|
9e19b457e2 | ||
|
|
e6846953b7 | ||
|
|
6d78aeac7b |
@@ -1397,7 +1397,7 @@
|
|||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = main/main.entitlements;
|
CODE_SIGN_ENTITLEMENTS = main/main.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 28;
|
CURRENT_PROJECT_VERSION = 30;
|
||||||
INFOPLIST_FILE = main/Info.plist;
|
INFOPLIST_FILE = main/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -1416,7 +1416,7 @@
|
|||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = main/main.entitlements;
|
CODE_SIGN_ENTITLEMENTS = main/main.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 28;
|
CURRENT_PROJECT_VERSION = 30;
|
||||||
INFOPLIST_FILE = main/Info.plist;
|
INFOPLIST_FILE = main/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -1435,7 +1435,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements;
|
CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 28;
|
CURRENT_PROJECT_VERSION = 30;
|
||||||
INFOPLIST_FILE = GlassVPN/Info.plist;
|
INFOPLIST_FILE = GlassVPN/Info.plist;
|
||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.0.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN";
|
PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN";
|
||||||
@@ -1453,7 +1453,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements;
|
CODE_SIGN_ENTITLEMENTS = GlassVPN/GlassVPN.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 28;
|
CURRENT_PROJECT_VERSION = 30;
|
||||||
INFOPLIST_FILE = GlassVPN/Info.plist;
|
INFOPLIST_FILE = GlassVPN/Info.plist;
|
||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.0.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN";
|
PRODUCT_BUNDLE_IDENTIFIER = "de.uni-bamberg.psi.AppCheck.VPN";
|
||||||
|
|||||||
@@ -53,3 +53,5 @@ That means, AppCheck does not have to be active in the foreground all the time.
|
|||||||
|
|
||||||
*information will be added soon™*
|
*information will be added soon™*
|
||||||
|
|
||||||
|
For now, go to the results page at [https://appchk.de/](https://appchk.de/).
|
||||||
|
Btw. we are searching for [help](https://appchk.de/help/) on our ongoing research project.
|
||||||
|
|||||||
@@ -30,14 +30,16 @@ struct NotificationBanner {
|
|||||||
img.tintColor = fg
|
img.tintColor = fg
|
||||||
view.addSubview(lbl)
|
view.addSubview(lbl)
|
||||||
view.addSubview(img)
|
view.addSubview(img)
|
||||||
img.anchor([.centerY], to: view.layoutMarginsGuide)
|
img.anchor([.centerY], to: lbl)
|
||||||
lbl.anchor([.top, .bottom, .trailing], to: view.layoutMarginsGuide)
|
lbl.anchor([.bottom, .trailing], to: view.layoutMarginsGuide)
|
||||||
img.widthAnchor =&= 25
|
img.widthAnchor =&= 25
|
||||||
img.heightAnchor =&= 25
|
img.heightAnchor =&= 25
|
||||||
if #available(iOS 11, *) {
|
if #available(iOS 11, *) {
|
||||||
img.leadingAnchor =&= view.layoutMarginsGuide.leadingAnchor
|
img.leadingAnchor =&= view.layoutMarginsGuide.leadingAnchor
|
||||||
|
lbl.topAnchor =&= view.layoutMarginsGuide.topAnchor
|
||||||
} else {
|
} else {
|
||||||
img.leadingAnchor =&= view.leadingAnchor + 8
|
img.leadingAnchor =&= view.leadingAnchor + 8
|
||||||
|
lbl.topAnchor =&= view.topAnchor + 8
|
||||||
}
|
}
|
||||||
lbl.leadingAnchor =&= img.trailingAnchor + 8
|
lbl.leadingAnchor =&= img.trailingAnchor + 8
|
||||||
img.bottomAnchor =<= view.bottomAnchor - 8 | .init(rawValue: 999)
|
img.bottomAnchor =<= view.bottomAnchor - 8 | .init(rawValue: 999)
|
||||||
|
|||||||
@@ -405,7 +405,7 @@ extension SQLiteDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func appBundleList() -> [AppBundleInfo]? {
|
func appBundleList() -> [AppBundleInfo]? {
|
||||||
try? run(sql: "SELECT appid, title, subtitle FROM rec WHERE appid IS NOT NULL GROUP BY appid ORDER BY title ASC;") {
|
try? run(sql: "SELECT appid, title, subtitle FROM rec WHERE appid IS NOT NULL GROUP BY appid ORDER BY lower(title) ASC;") {
|
||||||
allRows($0) {
|
allRows($0) {
|
||||||
AppBundleInfo(col_text($0, 0)!, col_text($0, 1), col_text($0, 2))
|
AppBundleInfo(col_text($0, 0)!, col_text($0, 1), col_text($0, 2))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,11 +33,13 @@ extension FilterOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension Recording {
|
extension Recording {
|
||||||
|
static let minTimeLongTerm: Timestamp = .hours(1)
|
||||||
|
|
||||||
var fallbackTitle: String { get {
|
var fallbackTitle: String { get {
|
||||||
isLongTerm ? "Background Recording" : "Unnamed Recording #\(id)"
|
isLongTerm ? "Background Recording" : "Unnamed Recording #\(id)"
|
||||||
} }
|
} }
|
||||||
var duration: Timestamp { get { (stop ?? .now()) - start } }
|
var duration: Timestamp { get { (stop ?? .now()) - start } }
|
||||||
var isLongTerm: Bool { duration > Timestamp.hours(1) }
|
var isLongTerm: Bool { duration > Recording.minTimeLongTerm }
|
||||||
var isShared: Bool { uploadkey?.count ?? 0 > 0}
|
var isShared: Bool { uploadkey?.count ?? 0 > 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,14 +87,21 @@ struct TimeFormat {
|
|||||||
|
|
||||||
/// Duration string with format `mm:ss` or `mm:ss.SSS`
|
/// Duration string with format `mm:ss` or `mm:ss.SSS`
|
||||||
static func from(_ duration: TimeInterval, millis: Bool = false, hours: Bool = false) -> String {
|
static func from(_ duration: TimeInterval, millis: Bool = false, hours: Bool = false) -> String {
|
||||||
let t = Int(duration)
|
var t = Int(duration)
|
||||||
let min = t / 60
|
var min = t / 60
|
||||||
let sec = t % 60
|
var sec = t % 60
|
||||||
if millis {
|
if millis {
|
||||||
let mil = Int(duration * 1000) % 1000
|
let mil = Int(duration * 1000) % 1000
|
||||||
return String(format: "%02d:%02d.%03d", min, sec, mil)
|
return String(format: "%02d:%02d.%03d", min, sec, mil)
|
||||||
} else if hours {
|
} else if hours {
|
||||||
return String(format: "%02d:%02d:%02d", min / 60, min % 60, sec)
|
if t < Recording.minTimeLongTerm {
|
||||||
|
t = Int(Recording.minTimeLongTerm) - t
|
||||||
|
min = t / 60
|
||||||
|
sec = t % 60
|
||||||
|
return String(format: "-%02d:%02d:%02d", min / 60, min % 60, sec)
|
||||||
|
} else {
|
||||||
|
return String(format: "%02d:%02d:%02d", min / 60, min % 60, sec)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return String(format: "%02d:%02d", min, sec)
|
return String(format: "%02d:%02d", min, sec)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="hm5-7q-Zfi">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="hm5-7q-Zfi">
|
||||||
<device id="retina4_0" orientation="portrait" appearance="light"/>
|
<device id="retina4_0" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
@@ -128,6 +128,7 @@
|
|||||||
<outlet property="buttonView" destination="La3-9e-6TK" id="UMg-xx-6OV"/>
|
<outlet property="buttonView" destination="La3-9e-6TK" id="UMg-xx-6OV"/>
|
||||||
<outlet property="headerView" destination="ppJ-js-Wwz" id="68u-8M-R2Q"/>
|
<outlet property="headerView" destination="ppJ-js-Wwz" id="68u-8M-R2Q"/>
|
||||||
<outlet property="runningView" destination="9Yj-FX-eFd" id="L2C-YR-2HN"/>
|
<outlet property="runningView" destination="9Yj-FX-eFd" id="L2C-YR-2HN"/>
|
||||||
|
<outlet property="startSegment" destination="2MI-6l-YQt" id="Jun-ct-Xag"/>
|
||||||
<outlet property="stopButton" destination="vAq-EZ-Gmx" id="XiW-1H-I9y"/>
|
<outlet property="stopButton" destination="vAq-EZ-Gmx" id="XiW-1H-I9y"/>
|
||||||
<outlet property="timeLabel" destination="rbR-np-cXD" id="EEe-8F-HT6"/>
|
<outlet property="timeLabel" destination="rbR-np-cXD" id="EEe-8F-HT6"/>
|
||||||
</connections>
|
</connections>
|
||||||
@@ -656,6 +657,7 @@ Duration: 60:00</string>
|
|||||||
<outlet property="appIcon" destination="rbW-pK-Kct" id="VlO-fG-y1a"/>
|
<outlet property="appIcon" destination="rbW-pK-Kct" id="VlO-fG-y1a"/>
|
||||||
<outlet property="appTitle" destination="Et0-8d-CId" id="HgD-oI-0J8"/>
|
<outlet property="appTitle" destination="Et0-8d-CId" id="HgD-oI-0J8"/>
|
||||||
<outlet property="buttonCancel" destination="TGg-60-wZW" id="5Ej-7t-jaD"/>
|
<outlet property="buttonCancel" destination="TGg-60-wZW" id="5Ej-7t-jaD"/>
|
||||||
|
<outlet property="buttonFilter" destination="LOr-e7-foG" id="qUx-1k-xJK"/>
|
||||||
<outlet property="buttonSave" destination="rWg-hE-Ydl" id="zfM-kx-erX"/>
|
<outlet property="buttonSave" destination="rWg-hE-Ydl" id="zfM-kx-erX"/>
|
||||||
<outlet property="chooseAppTap" destination="Jab-q2-U9X" id="Tzv-lm-sUm"/>
|
<outlet property="chooseAppTap" destination="Jab-q2-U9X" id="Tzv-lm-sUm"/>
|
||||||
<outlet property="inputDetails" destination="pql-H5-k6U" id="NXm-8f-5E6"/>
|
<outlet property="inputDetails" destination="pql-H5-k6U" id="NXm-8f-5E6"/>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class TVCShareRecording : UITableViewController, UITextViewDelegate, VCEditTextD
|
|||||||
|
|
||||||
// vars
|
// vars
|
||||||
var record: Recording!
|
var record: Recording!
|
||||||
private var shareNotes: Bool = false // opt-in
|
private var shareNotes: Bool = true // green switch is more present
|
||||||
private lazy var hasNotes: Bool = (self.record.notes != nil)
|
private lazy var hasNotes: Bool = (self.record.notes != nil)
|
||||||
private lazy var editedNotes: String = self.record.notes ?? ""
|
private lazy var editedNotes: String = self.record.notes ?? ""
|
||||||
private lazy var weekInYear: String = {
|
private lazy var weekInYear: String = {
|
||||||
@@ -86,6 +86,13 @@ class TVCShareRecording : UITableViewController, UITextViewDelegate, VCEditTextD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
|
||||||
|
if motion == .motionShake, let key = record.uploadkey {
|
||||||
|
UIPasteboard.general.string = key
|
||||||
|
banner(.ok, "Copied to clipboard", timeout: 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Table Data Source
|
// MARK: - Table Data Source
|
||||||
|
|
||||||
@@ -145,7 +152,7 @@ class TVCShareRecording : UITableViewController, UITextViewDelegate, VCEditTextD
|
|||||||
cell = tableView.dequeueReusableCell(withIdentifier: "shareKeyValueCell")!
|
cell = tableView.dequeueReusableCell(withIdentifier: "shareKeyValueCell")!
|
||||||
let src = dataSourceKeyValue[indexPath.row]
|
let src = dataSourceKeyValue[indexPath.row]
|
||||||
cell.textLabel?.text = src.key
|
cell.textLabel?.text = src.key
|
||||||
let flag = shareNotes && indexPath.row == 4
|
let flag = indexPath.row == 4 && shareNotes && hasNotes
|
||||||
cell.detailTextLabel?.text = flag ? editedNotes : src.value
|
cell.detailTextLabel?.text = flag ? editedNotes : src.value
|
||||||
case 3:
|
case 3:
|
||||||
cell = tableView.dequeueReusableCell(withIdentifier: "shareLogCell")!
|
cell = tableView.dequeueReusableCell(withIdentifier: "shareLogCell")!
|
||||||
@@ -234,8 +241,8 @@ class TVCShareRecording : UITableViewController, UITextViewDelegate, VCEditTextD
|
|||||||
|
|
||||||
// MARK: - Alerts & Banner
|
// MARK: - Alerts & Banner
|
||||||
|
|
||||||
private func banner(_ style: NotificationBanner.Style, _ msg: String) {
|
private func banner(_ style: NotificationBanner.Style, _ msg: String, timeout: TimeInterval = 3) {
|
||||||
NotificationBanner(msg, style: style).present(in: self)
|
NotificationBanner(msg, style: style).present(in: navigationController!, hideAfter: timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func showAlertAvailableSoon(_ urlStr: String, when: Int?) {
|
private func showAlertAvailableSoon(_ urlStr: String, when: Int?) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class VCEditRecording: UIViewController, UITextFieldDelegate, UITextViewDelegate
|
|||||||
@IBOutlet private var noteBottom: NSLayoutConstraint!
|
@IBOutlet private var noteBottom: NSLayoutConstraint!
|
||||||
|
|
||||||
@IBOutlet private var chooseAppTap: UITapGestureRecognizer!
|
@IBOutlet private var chooseAppTap: UITapGestureRecognizer!
|
||||||
|
@IBOutlet private var buttonFilter: UIButton!
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
if deleteOnCancel { // aka newly created
|
if deleteOnCancel { // aka newly created
|
||||||
@@ -23,6 +24,7 @@ class VCEditRecording: UIViewController, UITextFieldDelegate, UITextViewDelegate
|
|||||||
if Prefs.RecordingReminder.Enabled {
|
if Prefs.RecordingReminder.Enabled {
|
||||||
PushNotification.scheduleRecordingReminder(force: true)
|
PushNotification.scheduleRecordingReminder(force: true)
|
||||||
}
|
}
|
||||||
|
buttonFilter.isHidden = true
|
||||||
// mark as destructive
|
// mark as destructive
|
||||||
buttonCancel.tintColor = .systemRed
|
buttonCancel.tintColor = .systemRed
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ class VCRecordings: UIViewController, UINavigationControllerDelegate {
|
|||||||
@IBOutlet private var runningView: UIView!
|
@IBOutlet private var runningView: UIView!
|
||||||
@IBOutlet private var timeLabel: UILabel!
|
@IBOutlet private var timeLabel: UILabel!
|
||||||
@IBOutlet private var stopButton: UIButton!
|
@IBOutlet private var stopButton: UIButton!
|
||||||
|
@IBOutlet private var startSegment: UISegmentedControl!
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
|
startSegment.setTitleTextAttributes([NSAttributedString.Key.foregroundColor : UIColor.sysLink], for: .normal)
|
||||||
timeLabel.font = timeLabel.font.monoSpace()
|
timeLabel.font = timeLabel.font.monoSpace()
|
||||||
if let ongoing = RecordingsDB.getCurrent() {
|
if let ongoing = RecordingsDB.getCurrent() {
|
||||||
currentRecording = ongoing
|
currentRecording = ongoing
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class TVCOccurrenceContext: UITableViewController {
|
|||||||
private var copyDomain: String? = nil
|
private var copyDomain: String? = nil
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
|
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
|
||||||
if cellMenu.start(tableView, indexPath) {
|
if !firstOrLast(indexPath.row), cellMenu.start(tableView, indexPath) {
|
||||||
copyDomain = cellMenu.getSelected(dataSource)?.domain
|
copyDomain = cellMenu.getSelected(dataSource)?.domain
|
||||||
self.becomeFirstResponder()
|
self.becomeFirstResponder()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# What are Recordings?
|
# What are Recordings?
|
||||||
|
|
||||||
Similar to the default logging, recordings will intercept every request and log it for later review. App recordings are usually 3 – 5 minutes long and cover a single application. You can utilize recordings for App analysis or to get a ground truth on background traffic.
|
Similar to the default logging, recordings will intercept every request and log it for later review. App recordings are usually 1 – 4 minutes long and cover a single application. You can utilize recordings for App analysis or to get a ground truth on background traffic.
|
||||||
|
|
||||||
Optionally, you can help us by providing your app specific recordings. Together with your findings we can create a community driven privacy monitor. The research results will help you and others avoid Apps that unnecessarily share data with third-party providers.
|
Optionally, you can help us by providing your app specific recordings. Together with your findings we can create a community driven privacy monitor. The research results will help you and others avoid Apps that unnecessarily share data with third-party providers.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# How to record?
|
# How to record?
|
||||||
|
|
||||||
Before you begin, there are two types of recordings: app specific recordings and general background activity. The former are usually 3 – 5 minutes long, the latter need to be at least an hour long.
|
Before you begin, there are two types of recordings: app specific recordings and general background activity. The former are usually less than 5 minutes long, the latter must be at least an hour long.
|
||||||
|
|
||||||
### Important notice
|
### Important notice
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user