mirror of
https://github.com/safing/portmaster
synced 2025-09-02 02:29:12 +00:00
Merge pull request #1364 from safing/feature/unix-tag-improvements
Improve unix process matching tags
This commit is contained in:
commit
1a0be5fdde
3 changed files with 232 additions and 33 deletions
|
@ -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"
|
||||||
|
@ -16,8 +21,14 @@ 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,36 +45,77 @@ 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) {
|
||||||
// Get and verify AppImage location.
|
// Detect app image path via ENV vars.
|
||||||
appImageLocation, ok := p.Env["APPIMAGE"]
|
func() {
|
||||||
if !ok {
|
// Get and verify AppImage location.
|
||||||
return
|
appImageLocation, ok := p.Env["APPIMAGE"]
|
||||||
}
|
if !ok || appImageLocation == "" {
|
||||||
appImageMountDir, ok := p.Env["APPDIR"]
|
return
|
||||||
if !ok {
|
}
|
||||||
return
|
appImageMountDir, ok := p.Env["APPDIR"]
|
||||||
}
|
if !ok || appImageMountDir == "" {
|
||||||
// Check if the process path is in the mount dir.
|
return
|
||||||
if !strings.HasPrefix(p.Path, 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.
|
// 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
|
||||||
|
}
|
||||||
|
|
87
process/tags/flatpak_unix.go
Normal file
87
process/tags/flatpak_unix.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package tags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/safing/portbase/utils/osdetail"
|
||||||
|
"github.com/safing/portmaster/process"
|
||||||
|
"github.com/safing/portmaster/profile"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
err := process.RegisterTagHandler(new(flatpakHandler))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
flatpakName = "Flatpak"
|
||||||
|
flatpakIDTagKey = "flatpak-id"
|
||||||
|
)
|
||||||
|
|
||||||
|
// flatpakHandler handles flatpak processes on Unix systems.
|
||||||
|
type flatpakHandler struct{}
|
||||||
|
|
||||||
|
// Name returns the tag handler name.
|
||||||
|
func (h *flatpakHandler) Name() string {
|
||||||
|
return flatpakName
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagDescriptions returns a list of all possible tags and their description
|
||||||
|
// of this handler.
|
||||||
|
func (h *flatpakHandler) TagDescriptions() []process.TagDescription {
|
||||||
|
return []process.TagDescription{
|
||||||
|
{
|
||||||
|
ID: flatpakIDTagKey,
|
||||||
|
Name: "Flatpak ID",
|
||||||
|
Description: "ID of the flatpak.",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTags adds tags to the given process.
|
||||||
|
func (h *flatpakHandler) AddTags(p *process.Process) {
|
||||||
|
// Check if binary lives in the /app space.
|
||||||
|
if !strings.HasPrefix(p.Path, "/app/") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the Flatpak ID.
|
||||||
|
flatpakID, ok := p.Env["FLATPAK_ID"]
|
||||||
|
if !ok || flatpakID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add matching path for regular profile matching.
|
||||||
|
p.MatchingPath = p.Path
|
||||||
|
|
||||||
|
// Add app image tag.
|
||||||
|
p.Tags = append(p.Tags, profile.Tag{
|
||||||
|
Key: flatpakIDTagKey,
|
||||||
|
Value: flatpakID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProfile creates a profile based on the tags of the process.
|
||||||
|
// Returns nil to skip.
|
||||||
|
func (h *flatpakHandler) CreateProfile(p *process.Process) *profile.Profile {
|
||||||
|
if tag, ok := p.GetTag(flatpakIDTagKey); 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 flatpakIDTagKey.
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -7,10 +7,12 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/google/shlex"
|
"github.com/google/shlex"
|
||||||
|
|
||||||
|
"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"
|
||||||
)
|
)
|
||||||
|
@ -24,7 +26,8 @@ func init() {
|
||||||
type interpType struct {
|
type interpType struct {
|
||||||
process.TagDescription
|
process.TagDescription
|
||||||
|
|
||||||
Regex *regexp.Regexp
|
Extensions []string
|
||||||
|
Regex *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
var knownInterperters = []interpType{
|
var knownInterperters = []interpType{
|
||||||
|
@ -33,35 +36,40 @@ var knownInterperters = []interpType{
|
||||||
ID: "python-script",
|
ID: "python-script",
|
||||||
Name: "Python Script",
|
Name: "Python Script",
|
||||||
},
|
},
|
||||||
Regex: regexp.MustCompile(`^(/usr)?/bin/python[23]\.[0-9]+$`),
|
Extensions: []string{".py", ".py2", ".py3"},
|
||||||
|
Regex: regexp.MustCompile(`^(/usr)?/bin/python[23](\.[0-9]+)?$`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
TagDescription: process.TagDescription{
|
TagDescription: process.TagDescription{
|
||||||
ID: "shell-script",
|
ID: "shell-script",
|
||||||
Name: "Shell Script",
|
Name: "Shell Script",
|
||||||
},
|
},
|
||||||
Regex: regexp.MustCompile(`^(/usr)?/bin/(ba|k|z|a)?sh$`),
|
Extensions: []string{".sh", ".bash", ".ksh", ".zsh", ".ash"},
|
||||||
|
Regex: regexp.MustCompile(`^(/usr)?/bin/(ba|k|z|a)?sh$`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
TagDescription: process.TagDescription{
|
TagDescription: process.TagDescription{
|
||||||
ID: "perl-script",
|
ID: "perl-script",
|
||||||
Name: "Perl Script",
|
Name: "Perl Script",
|
||||||
},
|
},
|
||||||
Regex: regexp.MustCompile(`^(/usr)?/bin/perl$`),
|
Extensions: []string{".pl"},
|
||||||
|
Regex: regexp.MustCompile(`^(/usr)?/bin/perl$`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
TagDescription: process.TagDescription{
|
TagDescription: process.TagDescription{
|
||||||
ID: "ruby-script",
|
ID: "ruby-script",
|
||||||
Name: "Ruby Script",
|
Name: "Ruby Script",
|
||||||
},
|
},
|
||||||
Regex: regexp.MustCompile(`^(/usr)?/bin/ruby$`),
|
Extensions: []string{".rb"},
|
||||||
|
Regex: regexp.MustCompile(`^(/usr)?/bin/ruby$`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
TagDescription: process.TagDescription{
|
TagDescription: process.TagDescription{
|
||||||
ID: "nodejs-script",
|
ID: "nodejs-script",
|
||||||
Name: "NodeJS Script",
|
Name: "NodeJS Script",
|
||||||
},
|
},
|
||||||
Regex: regexp.MustCompile(`^(/usr)?/bin/node(js)?$`),
|
Extensions: []string{".js"},
|
||||||
|
Regex: regexp.MustCompile(`^(/usr)?/bin/node(js)?$`),
|
||||||
},
|
},
|
||||||
/*
|
/*
|
||||||
While similar to nodejs, electron is a bit harder as it uses a multiple processes
|
While similar to nodejs, electron is a bit harder as it uses a multiple processes
|
||||||
|
@ -148,16 +156,23 @@ func (h *InterpHandler) CreateProfile(p *process.Process) *profile.Profile {
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a nice script name from filename.
|
||||||
|
scriptName := filepath.Base(args[0])
|
||||||
|
for _, ext := range it.Extensions {
|
||||||
|
scriptName, _ = strings.CutSuffix(scriptName, ext)
|
||||||
|
}
|
||||||
|
scriptName = osdetail.GenerateBinaryNameFromPath(scriptName)
|
||||||
|
|
||||||
return profile.New(&profile.Profile{
|
return profile.New(&profile.Profile{
|
||||||
Source: profile.SourceLocal,
|
Source: profile.SourceLocal,
|
||||||
Name: fmt.Sprintf("%s: %s", it.Name, args[0]),
|
Name: fmt.Sprintf("%s: %s", it.Name, scriptName),
|
||||||
PresentationPath: tag.Value,
|
PresentationPath: tag.Value,
|
||||||
UsePresentationPath: true,
|
UsePresentationPath: true,
|
||||||
Fingerprints: []profile.Fingerprint{
|
Fingerprints: []profile.Fingerprint{
|
||||||
{
|
{
|
||||||
Type: profile.FingerprintTypeTagID,
|
Type: profile.FingerprintTypeTagID,
|
||||||
Operation: profile.FingerprintOperationEqualsID,
|
|
||||||
Key: it.ID,
|
Key: it.ID,
|
||||||
|
Operation: profile.FingerprintOperationEqualsID,
|
||||||
Value: tag.Value,
|
Value: tag.Value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Reference in a new issue