mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
Derive profile ID from fingerprints
This commit is contained in:
parent
bd410afee6
commit
e81953d8f3
3 changed files with 131 additions and 2 deletions
|
@ -4,6 +4,10 @@ import (
|
|||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/safing/jess/lhash"
|
||||
"github.com/safing/portbase/container"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// # Matching and Scores
|
||||
|
@ -57,6 +61,12 @@ type (
|
|||
Key string // Key must always fully match.
|
||||
Operation string
|
||||
Value string
|
||||
|
||||
// MergedFrom holds the ID of the profile from which this fingerprint was
|
||||
// merged from. The merged profile should create a new profile ID derived
|
||||
// from the new fingerprints and add all fingerprints with this field set
|
||||
// to the originating profile ID
|
||||
MergedFrom string
|
||||
}
|
||||
|
||||
// Tag represents a simple key/value kind of tag used in process metadata
|
||||
|
@ -347,3 +357,62 @@ func checkMatchStrength(value int) int {
|
|||
}
|
||||
return value
|
||||
}
|
||||
|
||||
const (
|
||||
deriveFPKeyIDForItemStart = iota + 1
|
||||
deriveFPKeyIDForType
|
||||
deriveFPKeyIDForKey
|
||||
deriveFPKeyIDForOperation
|
||||
deriveFPKeyIDForValue
|
||||
)
|
||||
|
||||
func deriveProfileID(fps []Fingerprint) string {
|
||||
// Sort the fingerprints.
|
||||
sortFingerprints(fps)
|
||||
|
||||
// Compile data for hashing.
|
||||
c := container.New(nil)
|
||||
c.AppendInt(len(fps))
|
||||
for _, fp := range fps {
|
||||
c.AppendNumber(deriveFPKeyIDForItemStart)
|
||||
if fp.Type != "" {
|
||||
c.AppendNumber(deriveFPKeyIDForType)
|
||||
c.AppendAsBlock([]byte(fp.Type))
|
||||
}
|
||||
if fp.Key != "" {
|
||||
c.AppendNumber(deriveFPKeyIDForKey)
|
||||
c.AppendAsBlock([]byte(fp.Key))
|
||||
}
|
||||
if fp.Operation != "" {
|
||||
c.AppendNumber(deriveFPKeyIDForOperation)
|
||||
c.AppendAsBlock([]byte(fp.Operation))
|
||||
}
|
||||
if fp.Value != "" {
|
||||
c.AppendNumber(deriveFPKeyIDForValue)
|
||||
c.AppendAsBlock([]byte(fp.Value))
|
||||
}
|
||||
}
|
||||
|
||||
// Hash and return.
|
||||
h := lhash.Digest(lhash.SHA3_256, c.CompileData())
|
||||
return h.Base58()
|
||||
}
|
||||
|
||||
func sortFingerprints(fps []Fingerprint) {
|
||||
slices.SortFunc[[]Fingerprint, Fingerprint](fps, func(a, b Fingerprint) int {
|
||||
switch {
|
||||
case a.Type != b.Type:
|
||||
return strings.Compare(a.Type, b.Type)
|
||||
case a.Key != b.Key:
|
||||
return strings.Compare(a.Key, b.Key)
|
||||
case a.Operation != b.Operation:
|
||||
return strings.Compare(a.Operation, b.Operation)
|
||||
case a.Value != b.Value:
|
||||
return strings.Compare(a.Value, b.Value)
|
||||
case a.MergedFrom != b.MergedFrom:
|
||||
return strings.Compare(a.MergedFrom, b.MergedFrom)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
53
profile/fingerprint_test.go
Normal file
53
profile/fingerprint_test.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package profile
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDeriveProfileID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fps := []Fingerprint{
|
||||
{
|
||||
Type: FingerprintTypePathID,
|
||||
Operation: FingerprintOperationEqualsID,
|
||||
Value: "/sbin/init",
|
||||
},
|
||||
{
|
||||
Type: FingerprintTypePathID,
|
||||
Operation: FingerprintOperationPrefixID,
|
||||
Value: "/",
|
||||
},
|
||||
{
|
||||
Type: FingerprintTypeEnvID,
|
||||
Key: "PORTMASTER_PROFILE",
|
||||
Operation: FingerprintOperationEqualsID,
|
||||
Value: "TEST-1",
|
||||
},
|
||||
{
|
||||
Type: FingerprintTypeTagID,
|
||||
Key: "tag-key-1",
|
||||
Operation: FingerprintOperationEqualsID,
|
||||
Value: "tag-key-2",
|
||||
},
|
||||
}
|
||||
|
||||
// Create rand source for shuffling.
|
||||
rnd := rand.New(rand.NewSource(time.Now().UnixNano())) //nolint:gosec
|
||||
|
||||
// Test 100 times.
|
||||
for i := 0; i < 100; i++ {
|
||||
// Shuffle fingerprints.
|
||||
rnd.Shuffle(len(fps), func(i, j int) {
|
||||
fps[i], fps[j] = fps[j], fps[i]
|
||||
})
|
||||
|
||||
// Check if fingerprint matches.
|
||||
id := deriveProfileID(fps)
|
||||
assert.Equal(t, "PTSRP7rdCnmvdjRoPMTrtjj7qk7PxR1a9YdBWUGwnZXJh2", id)
|
||||
}
|
||||
}
|
|
@ -265,9 +265,16 @@ func New(profile *Profile) *Profile {
|
|||
profile.Config = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// Generate random ID if none is given.
|
||||
// Generate ID if none is given.
|
||||
if profile.ID == "" {
|
||||
profile.ID = utils.RandomUUID("").String()
|
||||
if len(profile.Fingerprints) > 0 {
|
||||
// Derive from fingerprints.
|
||||
profile.ID = deriveProfileID(profile.Fingerprints)
|
||||
} else {
|
||||
// Generate random ID as fallback.
|
||||
log.Warningf("profile: creating new profile without fingerprints to derive ID from")
|
||||
profile.ID = utils.RandomUUID("").String()
|
||||
}
|
||||
}
|
||||
|
||||
// Make key from ID and source.
|
||||
|
|
Loading…
Add table
Reference in a new issue