From bb091361d912b082b6e4aaa9c90cc535eebf24d7 Mon Sep 17 00:00:00 2001 From: Mitch Francese <2227948+tux234@users.noreply.github.com> Date: Tue, 17 Mar 2026 11:56:20 -0400 Subject: [PATCH] Add Tor Browser as a Fleet-maintained app (#41551) Adds Tor Browser as a Fleet-maintained app for macOS only. ## Changes - **macOS**: Homebrew cask `tor-browser`, DMG installer, bundle identifier `org.torproject.torbrowser` - Icon sourced from the official Tor Project GitHub at 128x128 - Added to `apps.json` catalog in alphabetical order **Note:** Windows support was dropped. Tor Browser for Windows uses a portable installer that installs to `%LOCALAPPDATA%` (not `C:\Program Files`), which is outside the scope of what Fleet's CI validator and Windows install validation can detect. macOS-only is the correct scope for this app. ## Test plan - [ ] Verify `go run cmd/maintained-apps/main.go --slug="tor-browser/darwin"` produces valid output - [ ] Confirm icon renders correctly in the software catalog UI - [ ] Confirm macOS install/uninstall scripts work on a test device --- .../inputs/homebrew/tor-browser.json | 8 +++++++ ee/maintained-apps/outputs/apps.json | 7 ++++++ .../outputs/tor-browser/darwin.json | 21 ++++++++++++++++++ .../components/icons/TorBrowser.tsx | 14 ++++++++++++ .../SoftwarePage/components/icons/index.ts | 2 ++ .../images/app-icon-tor-browser-60x60@2x.png | Bin 0 -> 9007 bytes 6 files changed, 52 insertions(+) create mode 100644 ee/maintained-apps/inputs/homebrew/tor-browser.json create mode 100644 ee/maintained-apps/outputs/tor-browser/darwin.json create mode 100644 frontend/pages/SoftwarePage/components/icons/TorBrowser.tsx create mode 100644 website/assets/images/app-icon-tor-browser-60x60@2x.png diff --git a/ee/maintained-apps/inputs/homebrew/tor-browser.json b/ee/maintained-apps/inputs/homebrew/tor-browser.json new file mode 100644 index 0000000000..39026226f6 --- /dev/null +++ b/ee/maintained-apps/inputs/homebrew/tor-browser.json @@ -0,0 +1,8 @@ +{ + "name": "Tor Browser", + "slug": "tor-browser/darwin", + "unique_identifier": "org.torproject.torbrowser", + "token": "tor-browser", + "installer_format": "dmg", + "default_categories": ["Browsers"] +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index 14d75ed750..92aca78784 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -1667,6 +1667,13 @@ "unique_identifier": "com.todoist.mac.Todoist", "description": "Todoist is a to do list and a planning center for individuals and teams." }, + { + "name": "Tor Browser", + "slug": "tor-browser/darwin", + "platform": "darwin", + "unique_identifier": "org.torproject.torbrowser", + "description": "Tor Browser is a privacy-focused web browser that protects against tracking, surveillance, and censorship by routing traffic through the Tor network." + }, { "name": "Tower", "slug": "tower/darwin", diff --git a/ee/maintained-apps/outputs/tor-browser/darwin.json b/ee/maintained-apps/outputs/tor-browser/darwin.json new file mode 100644 index 0000000000..9607c057e8 --- /dev/null +++ b/ee/maintained-apps/outputs/tor-browser/darwin.json @@ -0,0 +1,21 @@ +{ + "versions": [ + { + "version": "15.0.7", + "queries": { + "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'org.torproject.torbrowser';" + }, + "installer_url": "https://www.torproject.org/dist/torbrowser/15.0.7/tor-browser-macos-15.0.7.dmg", + "install_script_ref": "4530bdac", + "uninstall_script_ref": "1d588638", + "sha256": "29852c4f3aa3b77bccdf359c5a22cf3ffa49ad1828b3967fb722134ffdc29e20", + "default_categories": [ + "Browsers" + ] + } + ], + "refs": { + "1d588638": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nsudo rm -rf \"$APPDIR/Tor Browser.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/org.mozilla.tor browser.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/TorBrowser-Data'\ntrash $LOGGED_IN_USER '~/Library/Preferences/org.mozilla.tor browser.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/org.torproject.torbrowser.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/org.torproject.torbrowser.savedState'\n", + "4530bdac": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath $INSTALLER_PATH)\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local timeout_duration=10\n\n # check if the application is running\n if ! osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null; then\n eval \"export $var_name=0\"\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ $EUID -eq 0 && \"$console_user\" == \"root\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ $EUID -eq 0 && \"$console_user\" == \"root\" ]]; then\n echo \"Not logged into a non-root GUI; skipping relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Try to launch the application\n if osascript -e \"tell application id \\\"$bundle_id\\\" to activate\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\n fi\n}\n\n\n# extract contents\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nhdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\"\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\"\n# copy to the applications folder\nquit_and_track_application 'org.torproject.torbrowser'\nif [ -d \"$APPDIR/Tor Browser.app\" ]; then\n\tsudo mv \"$APPDIR/Tor Browser.app\" \"$TMPDIR/Tor Browser.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/Tor Browser.app\" \"$APPDIR\"\nrelaunch_application 'org.torproject.torbrowser'\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/TorBrowser.tsx b/frontend/pages/SoftwarePage/components/icons/TorBrowser.tsx new file mode 100644 index 0000000000..dfc233eb0a --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/TorBrowser.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const TorBrowser = (props: SVGProps) => ( + + + +); +export default TorBrowser; diff --git a/frontend/pages/SoftwarePage/components/icons/index.ts b/frontend/pages/SoftwarePage/components/icons/index.ts index ccf7cfe327..5e8fc47393 100644 --- a/frontend/pages/SoftwarePage/components/icons/index.ts +++ b/frontend/pages/SoftwarePage/components/icons/index.ts @@ -214,6 +214,7 @@ import TextExpander from "./TextExpander"; import TheUnarchiver from "./TheUnarchiver"; import Thunderbird from "./Thunderbird"; import Todoist from "./Todoist"; +import TorBrowser from "./TorBrowser"; import Tower from "./Tower"; import Transmit from "./Transmit"; import Tunnelblick from "./Tunnelblick"; @@ -470,6 +471,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = { "the unarchiver": TheUnarchiver, thunderbird: Thunderbird, todoist: Todoist, + "tor browser": TorBrowser, tower: Tower, transmit: Transmit, tunnelblick: Tunnelblick, diff --git a/website/assets/images/app-icon-tor-browser-60x60@2x.png b/website/assets/images/app-icon-tor-browser-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..18f3572d0d797c91c15cc775c7d6a3e964b0b0ac GIT binary patch literal 9007 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 literal 0 HcmV?d00001