Skip to content

Commit

Permalink
Add prompt to confirm that the user trusts the origin before download…
Browse files Browse the repository at this point in the history
…ing (#11)
  • Loading branch information
lfroms authored Aug 26, 2024
1 parent 2a3d302 commit 68abb63
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 1 deletion.
8 changes: 8 additions & 0 deletions Tophat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
804ECB7C2975C15300DE78F4 /* DevicePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804ECB7B2975C15300DE78F4 /* DevicePicker.swift */; };
804ECB7E2975C18300DE78F4 /* DeviceMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804ECB7D2975C18300DE78F4 /* DeviceMenu.swift */; };
804ECB802975C68400DE78F4 /* VisibleWhenButtonHoveredViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804ECB7F2975C68400DE78F4 /* VisibleWhenButtonHoveredViewModifier.swift */; };
804F37F92C7CE46F0005A869 /* HostTrustResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804F37F82C7CE46F0005A869 /* HostTrustResult.swift */; };
804F37FD2C7CEFB00005A869 /* TrustedHostAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804F37FC2C7CEFB00005A869 /* TrustedHostAlert.swift */; };
804FF6592914239800147652 /* Collection+Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804FF6582914239800147652 /* Collection+Filter.swift */; };
804FFE1829C3BEA5002B64AA /* BadgedURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804FFE1729C3BEA5002B64AA /* BadgedURL.swift */; };
80518F4F2984600900FB8803 /* Apps+Add.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80518F4D29845FB100FB8803 /* Apps+Add.swift */; };
Expand Down Expand Up @@ -262,6 +264,8 @@
804ECB7B2975C15300DE78F4 /* DevicePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DevicePicker.swift; sourceTree = "<group>"; };
804ECB7D2975C18300DE78F4 /* DeviceMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceMenu.swift; sourceTree = "<group>"; };
804ECB7F2975C68400DE78F4 /* VisibleWhenButtonHoveredViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisibleWhenButtonHoveredViewModifier.swift; sourceTree = "<group>"; };
804F37F82C7CE46F0005A869 /* HostTrustResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostTrustResult.swift; sourceTree = "<group>"; };
804F37FC2C7CEFB00005A869 /* TrustedHostAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrustedHostAlert.swift; sourceTree = "<group>"; };
804FF6582914239800147652 /* Collection+Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+Filter.swift"; sourceTree = "<group>"; };
804FFE1729C3BEA5002B64AA /* BadgedURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgedURL.swift; sourceTree = "<group>"; };
80518F4D29845FB100FB8803 /* Apps+Add.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Apps+Add.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -711,6 +715,7 @@
809BD03D290CA40900FD4043 /* DeviceSelectionManager.swift */,
80A66D6A2981BC9900ECBCB6 /* ErrorNotifier.swift */,
80564B5529834203002DC136 /* FileTypes.swift */,
804F37F82C7CE46F0005A869 /* HostTrustResult.swift */,
80FF03EE29087473008509E0 /* InstallCoordinator.swift */,
80564B4F298340D1002DC136 /* InstallCoordinatorDelegate.swift */,
809C8570297B056F004CE6A2 /* LaunchAppAction.swift */,
Expand All @@ -723,6 +728,7 @@
80A66D692981BC9900ECBCB6 /* PrepareDeviceAction.swift */,
80CBACFE2989B8B100F778DD /* ShowOnboardingWindowAction.swift */,
80518F562984804C00FB8803 /* TophatCtlSymbolicLinkManager.swift */,
804F37FC2C7CEFB00005A869 /* TrustedHostAlert.swift */,
8020A6DD297F301700FEA490 /* URLHandler.swift */,
80A66D732981BD2200ECBCB6 /* UtilityPathPreferences.swift */,
);
Expand Down Expand Up @@ -948,6 +954,7 @@
80EB5D49296F5AAF0011DE5F /* InstallApplicationTask.swift in Sources */,
8006E7E32943C95D0089805E /* MenuItemButtonStyle.swift in Sources */,
8006E7E92943C9B80089805E /* ToggleableRow.swift in Sources */,
804F37FD2C7CEFB00005A869 /* TrustedHostAlert.swift in Sources */,
80CBACF229898FFE00F778DD /* AboutView.swift in Sources */,
80629BF52939818C0077960E /* AppsTab.swift in Sources */,
80D71F262984CF100006E1BF /* OnboardingItemLayout.swift in Sources */,
Expand All @@ -960,6 +967,7 @@
8029B6A72AC239FE00BD1D30 /* DeviceIsLockedViewModifier.swift in Sources */,
80D71F242984CEF40006E1BF /* OnboardingItemStatusIcon.swift in Sources */,
8090E25F29677489003106B9 /* MainProgressView.swift in Sources */,
804F37F92C7CE46F0005A869 /* HostTrustResult.swift in Sources */,
80629BFC293981B10077960E /* CodableAppStorage.swift in Sources */,
8026714B2947C770001A804D /* QuickLaunchPanel.swift in Sources */,
8090E263296774C1003106B9 /* StatusView.swift in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions Tophat/TophatApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ extension AppDelegate: InstallCoordinatorDelegate {
deliverImmediately: true
)
}

func installCoordinator(didPromptToAllowUntrustedHost host: String) async -> HostTrustResult {
await TrustedHostAlert().requestTrust(for: host)
}
}

// MARK: - NotificationHandlerDelegate
Expand Down
12 changes: 12 additions & 0 deletions Tophat/Utilities/HostTrustResult.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// HostTrustResult.swift
// Tophat
//
// Created by Lukas Romsicki on 2024-08-26.
// Copyright © 2024 Shopify. All rights reserved.
//

enum HostTrustResult {
case allow
case block
}
16 changes: 16 additions & 0 deletions Tophat/Utilities/InstallCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ final class InstallCoordinator {
}

private func launch(artifactURL: URL, device: Device?, context: LaunchContext? = nil) async throws {
guard await validateHostTrust(artifactURL: artifactURL) == .allow else {
return
}

let fetchArtifact = FetchArtifactTask(taskStatusReporter: taskStatusReporter, pinnedApplicationState: pinnedApplicationState, context: context)
let prepareDevice = PrepareDeviceTask(taskStatusReporter: taskStatusReporter)

Expand Down Expand Up @@ -119,6 +123,18 @@ final class InstallCoordinator {
await deviceManager.loadDevices()
}

private func validateHostTrust(artifactURL: URL) async -> HostTrustResult {
if artifactURL.isFileURL {
return .allow
}

guard let host = artifactURL.host() else {
return .block
}

return await delegate?.installCoordinator(didPromptToAllowUntrustedHost: host) ?? .block
}

private func notifyError(error: Error, platform: Platform? = nil) {
log.error("An error occurred while installing the application: \(error.localizedDescription)")
delegate?.installCoordinator(didFailToInstallAppForPlatform: platform)
Expand Down
3 changes: 2 additions & 1 deletion Tophat/Utilities/InstallCoordinatorDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

import TophatFoundation

public protocol InstallCoordinatorDelegate: AnyObject {
protocol InstallCoordinatorDelegate: AnyObject {
func installCoordinator(didSuccessfullyInstallAppForPlatform platform: Platform)
func installCoordinator(didFailToInstallAppForPlatform platform: Platform?)
func installCoordinator(didPromptToAllowUntrustedHost host: String) async -> HostTrustResult
}
46 changes: 46 additions & 0 deletions Tophat/Utilities/TrustedHostAlert.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// TrustedHostAlert.swift
// Tophat
//
// Created by Lukas Romsicki on 2024-08-26.
// Copyright © 2024 Shopify. All rights reserved.
//

import Foundation
import AppKit
import SwiftUI

final class TrustedHostAlert {
@CodableAppStorage("TrustedHosts") private var trustedHosts: [String] = []

func requestTrust(for host: String) async -> HostTrustResult {
if trustedHosts.contains(host) {
return .allow
}

let result = await MainActor.run {
NSApp.activate(ignoringOtherApps: true)

let alert = NSAlert()
alert.alertStyle = .critical
alert.messageText = "The host “\(host)” has not been trusted. Are you sure you want to continue?"
alert.informativeText = "Launching an application containing malicious code can harm your Mac or compromise your privacy. Be sure you trust the origin of this application before continuing."

let trustButton = alert.addButton(withTitle: "Trust")
trustButton.keyEquivalent = ""

let cancelButton = alert.addButton(withTitle: "Cancel")
cancelButton.keyEquivalent = "\r"

return alert.runModal()
}

switch result {
case .alertFirstButtonReturn:
trustedHosts.append(host)
return .allow
default:
return .block
}
}
}

0 comments on commit 68abb63

Please sign in to comment.