From 130cc40fea6e7045eb61b9075eb8737f0384911f Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 17 Nov 2023 12:21:10 +0100 Subject: [PATCH] Improve AppImage matching, even without ENV vars --- process/tags/appimage_unix.go | 147 ++++++++++++++++++++++++++++------ 1 file changed, 122 insertions(+), 25 deletions(-) diff --git a/process/tags/appimage_unix.go b/process/tags/appimage_unix.go index 37548242..b2f47750 100644 --- a/process/tags/appimage_unix.go +++ b/process/tags/appimage_unix.go @@ -1,8 +1,13 @@ package tags import ( + "bufio" + "bytes" + "os" + "regexp" "strings" + "github.com/safing/portbase/log" "github.com/safing/portbase/utils/osdetail" "github.com/safing/portmaster/process" "github.com/safing/portmaster/profile" @@ -16,8 +21,14 @@ func init() { } const ( - appImageName = "AppImage" - appImagePathTagKey = "app-image-path" + appImageName = "AppImage" + appImagePathTagKey = "app-image-path" + appImageMountIDTagKey = "app-image-mount-id" +) + +var ( + appImageMountDirRegex = regexp.MustCompile(`^/tmp/.mount_[^/]+`) + appImageMountNameExtractRegex = regexp.MustCompile(`^[A-Za-z0-9]+`) ) // AppImageHandler handles AppImage processes on Unix systems. @@ -34,36 +45,77 @@ func (h *AppImageHandler) TagDescriptions() []process.TagDescription { return []process.TagDescription{ { ID: appImagePathTagKey, - Name: "App Image Path", + Name: "AppImage Path", Description: "Path to the app image file itself.", }, + { + ID: appImageMountIDTagKey, + Name: "AppImage Mount ID", + Description: "Extracted ID from the AppImage mount name. Use AppImage Path instead, if available.", + }, } } // AddTags adds tags to the given process. func (h *AppImageHandler) AddTags(p *process.Process) { - // Get and verify AppImage location. - appImageLocation, ok := p.Env["APPIMAGE"] - if !ok { - return - } - appImageMountDir, ok := p.Env["APPDIR"] - if !ok { - return - } - // Check if the process path is in the mount dir. - if !strings.HasPrefix(p.Path, appImageMountDir) { - return - } + // Detect app image path via ENV vars. + func() { + // Get and verify AppImage location. + appImageLocation, ok := p.Env["APPIMAGE"] + if !ok || appImageLocation == "" { + return + } + appImageMountDir, ok := p.Env["APPDIR"] + if !ok || appImageMountDir == "" { + return + } + // Check if the process path is in the mount dir. + if !strings.HasPrefix(p.Path, appImageMountDir) { + return + } - // Add matching path for regular profile matching. - p.MatchingPath = appImageLocation + // Add matching path for regular profile matching. + p.MatchingPath = appImageLocation - // Add app image tags. - p.Tags = append(p.Tags, profile.Tag{ - Key: appImagePathTagKey, - Value: appImageLocation, - }) + // Add app image tag. + p.Tags = append(p.Tags, profile.Tag{ + Key: appImagePathTagKey, + Value: appImageLocation, + }) + }() + + // Detect app image mount point. + func() { + // Check if binary path matches app image mount pattern. + mountDir := appImageMountDirRegex.FindString(p.Path) + if mountDir == "" { + return + } + + // Get mount name of mount dir. + // Also, this confirm this is actually a mounted dir. + mountName, err := getAppImageMountName(mountDir) + if err != nil { + log.Debugf("process/tags: failed to get mount name: %s", err) + return + } + if mountName == "" { + return + } + + // Extract a usable ID from the mount name. + mountName, _ = strings.CutPrefix(mountName, "gearlever_") + mountName = appImageMountNameExtractRegex.FindString(mountName) + if mountName == "" { + return + } + + // Add app image tag. + p.Tags = append(p.Tags, profile.Tag{ + Key: appImageMountIDTagKey, + Value: mountName, + }) + }() } // CreateProfile creates a profile based on the tags of the process. @@ -72,12 +124,13 @@ func (h *AppImageHandler) CreateProfile(p *process.Process) *profile.Profile { if tag, ok := p.GetTag(appImagePathTagKey); ok { return profile.New(&profile.Profile{ Source: profile.SourceLocal, - Name: osdetail.GenerateBinaryNameFromPath(tag.Value), + Name: osdetail.GenerateBinaryNameFromPath(p.Path), PresentationPath: p.Path, UsePresentationPath: true, Fingerprints: []profile.Fingerprint{ { - Type: profile.FingerprintTypePathID, + Type: profile.FingerprintTypeTagID, + Key: tag.Key, Operation: profile.FingerprintOperationEqualsID, Value: tag.Value, // Value of appImagePathTagKey. }, @@ -85,5 +138,49 @@ func (h *AppImageHandler) CreateProfile(p *process.Process) *profile.Profile { }) } + if tag, ok := p.GetTag(appImageMountIDTagKey); ok { + return profile.New(&profile.Profile{ + Source: profile.SourceLocal, + Name: osdetail.GenerateBinaryNameFromPath(p.Path), + PresentationPath: p.Path, + UsePresentationPath: true, + Fingerprints: []profile.Fingerprint{ + { + Type: profile.FingerprintTypeTagID, + Key: tag.Key, + Operation: profile.FingerprintOperationEqualsID, + Value: tag.Value, // Value of appImageMountIDTagKey. + }, + }, + }) + } + return nil } + +func getAppImageMountName(mountPoint string) (mountName string, err error) { + // Get mounts. + data, err := os.ReadFile("/proc/mounts") + if err != nil { + return "", err + } + + scanner := bufio.NewScanner(bytes.NewReader(data)) + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + if len(fields) >= 2 { + switch { + case fields[1] != mountPoint: + case !strings.HasSuffix(strings.ToLower(fields[0]), ".appimage"): + default: + // Found AppImage mount! + return fields[0], nil + } + } + } + if scanner.Err() != nil { + return "", scanner.Err() + } + + return "", nil +}