Improve AppImage matching, even without ENV vars

This commit is contained in:
Daniel 2023-11-17 12:21:10 +01:00
parent beed574fa3
commit 130cc40fea

View file

@ -1,8 +1,13 @@
package tags package tags
import ( import (
"bufio"
"bytes"
"os"
"regexp"
"strings" "strings"
"github.com/safing/portbase/log"
"github.com/safing/portbase/utils/osdetail" "github.com/safing/portbase/utils/osdetail"
"github.com/safing/portmaster/process" "github.com/safing/portmaster/process"
"github.com/safing/portmaster/profile" "github.com/safing/portmaster/profile"
@ -18,6 +23,12 @@ func init() {
const ( const (
appImageName = "AppImage" appImageName = "AppImage"
appImagePathTagKey = "app-image-path" 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. // AppImageHandler handles AppImage processes on Unix systems.
@ -34,21 +45,28 @@ func (h *AppImageHandler) TagDescriptions() []process.TagDescription {
return []process.TagDescription{ return []process.TagDescription{
{ {
ID: appImagePathTagKey, ID: appImagePathTagKey,
Name: "App Image Path", Name: "AppImage Path",
Description: "Path to the app image file itself.", 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. // AddTags adds tags to the given process.
func (h *AppImageHandler) AddTags(p *process.Process) { func (h *AppImageHandler) AddTags(p *process.Process) {
// Detect app image path via ENV vars.
func() {
// Get and verify AppImage location. // Get and verify AppImage location.
appImageLocation, ok := p.Env["APPIMAGE"] appImageLocation, ok := p.Env["APPIMAGE"]
if !ok { if !ok || appImageLocation == "" {
return return
} }
appImageMountDir, ok := p.Env["APPDIR"] appImageMountDir, ok := p.Env["APPDIR"]
if !ok { if !ok || appImageMountDir == "" {
return return
} }
// Check if the process path is in the mount dir. // Check if the process path is in the mount dir.
@ -59,11 +77,45 @@ func (h *AppImageHandler) AddTags(p *process.Process) {
// Add matching path for regular profile matching. // Add matching path for regular profile matching.
p.MatchingPath = appImageLocation p.MatchingPath = appImageLocation
// Add app image tags. // Add app image tag.
p.Tags = append(p.Tags, profile.Tag{ p.Tags = append(p.Tags, profile.Tag{
Key: appImagePathTagKey, Key: appImagePathTagKey,
Value: appImageLocation, 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. // 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 { if tag, ok := p.GetTag(appImagePathTagKey); ok {
return profile.New(&profile.Profile{ return profile.New(&profile.Profile{
Source: profile.SourceLocal, Source: profile.SourceLocal,
Name: osdetail.GenerateBinaryNameFromPath(tag.Value), Name: osdetail.GenerateBinaryNameFromPath(p.Path),
PresentationPath: p.Path, PresentationPath: p.Path,
UsePresentationPath: true, UsePresentationPath: true,
Fingerprints: []profile.Fingerprint{ Fingerprints: []profile.Fingerprint{
{ {
Type: profile.FingerprintTypePathID, Type: profile.FingerprintTypeTagID,
Key: tag.Key,
Operation: profile.FingerprintOperationEqualsID, Operation: profile.FingerprintOperationEqualsID,
Value: tag.Value, // Value of appImagePathTagKey. 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 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
}