Fix mis-assigned FMA bundle identifiers, switch to fuzzy matching on queries where Windows apps include version number in the name (incl. special fixes for Firefox ESR)#42628
Conversation
- Abstract: change unique_identifier from com.SweetScape.010Editor (010 Editor's bundle ID) to com.elasticprojects.abstract-desktop. Update exists query and install script refs in output manifest. - Teleport Suite: change unique_identifier from com.gravitational.teleport (pkg receipt ID) to com.gravitational.teleport.tsh (tsh.app bundle ID). Update exists query in output manifest. - Amazon Chime (outputs/apps.json): fix unique_identifier from com.runningwithcrayons.Alfred (Alfred's bundle ID) to com.amazon.Amazon-Chime. - Beyond Compare (outputs/apps.json): fix casing from com.scootersoftware.BeyondCompare to com.ScooterSoftware.BeyondCompare.
…matching Several winget apps had version numbers baked into unique_identifier, causing exists queries to do exact matches against stale version strings. Strip version numbers and add fuzzy_match_name: true so the generated query uses LIKE 'Name %' instead of an exact match. Replace the bool FuzzyMatchName field with a custom fuzzyMatch type that accepts a JSON boolean (true/false) or a string containing a custom LIKE pattern. This is backwards-compatible: true and false behave exactly as before, and a string value is used as a verbatim LIKE pattern. Affected apps: 010 Editor, 7-Zip, Airtame, GIMP, Notion, Postman, PuTTY.
Firefox ESR registers in Windows as 'Mozilla Firefox X.Y.Z ESR (x64 en-US)'.
A simple fuzzy prefix match ('Mozilla Firefox %') would also match regular
Firefox ('Mozilla Firefox (x64 en-US)').
Use the new custom fuzzy_match_name string support to specify the pattern
'Mozilla Firefox % ESR %', which uniquely matches ESR builds without
colliding with regular Firefox.
|
@claude review |
|
@coderabbitai full-review |
|
✅ Actions performedFull review triggered. |
WalkthroughThis pull request introduces configurable fuzzy matching to the winget app ingester and updates application detection queries across Windows and macOS platforms. The core changes include: refactoring the ingester's 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ee/maintained-apps/ingesters/winget/ingester.go`:
- Around line 351-367: The existsQuery construction currently injects raw values
(input.FuzzyMatchName.Custom, name, publisher) into SQL via fmt.Sprintf
(existsQuery), which allows quotes/injection; change to use parameterized
queries instead of interpolation: build the SQL with placeholders (e.g., "SELECT
1 FROM programs WHERE name LIKE ? AND publisher = ?" or the driver-specific
$1/$2 style) for the three branches (the cases for input.FuzzyMatchName.Custom,
input.FuzzyMatchName.Enabled, and default) and pass the corresponding arguments
when executing the query (the code that uses existsQuery should call
db.QueryRow/Query with the args), or if you must keep string composition,
properly escape single quotes in the values before inserting; update the code
paths that execute existsQuery accordingly so no raw name/publisher/custom
strings are interpolated.
In `@ee/maintained-apps/outputs/teleport-suite/darwin.json`:
- Line 6: The detection SQL in darwin.json now checks bundle_identifier =
'com.gravitational.teleport.tsh' but the embedded installer script still targets
'com.gravitational.teleport' for quit/relaunch; update the installer calls to
use the corrected bundle id so lifecycle handling matches detection. Locate the
"exists" SQL entry referencing com.gravitational.teleport.tsh and the installer
script code that calls quit/relaunch on com.gravitational.teleport, and change
those script references to com.gravitational.teleport.tsh (or alternatively make
the SQL match the original bundle id if the script must remain unchanged) so
both the detection (exists) and lifecycle actions reference the same bundle
identifier.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 580c6957-743c-47c6-afe5-a35d5b01e85e
📒 Files selected for processing (23)
ee/maintained-apps/ingesters/winget/ingester.goee/maintained-apps/ingesters/winget/ingester_test.goee/maintained-apps/inputs/homebrew/abstract.jsonee/maintained-apps/inputs/homebrew/teleport-suite.jsonee/maintained-apps/inputs/winget/010-editor.jsonee/maintained-apps/inputs/winget/7-zip.jsonee/maintained-apps/inputs/winget/airtame.jsonee/maintained-apps/inputs/winget/firefox@esr.jsonee/maintained-apps/inputs/winget/gimp.jsonee/maintained-apps/inputs/winget/notion.jsonee/maintained-apps/inputs/winget/postman.jsonee/maintained-apps/inputs/winget/putty.jsonee/maintained-apps/outputs/010-editor/windows.jsonee/maintained-apps/outputs/7-zip/windows.jsonee/maintained-apps/outputs/abstract/darwin.jsonee/maintained-apps/outputs/airtame/windows.jsonee/maintained-apps/outputs/apps.jsonee/maintained-apps/outputs/firefox@esr/windows.jsonee/maintained-apps/outputs/gimp/windows.jsonee/maintained-apps/outputs/notion/windows.jsonee/maintained-apps/outputs/postman/windows.jsonee/maintained-apps/outputs/putty/windows.jsonee/maintained-apps/outputs/teleport-suite/darwin.json
| var existsQuery string | ||
| switch { | ||
| case input.FuzzyMatchName.Custom != "": | ||
| existsQuery = fmt.Sprintf( | ||
| "SELECT 1 FROM programs WHERE name LIKE '%s' AND publisher = '%s';", | ||
| input.FuzzyMatchName.Custom, publisher, | ||
| ) | ||
| case input.FuzzyMatchName.Enabled: | ||
| existsQuery = fmt.Sprintf( | ||
| "SELECT 1 FROM programs WHERE name LIKE '%s %%' AND publisher = '%s';", | ||
| name, publisher, | ||
| ) | ||
| default: | ||
| existsQuery = fmt.Sprintf( | ||
| "SELECT 1 FROM programs WHERE name = '%s' AND publisher = '%s';", | ||
| name, publisher, | ||
| ) |
There was a problem hiding this comment.
Escape SQL literals before composing existsQuery.
Line 355, Line 360, and Line 365 interpolate raw strings into SQL literals. A single quote in publisher, name, or custom pattern can break the query (and can become injection-prone). Please escape values before formatting.
Suggested fix
// TODO - consider UpgradeCode here?
var existsQuery string
+ sqlEscape := func(v string) string {
+ return strings.ReplaceAll(v, "'", "''")
+ }
+ safeName := sqlEscape(name)
+ safePublisher := sqlEscape(publisher)
switch {
case input.FuzzyMatchName.Custom != "":
existsQuery = fmt.Sprintf(
"SELECT 1 FROM programs WHERE name LIKE '%s' AND publisher = '%s';",
- input.FuzzyMatchName.Custom, publisher,
+ sqlEscape(input.FuzzyMatchName.Custom), safePublisher,
)
case input.FuzzyMatchName.Enabled:
existsQuery = fmt.Sprintf(
"SELECT 1 FROM programs WHERE name LIKE '%s %%' AND publisher = '%s';",
- name, publisher,
+ safeName, safePublisher,
)
default:
existsQuery = fmt.Sprintf(
"SELECT 1 FROM programs WHERE name = '%s' AND publisher = '%s';",
- name, publisher,
+ safeName, safePublisher,
)
}As per coding guidelines, "Review all SQL queries for possible SQL injection."
Also applies to: 370-370
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ee/maintained-apps/ingesters/winget/ingester.go` around lines 351 - 367, The
existsQuery construction currently injects raw values
(input.FuzzyMatchName.Custom, name, publisher) into SQL via fmt.Sprintf
(existsQuery), which allows quotes/injection; change to use parameterized
queries instead of interpolation: build the SQL with placeholders (e.g., "SELECT
1 FROM programs WHERE name LIKE ? AND publisher = ?" or the driver-specific
$1/$2 style) for the three branches (the cases for input.FuzzyMatchName.Custom,
input.FuzzyMatchName.Enabled, and default) and pass the corresponding arguments
when executing the query (the code that uses existsQuery should call
db.QueryRow/Query with the args), or if you must keep string composition,
properly escape single quotes in the values before inserting; update the code
paths that execute existsQuery accordingly so no raw name/publisher/custom
strings are interpolated.
| "version": "18.7.3", | ||
| "queries": { | ||
| "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'com.gravitational.teleport';", | ||
| "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'com.gravitational.teleport.tsh';", |
There was a problem hiding this comment.
Bundle ID update is inconsistent with install script app-id calls.
Line 6 now keys detection on com.gravitational.teleport.tsh, but the embedded install script still quits/relaunches com.gravitational.teleport. This leaves app lifecycle handling out of sync with the corrected identifier.
Proposed fix
-quit_and_track_application 'com.gravitational.teleport'
+quit_and_track_application 'com.gravitational.teleport.tsh'
...
-relaunch_application 'com.gravitational.teleport'
+relaunch_application 'com.gravitational.teleport.tsh'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ee/maintained-apps/outputs/teleport-suite/darwin.json` at line 6, The
detection SQL in darwin.json now checks bundle_identifier =
'com.gravitational.teleport.tsh' but the embedded installer script still targets
'com.gravitational.teleport' for quit/relaunch; update the installer calls to
use the corrected bundle id so lifecycle handling matches detection. Locate the
"exists" SQL entry referencing com.gravitational.teleport.tsh and the installer
script code that calls quit/relaunch on com.gravitational.teleport, and change
those script references to com.gravitational.teleport.tsh (or alternatively make
the SQL match the original bundle id if the script must remain unchanged) so
both the detection (exists) and lifecycle actions reference the same bundle
identifier.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #42628 +/- ##
==========================================
- Coverage 66.88% 66.86% -0.03%
==========================================
Files 2591 2587 -4
Lines 207684 207582 -102
Branches 9322 9187 -135
==========================================
- Hits 138913 138797 -116
- Misses 56136 56148 +12
- Partials 12635 12637 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
# Conflicts: # ee/maintained-apps/ingesters/winget/ingester_test.go # ee/maintained-apps/outputs/010-editor/windows.json # ee/maintained-apps/outputs/7-zip/windows.json # ee/maintained-apps/outputs/abstract/darwin.json # ee/maintained-apps/outputs/airtame/windows.json # ee/maintained-apps/outputs/firefox@esr/windows.json # ee/maintained-apps/outputs/gimp/windows.json # ee/maintained-apps/outputs/notion/windows.json # ee/maintained-apps/outputs/postman/windows.json # ee/maintained-apps/outputs/putty/windows.json # ee/maintained-apps/outputs/teleport-suite/darwin.json
Script Diff Resultsee/maintained-apps/outputs/010-editor/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/7-zip/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/abstract/darwin.json=== Install // 40f16970 -> 17ab2dcc ===
--- /tmp/old.YjuE8j 2026-04-10 15:16:59.505924897 +0000
+++ /tmp/new.EuZjTH 2026-04-10 15:16:59.505924897 +0000
@@ -11,7 +11,9 @@
local timeout_duration=10
# check if the application is running
- if ! osascript -e "application id \"$bundle_id\" is running" 2>/dev/null; then
+ local app_running
+ app_running=$(osascript -e "application id \"$bundle_id\" is running" 2>/dev/null)
+ if [[ "$app_running" != "true" ]]; then
eval "export $var_name=0"
return
fi
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/airtame/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/firefox@esr/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/gimp/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/notion/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/postman/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/putty/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/teleport-suite/darwin.json=== Install // 3435fc52 -> 4f45dd14 ===
--- /tmp/old.3mZiux 2026-04-10 15:16:59.760927258 +0000
+++ /tmp/new.inUwDV 2026-04-10 15:16:59.761927268 +0000
@@ -11,7 +11,9 @@
local timeout_duration=10
# check if the application is running
- if ! osascript -e "application id \"$bundle_id\" is running" 2>/dev/null; then
+ local app_running
+ app_running=$(osascript -e "application id \"$bundle_id\" is running" 2>/dev/null)
+ if [[ "$app_running" != "true" ]]; then
eval "export $var_name=0"
return
fi
@@ -80,6 +82,6 @@
# install pkg files
-quit_and_track_application 'com.gravitational.teleport'
-sudo installer -pkg "$TMPDIR/teleport-18.7.3.pkg" -target /
-relaunch_application 'com.gravitational.teleport'
+quit_and_track_application 'com.gravitational.teleport.tsh'
+sudo installer -pkg "$TMPDIR/teleport-18.7.4.pkg" -target /
+relaunch_application 'com.gravitational.teleport.tsh'
=== Uninstall Script (no changes) === |
Zed + Opus 4.6; prompt: `ee/maintained-apps/outputs/postman/windows.json` is behaving unexpectedly; L7 should basically be a wrapped L6. What's going on?
Zed + Opus 4.6; prompt: In `ee/maintained-apps/ingesters/winget/ingester.go`, escape SQL params in L386-400 before interpolating to avoid SQLi.
Script Diff Resultsee/maintained-apps/outputs/010-editor/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/7-zip/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/abstract/darwin.json=== Install // 40f16970 -> 17ab2dcc ===
--- /tmp/old.kEswuI 2026-04-10 18:19:05.456630489 +0000
+++ /tmp/new.SU1hiA 2026-04-10 18:19:05.456630489 +0000
@@ -11,7 +11,9 @@
local timeout_duration=10
# check if the application is running
- if ! osascript -e "application id \"$bundle_id\" is running" 2>/dev/null; then
+ local app_running
+ app_running=$(osascript -e "application id \"$bundle_id\" is running" 2>/dev/null)
+ if [[ "$app_running" != "true" ]]; then
eval "export $var_name=0"
return
fi
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/airtame/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/firefox@esr/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/gimp/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/notion/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/postman/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/putty/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/teleport-suite/darwin.json=== Install // 3435fc52 -> 4f45dd14 ===
--- /tmp/old.Tt6ik1 2026-04-10 18:19:05.650628933 +0000
+++ /tmp/new.RQgBA1 2026-04-10 18:19:05.650628933 +0000
@@ -11,7 +11,9 @@
local timeout_duration=10
# check if the application is running
- if ! osascript -e "application id \"$bundle_id\" is running" 2>/dev/null; then
+ local app_running
+ app_running=$(osascript -e "application id \"$bundle_id\" is running" 2>/dev/null)
+ if [[ "$app_running" != "true" ]]; then
eval "export $var_name=0"
return
fi
@@ -80,6 +82,6 @@
# install pkg files
-quit_and_track_application 'com.gravitational.teleport'
-sudo installer -pkg "$TMPDIR/teleport-18.7.3.pkg" -target /
-relaunch_application 'com.gravitational.teleport'
+quit_and_track_application 'com.gravitational.teleport.tsh'
+sudo installer -pkg "$TMPDIR/teleport-18.7.4.pkg" -target /
+relaunch_application 'com.gravitational.teleport.tsh'
=== Uninstall Script (no changes) === |
Zed + Opus 4.6; prompt: Build tests covering `setUpExistsQuery` in `ee/maintained-apps/ingesters/winget/ingester.go`
Zed + Opus 4.6; prompts: > Would it make sense to extract name handling from `setUpExistsQuery` into a method on fuzzyMatch? Probably would allow for some early returns rather than the existing case statement. (it decided it was a worthwhile refactor and asked if it should proceed) > yep After the modifications, I switched the method to be a pointer receiver
Script Diff Resultsee/maintained-apps/outputs/010-editor/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/7-zip/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/abstract/darwin.json=== Install // 40f16970 -> 17ab2dcc ===
--- /tmp/old.DgPkwo 2026-04-10 18:31:26.839481539 +0000
+++ /tmp/new.HMR3nj 2026-04-10 18:31:26.839481539 +0000
@@ -11,7 +11,9 @@
local timeout_duration=10
# check if the application is running
- if ! osascript -e "application id \"$bundle_id\" is running" 2>/dev/null; then
+ local app_running
+ app_running=$(osascript -e "application id \"$bundle_id\" is running" 2>/dev/null)
+ if [[ "$app_running" != "true" ]]; then
eval "export $var_name=0"
return
fi
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/airtame/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/firefox@esr/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/gimp/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/notion/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/postman/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/putty/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/teleport-suite/darwin.json=== Install // 3435fc52 -> 4f45dd14 ===
--- /tmp/old.GyPKoC 2026-04-10 18:31:27.091480058 +0000
+++ /tmp/new.BCqln2 2026-04-10 18:31:27.091480058 +0000
@@ -11,7 +11,9 @@
local timeout_duration=10
# check if the application is running
- if ! osascript -e "application id \"$bundle_id\" is running" 2>/dev/null; then
+ local app_running
+ app_running=$(osascript -e "application id \"$bundle_id\" is running" 2>/dev/null)
+ if [[ "$app_running" != "true" ]]; then
eval "export $var_name=0"
return
fi
@@ -80,6 +82,6 @@
# install pkg files
-quit_and_track_application 'com.gravitational.teleport'
-sudo installer -pkg "$TMPDIR/teleport-18.7.3.pkg" -target /
-relaunch_application 'com.gravitational.teleport'
+quit_and_track_application 'com.gravitational.teleport.tsh'
+sudo installer -pkg "$TMPDIR/teleport-18.7.4.pkg" -target /
+relaunch_application 'com.gravitational.teleport.tsh'
=== Uninstall Script (no changes) === |
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
There was a problem hiding this comment.
Pull request overview
Fixes incorrect Fleet Maintained App (FMA) identifiers and improves Windows install-detection robustness by switching version-sensitive programs.name checks to fuzzy LIKE matching, including custom patterns for Firefox ESR.
Changes:
- Corrects mis-assigned Darwin bundle identifiers (e.g., Abstract, Amazon Chime, Teleport Suite) and aligns
apps.jsonunique identifiers. - Adds/extends WinGet ingester support for fuzzy name matching (boolean or custom pattern string) and updates affected Windows app manifests/outputs to use
LIKE. - Hardens patch-policy query generation to safely wrap
existsqueries that include%(SQLLIKE) by escaping%beforefmt.Sprintf, plus adds test coverage.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pkg/patch_policy/patch_policy.go | Escapes literal % in ExistsQuery before using fmt.Sprintf to generate patched queries. |
| pkg/patch_policy/patch_policy_test.go | Adds test cases validating patched-query generation with LIKE patterns containing %. |
| ee/maintained-apps/ingesters/winget/ingester.go | Introduces fuzzyMatch (bool or custom string), centralizes exists-query generation, and escapes single quotes in SQL literals. |
| ee/maintained-apps/ingesters/winget/ingester_test.go | Adds unit tests for fuzzyMatch JSON unmarshaling and setUpExistsQuery behavior (including escaping). |
| ee/maintained-apps/outputs/teleport-suite/darwin.json | Updates Teleport Suite Darwin bundle identifier used for exists/patched and updates install script bundle id/ref. |
| ee/maintained-apps/outputs/abstract/darwin.json | Fixes Abstract Darwin bundle identifier in queries and updates install script to use the corrected bundle id. |
| ee/maintained-apps/outputs/apps.json | Fixes catalog unique identifiers (e.g., Amazon Chime, Beyond Compare) to match actual bundle ids. |
| ee/maintained-apps/outputs/putty/windows.json | Switches exists to LIKE so detection survives name/version changes. |
| ee/maintained-apps/outputs/postman/windows.json | Switches exists to LIKE for version-agnostic detection. |
| ee/maintained-apps/outputs/notion/windows.json | Switches exists to LIKE for version-agnostic detection. |
| ee/maintained-apps/outputs/gimp/windows.json | Switches exists to LIKE for version-agnostic detection. |
| ee/maintained-apps/outputs/airtame/windows.json | Switches exists to LIKE for version-agnostic detection. |
| ee/maintained-apps/outputs/7-zip/windows.json | Switches exists to LIKE for version-agnostic detection. |
| ee/maintained-apps/outputs/010-editor/windows.json | Switches exists to LIKE for version-agnostic detection. |
| ee/maintained-apps/outputs/firefox@esr/windows.json | Switches exists to a custom LIKE pattern to uniquely match ESR variants. |
| ee/maintained-apps/inputs/winget/putty.json | Updates unique_identifier and enables fuzzy matching to avoid embedding version in exists. |
| ee/maintained-apps/inputs/winget/postman.json | Updates unique_identifier and enables fuzzy matching. |
| ee/maintained-apps/inputs/winget/notion.json | Updates unique_identifier and enables fuzzy matching. |
| ee/maintained-apps/inputs/winget/gimp.json | Updates unique_identifier and enables fuzzy matching. |
| ee/maintained-apps/inputs/winget/airtame.json | Updates unique_identifier and enables fuzzy matching. |
| ee/maintained-apps/inputs/winget/7-zip.json | Updates unique_identifier and enables fuzzy matching. |
| ee/maintained-apps/inputs/winget/010-editor.json | Updates unique_identifier and enables fuzzy matching. |
| ee/maintained-apps/inputs/winget/firefox@esr.json | Uses custom fuzzy pattern string for ESR-specific matching. |
| ee/maintained-apps/inputs/homebrew/teleport-suite.json | Updates Darwin unique identifier to match corrected Teleport Suite bundle id. |
| ee/maintained-apps/inputs/homebrew/abstract.json | Updates Darwin unique identifier to match corrected Abstract bundle id. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "refs": { | ||
| "161ebbe3": "#!/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 local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; 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# install pkg files\nquit_and_track_application 'com.gravitational.teleport'\nsudo installer -pkg \"$TMPDIR/teleport-18.7.4.pkg\" -target /\nrelaunch_application 'com.gravitational.teleport'\n", | ||
| "42474e69": "#!/bin/sh\n\n# variables\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nexpand_pkgid_and_map() {\n local PKGID=\"$1\"\n local FUNC=\"$2\"\n if [[ \"$PKGID\" == *\"*\" ]]; then\n local prefix=\"${PKGID%\\*}\"\n # Convert (.*) to .* for regex matching to handle patterns like (.*).com.example.app\n local regex_pattern=$(echo \"$prefix\" | sed 's/(\\.\\*)/.*/g')\n echo \"Expanding wildcard for PKGID: $PKGID (pattern: ^${regex_pattern})\"\n for receipt in $(pkgutil --pkgs | grep -E \"^${regex_pattern}\"); do\n echo \"Processing $receipt\"\n \"$FUNC\" \"$receipt\"\n done\n else\n \"$FUNC\" \"$PKGID\"\n fi\n}\n\nforget_pkg() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" forget_receipt\n}\n\nforget_receipt() {\n local PKGID=\"$1\"\n sudo pkgutil --forget \"$PKGID\"\n}\n\nremove_pkg_files() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" remove_receipt_files\n}\n\nremove_receipt_files() {\n local PKGID=\"$1\"\n local PKGINFO VOLUME INSTALL_LOCATION FULL_INSTALL_LOCATION\n\n echo \"pkgutil --pkg-info-plist \\\"$PKGID\\\"\"\n PKGINFO=$(pkgutil --pkg-info-plist \"$PKGID\")\n VOLUME=$(echo \"$PKGINFO\" | awk '/<key>volume<\\/key>/ {getline; gsub(/.*<string>|<\\/string>.*/, \"\"); print}')\n INSTALL_LOCATION=$(echo \"$PKGINFO\" | awk '/<key>install-location<\\/key>/ {getline; gsub(/.*<string>|<\\/string>.*/, \"\"); print}')\n\n if [ -z \"$INSTALL_LOCATION\" ] || [ \"$INSTALL_LOCATION\" = \"/\" ]; then\n FULL_INSTALL_LOCATION=\"$VOLUME\"\n else\n FULL_INSTALL_LOCATION=\"$VOLUME/$INSTALL_LOCATION\"\n FULL_INSTALL_LOCATION=$(echo \"$FULL_INSTALL_LOCATION\" | sed 's|//|/|g')\n fi\n\n echo \"sudo pkgutil --only-files --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-files --files \"$PKGID\" | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n echo \"sudo pkgutil --only-dirs --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | grep '\\\\.app$' | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-dirs --files \"$PKGID\" | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" | grep '\\.app$' | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n root_app_dir=$(\n sudo pkgutil --only-dirs --files \"$PKGID\" \\\n | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" \\\n | grep 'Applications' \\\n | awk '{ print length, $0 }' \\\n | sort -n \\\n | head -n1 \\\n | cut -d' ' -f2-\n )\n if [ -n \"$root_app_dir\" ]; then\n echo \"sudo rmdir -p \\\"$root_app_dir\\\" 2>/dev/null || :\"\n sudo rmdir -p \"$root_app_dir\" 2>/dev/null || :\n fi\n}\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\n# Remove teleport-suite packages (handles wildcard patterns with (.*))\nremove_pkg_files '(.*).com.gravitational.teleport.tctl'\nforget_pkg '(.*).com.gravitational.teleport.tctl'\nremove_pkg_files '(.*).com.gravitational.teleport.tsh'\nforget_pkg '(.*).com.gravitational.teleport.tsh'\nremove_pkg_files 'com.gravitational.teleport'\nforget_pkg 'com.gravitational.teleport'\n\n# Explicitly remove apps from /Applications (in case pkgutil removal didn't catch them)\nsudo rm -rf '/Applications/tctl.app'\nsudo rm -rf '/Applications/tsh.app'\n\n# Remove binaries\nsudo rm -rf '/usr/local/bin/fdpass-teleport'\nsudo rm -rf '/usr/local/bin/tbot'\nsudo rm -rf '/usr/local/bin/tctl'\nsudo rm -rf '/usr/local/bin/teleport'\nsudo rm -rf '/usr/local/bin/tsh'\n\n# Remove user data\ntrash $LOGGED_IN_USER '~/.tsh'\n\n" | ||
| "42474e69": "#!/bin/sh\n\n# variables\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nexpand_pkgid_and_map() {\n local PKGID=\"$1\"\n local FUNC=\"$2\"\n if [[ \"$PKGID\" == *\"*\" ]]; then\n local prefix=\"${PKGID%\\*}\"\n # Convert (.*) to .* for regex matching to handle patterns like (.*).com.example.app\n local regex_pattern=$(echo \"$prefix\" | sed 's/(\\.\\*)/.*/g')\n echo \"Expanding wildcard for PKGID: $PKGID (pattern: ^${regex_pattern})\"\n for receipt in $(pkgutil --pkgs | grep -E \"^${regex_pattern}\"); do\n echo \"Processing $receipt\"\n \"$FUNC\" \"$receipt\"\n done\n else\n \"$FUNC\" \"$PKGID\"\n fi\n}\n\nforget_pkg() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" forget_receipt\n}\n\nforget_receipt() {\n local PKGID=\"$1\"\n sudo pkgutil --forget \"$PKGID\"\n}\n\nremove_pkg_files() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" remove_receipt_files\n}\n\nremove_receipt_files() {\n local PKGID=\"$1\"\n local PKGINFO VOLUME INSTALL_LOCATION FULL_INSTALL_LOCATION\n\n echo \"pkgutil --pkg-info-plist \\\"$PKGID\\\"\"\n PKGINFO=$(pkgutil --pkg-info-plist \"$PKGID\")\n VOLUME=$(echo \"$PKGINFO\" | awk '/<key>volume<\\/key>/ {getline; gsub(/.*<string>|<\\/string>.*/, \"\"); print}')\n INSTALL_LOCATION=$(echo \"$PKGINFO\" | awk '/<key>install-location<\\/key>/ {getline; gsub(/.*<string>|<\\/string>.*/, \"\"); print}')\n\n if [ -z \"$INSTALL_LOCATION\" ] || [ \"$INSTALL_LOCATION\" = \"/\" ]; then\n FULL_INSTALL_LOCATION=\"$VOLUME\"\n else\n FULL_INSTALL_LOCATION=\"$VOLUME/$INSTALL_LOCATION\"\n FULL_INSTALL_LOCATION=$(echo \"$FULL_INSTALL_LOCATION\" | sed 's|//|/|g')\n fi\n\n echo \"sudo pkgutil --only-files --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-files --files \"$PKGID\" | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n echo \"sudo pkgutil --only-dirs --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | grep '\\\\.app$' | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-dirs --files \"$PKGID\" | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" | grep '\\.app$' | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n root_app_dir=$(\n sudo pkgutil --only-dirs --files \"$PKGID\" \\\n | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" \\\n | grep 'Applications' \\\n | awk '{ print length, $0 }' \\\n | sort -n \\\n | head -n1 \\\n | cut -d' ' -f2-\n )\n if [ -n \"$root_app_dir\" ]; then\n echo \"sudo rmdir -p \\\"$root_app_dir\\\" 2>/dev/null || :\"\n sudo rmdir -p \"$root_app_dir\" 2>/dev/null || :\n fi\n}\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\n# Remove teleport-suite packages (handles wildcard patterns with (.*))\nremove_pkg_files '(.*).com.gravitational.teleport.tctl'\nforget_pkg '(.*).com.gravitational.teleport.tctl'\nremove_pkg_files '(.*).com.gravitational.teleport.tsh'\nforget_pkg '(.*).com.gravitational.teleport.tsh'\nremove_pkg_files 'com.gravitational.teleport'\nforget_pkg 'com.gravitational.teleport'\n\n# Explicitly remove apps from /Applications (in case pkgutil removal didn't catch them)\nsudo rm -rf '/Applications/tctl.app'\nsudo rm -rf '/Applications/tsh.app'\n\n# Remove binaries\nsudo rm -rf '/usr/local/bin/fdpass-teleport'\nsudo rm -rf '/usr/local/bin/tbot'\nsudo rm -rf '/usr/local/bin/tctl'\nsudo rm -rf '/usr/local/bin/teleport'\nsudo rm -rf '/usr/local/bin/tsh'\n\n# Remove user data\ntrash $LOGGED_IN_USER '~/.tsh'\n\n", |
There was a problem hiding this comment.
The embedded uninstall script’s expand_pkgid_and_map logic appears incorrect for the (.*).com.gravitational.teleport.* patterns used below. It strips everything after the last * via ${PKGID%\*}, but in a pattern like (.*).com.gravitational.teleport.tsh the last * is inside (.*), so the computed prefix becomes (. and the subsequent grep -E won’t match any receipts. This will prevent remove_pkg_files/forget_pkg from expanding and removing the intended package IDs. Consider changing the wildcard expansion to convert the full pattern to a regex (without truncating at *), or switch to a wildcard form where * only appears as a trailing glob (e.g. com.gravitational.teleport.tsh*) so the existing prefix logic works.
| "42474e69": "#!/bin/sh\n\n# variables\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nexpand_pkgid_and_map() {\n local PKGID=\"$1\"\n local FUNC=\"$2\"\n if [[ \"$PKGID\" == *\"*\" ]]; then\n local prefix=\"${PKGID%\\*}\"\n # Convert (.*) to .* for regex matching to handle patterns like (.*).com.example.app\n local regex_pattern=$(echo \"$prefix\" | sed 's/(\\.\\*)/.*/g')\n echo \"Expanding wildcard for PKGID: $PKGID (pattern: ^${regex_pattern})\"\n for receipt in $(pkgutil --pkgs | grep -E \"^${regex_pattern}\"); do\n echo \"Processing $receipt\"\n \"$FUNC\" \"$receipt\"\n done\n else\n \"$FUNC\" \"$PKGID\"\n fi\n}\n\nforget_pkg() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" forget_receipt\n}\n\nforget_receipt() {\n local PKGID=\"$1\"\n sudo pkgutil --forget \"$PKGID\"\n}\n\nremove_pkg_files() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" remove_receipt_files\n}\n\nremove_receipt_files() {\n local PKGID=\"$1\"\n local PKGINFO VOLUME INSTALL_LOCATION FULL_INSTALL_LOCATION\n\n echo \"pkgutil --pkg-info-plist \\\"$PKGID\\\"\"\n PKGINFO=$(pkgutil --pkg-info-plist \"$PKGID\")\n VOLUME=$(echo \"$PKGINFO\" | awk '/<key>volume<\\/key>/ {getline; gsub(/.*<string>|<\\/string>.*/, \"\"); print}')\n INSTALL_LOCATION=$(echo \"$PKGINFO\" | awk '/<key>install-location<\\/key>/ {getline; gsub(/.*<string>|<\\/string>.*/, \"\"); print}')\n\n if [ -z \"$INSTALL_LOCATION\" ] || [ \"$INSTALL_LOCATION\" = \"/\" ]; then\n FULL_INSTALL_LOCATION=\"$VOLUME\"\n else\n FULL_INSTALL_LOCATION=\"$VOLUME/$INSTALL_LOCATION\"\n FULL_INSTALL_LOCATION=$(echo \"$FULL_INSTALL_LOCATION\" | sed 's|//|/|g')\n fi\n\n echo \"sudo pkgutil --only-files --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-files --files \"$PKGID\" | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n echo \"sudo pkgutil --only-dirs --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | grep '\\\\.app$' | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-dirs --files \"$PKGID\" | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" | grep '\\.app$' | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n root_app_dir=$(\n sudo pkgutil --only-dirs --files \"$PKGID\" \\\n | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" \\\n | grep 'Applications' \\\n | awk '{ print length, $0 }' \\\n | sort -n \\\n | head -n1 \\\n | cut -d' ' -f2-\n )\n if [ -n \"$root_app_dir\" ]; then\n echo \"sudo rmdir -p \\\"$root_app_dir\\\" 2>/dev/null || :\"\n sudo rmdir -p \"$root_app_dir\" 2>/dev/null || :\n fi\n}\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\n# Remove teleport-suite packages (handles wildcard patterns with (.*))\nremove_pkg_files '(.*).com.gravitational.teleport.tctl'\nforget_pkg '(.*).com.gravitational.teleport.tctl'\nremove_pkg_files '(.*).com.gravitational.teleport.tsh'\nforget_pkg '(.*).com.gravitational.teleport.tsh'\nremove_pkg_files 'com.gravitational.teleport'\nforget_pkg 'com.gravitational.teleport'\n\n# Explicitly remove apps from /Applications (in case pkgutil removal didn't catch them)\nsudo rm -rf '/Applications/tctl.app'\nsudo rm -rf '/Applications/tsh.app'\n\n# Remove binaries\nsudo rm -rf '/usr/local/bin/fdpass-teleport'\nsudo rm -rf '/usr/local/bin/tbot'\nsudo rm -rf '/usr/local/bin/tctl'\nsudo rm -rf '/usr/local/bin/teleport'\nsudo rm -rf '/usr/local/bin/tsh'\n\n# Remove user data\ntrash $LOGGED_IN_USER '~/.tsh'\n\n", | |
| "42474e69": "#!/bin/sh\n\n# variables\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nexpand_pkgid_and_map() {\n local PKGID=\"$1\"\n local FUNC=\"$2\"\n if [[ \"$PKGID\" == *\"*\" ]]; then\n local regex_pattern=\"^${PKGID}$\"\n echo \"Expanding package pattern for PKGID: $PKGID (pattern: ${regex_pattern})\"\n for receipt in $(pkgutil --pkgs | grep -E \"$regex_pattern\"); do\n echo \"Processing $receipt\"\n \"$FUNC\" \"$receipt\"\n done\n else\n \"$FUNC\" \"$PKGID\"\n fi\n}\n\nforget_pkg() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" forget_receipt\n}\n\nforget_receipt() {\n local PKGID=\"$1\"\n sudo pkgutil --forget \"$PKGID\"\n}\n\nremove_pkg_files() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" remove_receipt_files\n}\n\nremove_receipt_files() {\n local PKGID=\"$1\"\n local PKGINFO VOLUME INSTALL_LOCATION FULL_INSTALL_LOCATION\n\n echo \"pkgutil --pkg-info-plist \\\"$PKGID\\\"\"\n PKGINFO=$(pkgutil --pkg-info-plist \"$PKGID\")\n VOLUME=$(echo \"$PKGINFO\" | awk '/<key>volume<\\/key>/ {getline; gsub(/.*<string>|<\\/string>.*/, \"\"); print}')\n INSTALL_LOCATION=$(echo \"$PKGINFO\" | awk '/<key>install-location<\\/key>/ {getline; gsub(/.*<string>|<\\/string>.*/, \"\"); print}')\n\n if [ -z \"$INSTALL_LOCATION\" ] || [ \"$INSTALL_LOCATION\" = \"/\" ]; then\n FULL_INSTALL_LOCATION=\"$VOLUME\"\n else\n FULL_INSTALL_LOCATION=\"$VOLUME/$INSTALL_LOCATION\"\n FULL_INSTALL_LOCATION=$(echo \"$FULL_INSTALL_LOCATION\" | sed 's|//|/|g')\n fi\n\n echo \"sudo pkgutil --only-files --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-files --files \"$PKGID\" | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n echo \"sudo pkgutil --only-dirs --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | grep '\\\\.app$' | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-dirs --files \"$PKGID\" | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" | grep '\\.app$' | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n root_app_dir=$(\n sudo pkgutil --only-dirs --files \"$PKGID\" \\\n | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" \\\n | grep 'Applications' \\\n | awk '{ print length, $0 }' \\\n | sort -n \\\n | head -n1 \\\n | cut -d' ' -f2-\n )\n if [ -n \"$root_app_dir\" ]; then\n echo \"sudo rmdir -p \\\"$root_app_dir\\\" 2>/dev/null || :\"\n sudo rmdir -p \"$root_app_dir\" 2>/dev/null || :\n fi\n}\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\n# Remove teleport-suite packages (handles regex package ID patterns such as (.*).com.gravitational.teleport.tsh)\nremove_pkg_files '(.*).com.gravitational.teleport.tctl'\nforget_pkg '(.*).com.gravitational.teleport.tctl'\nremove_pkg_files '(.*).com.gravitational.teleport.tsh'\nforget_pkg '(.*).com.gravitational.teleport.tsh'\nremove_pkg_files 'com.gravitational.teleport'\nforget_pkg 'com.gravitational.teleport'\n\n# Explicitly remove apps from /Applications (in case pkgutil removal didn't catch them)\nsudo rm -rf '/Applications/tctl.app'\nsudo rm -rf '/Applications/tsh.app'\n\n# Remove binaries\nsudo rm -rf '/usr/local/bin/fdpass-teleport'\nsudo rm -rf '/usr/local/bin/tbot'\nsudo rm -rf '/usr/local/bin/tctl'\nsudo rm -rf '/usr/local/bin/teleport'\nsudo rm -rf '/usr/local/bin/tsh'\n\n# Remove user data\ntrash $LOGGED_IN_USER '~/.tsh'\n\n", |
There was a problem hiding this comment.
@allenhouchins Have you seen this issue come up anywhere?
There was a problem hiding this comment.
@iansltx these "suites" of apps have been challenging. I did confirm that the install and uninstall script work for this specific FMA before the proposed changes.
| "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'com.SweetScape.010Editor';", | ||
| "patched": "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM apps WHERE bundle_identifier = 'com.SweetScape.010Editor' AND version_compare(bundle_short_version, '98.6.3') < 0);" | ||
| "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'com.elasticprojects.abstract-desktop';", | ||
| "patched": "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM apps WHERE bundle_identifier = 'com.elasticprojects.abstract-desktop' AND version_compare(bundle_short_version, '98.6.3') < 0);" |
There was a problem hiding this comment.
Thanks for catching this!
|
@iansltx is the plan that the windows ingester will always put "fuzzy_match_name": true by default? Or could you at least help me understand and document when we should or should not use that logic? |
|
@allenhouchins Fuzzy matching is off by default because a lot of Windows apps are well-behaved (no version number in the osquery-pulled name). You'd use this when osquery reports a name including version number. The standard fuzzy match handles cases where the version number is the last thing in the name string, while you can provide your own value for the LIKE query in case you need more control (e.g. with Firefox ESR). |
Make sense. Thanks! |
| "slug": "amazon-chime/darwin", | ||
| "platform": "darwin", | ||
| "unique_identifier": "com.runningwithcrayons.Alfred", | ||
| "unique_identifier": "com.amazon.Amazon-Chime", |
There was a problem hiding this comment.
Would we eventually want to have ingesters automatically update apps.json rather than only add new apps?
There was a problem hiding this comment.
Yeah probably. Out of scope for this case though.
Resolves #42714.
Zed + Opus 4.6; initial prompts (see additional ones in follow-on commits):
Audit our existing Fleet Maintained App catalog. Look for:
existsqueriesexistsquery, e.g. 7-zip or 010 Editor or Airtame. These should be fuzzy-matched.For each affected app, revise input manifests to fix the issues. For (1), revise apps.json if needed as well. Don't modify apps.json for (2) cases.
Are there any discrepancies between bundle identifiers in input manifests for Darwin apps and apps.json? If so, fix them.
Outputs will get overwritten by the ingester if neither the ingester nor the input JSON files are changed. Make whatever changes need to be made so that these edits survive an FMA ingestion cycle.
Revise
fuzzyto allow specifying a custom value e.g.Mozilal Firefox % (ESR)in addition to the existing true/false, then use that new functionality to build unique queries for Firefox ESR.Commit these changes, across multiple commits (there will be cases where a changes to a single file will be spread across multiple commits, most notably apps.json). Split commits out as follows:
Prefix commit messages with "🤖 ".
The ingester and test changes should've gone in commit 3. Move them there from commit 2.
Summary by CodeRabbit
Bug Fixes
New Features
Tests