diff --git a/.gitignore b/.gitignore
index e695e018..d8cc9d05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,7 +32,6 @@ Desktop.ini
.vscode/
-
# Temp
playground/_output
@@ -50,4 +49,5 @@ release/
docs/.vitepress/dist/
docs/.vitepress/cache/
-local/
\ No newline at end of file
+local/
+dist/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 02e5cacb..f98a4918 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,8 +2,32 @@
All notable changes to this project will be documented in this file.
+
-## Unreleased - feature/loadremote
+## EXPERIMENTAL FEATURE - feature/targetid
+
+**Commit**: [b84f0f9](https://github.com/xfhg/intercept/commit/b84f0f9)
+
+**Branch** [feature/targetid](https://github.com/xfhg/intercept/tree/feature/targetid)
+
+**Summary**: Fingerprint hosts for reporting --experimental
+
+### Breaking
+- Properties on Final SARIF report key names corrected to kebab case.
+
+### Added
+- Added Global hostData & hostFingerprint
+- Added "host-data" & "host-fingerprint" to Final SARIF Report
+
+### Changed
+- Properties on Final SARIF report key names corrected to kebab case.
+
+### Removed
+- None
+
+
+
+## FEATURE - feature/loadremote
**Commit**: [e95c0ed](https://github.com/xfhg/intercept/commit/e95c0ed)
diff --git a/cmd/policy.go b/cmd/policy.go
index 5dbe986b..e95c6d28 100644
--- a/cmd/policy.go
+++ b/cmd/policy.go
@@ -26,6 +26,12 @@ type Config struct {
ReportSchedule string `yaml:"report_schedule"`
} `yaml:"Flags"`
Metadata struct {
+ HostOS string `yaml:"host_os,omitempty"`
+ HostMAC string `yaml:"host_mac,omitempty"`
+ HostARCH string `yaml:"host_arch,omitempty"`
+ HostNAME string `yaml:"host_name,omitempty"`
+ HostFingerprint string `yaml:"host_fingerprint,omitempty"`
+ HostInfo string `yaml:"host_info,omitempty"`
MsgExitClean string `yaml:"MsgExitClean"`
MsgExitWarning string `yaml:"MsgExitWarning"`
MsgExitCritical string `yaml:"MsgExitCritical"`
@@ -146,14 +152,15 @@ func LoadPolicyFile(filename string) (*PolicyFile, error) {
// LoadRemotePolicy loads a policy file from a remote HTTPS endpoint
func LoadRemotePolicy(url string, expectedChecksum string) (*PolicyFile, error) {
// Create a temporary directory to store the downloaded file
- tempDir, err := os.MkdirTemp(outputDir, "_remote")
+ remoteDir := filepath.Join(outputDir, "_remote")
+ err := os.MkdirAll(remoteDir, 0755)
if err != nil {
return nil, fmt.Errorf("failed to create temporary directory: %w", err)
}
- defer os.RemoveAll(tempDir) // Clean up the temporary directory when done
+ defer os.RemoveAll(remoteDir) // Clean up the temporary directory when done
// Generate a temporary file name
- tempFile := filepath.Join(tempDir, "remote_policy.yaml")
+ tempFile := filepath.Join(remoteDir, "remote_policy.yaml")
// Create a resty client
client := resty.New()
diff --git a/cmd/root.go b/cmd/root.go
index c0164ebf..6aa4141b 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -20,6 +20,9 @@ var (
silentMode bool
nologMode bool
+ hostData string
+ hostFingerprint string
+
buildVersion string
buildSignature string
@@ -94,8 +97,30 @@ func setupLogging() {
log = zerolog.New(output).With().Timestamp().Logger()
if experimentalMode {
+
+ // ----------------------------------------------
+ // ---------------------------------------------- EXPERIMENTAL log caller debug
+ // ----------------------------------------------
+
log = zerolog.New(output).With().Timestamp().Logger().With().Caller().Logger()
// log = zerolog.New(output).With().Timestamp().Logger().With().Str("id", intercept_run_id).Logger()
+
+ // ----------------------------------------------
+ // ---------------------------------------------- EXPERIMENTAL feature/targetid
+ // ----------------------------------------------
+
+ hostInfo, err := GetHostInfo()
+ if err != nil {
+ log.Error().Msgf("Error gathering host info: %v\n", err)
+ }
+
+ hostData, hostFingerprint, err := FingerprintHost(hostInfo)
+ if err != nil {
+ log.Error().Msgf("Error generating fingerprint: %v\n", err)
+ }
+ log.Info().Msgf("Host Data: %s", hostData)
+ log.Info().Msgf("Host Fingerprint: %s", hostFingerprint)
+
}
if silentMode {
diff --git a/cmd/sarif.go b/cmd/sarif.go
index ed1ad4e1..6fef639b 100644
--- a/cmd/sarif.go
+++ b/cmd/sarif.go
@@ -8,6 +8,8 @@ import (
"path/filepath"
"strings"
"time"
+
+ "github.com/charlievieth/fastwalk"
)
// SARIFLevel represents the severity level in SARIF format
@@ -127,9 +129,14 @@ type ArtifactLocation struct {
}
type Region struct {
- StartLine int `json:"startLine"`
- StartColumn int `json:"startColumn"`
- EndColumn int `json:"endColumn"`
+ StartLine int `json:"startLine"`
+ StartColumn int `json:"startColumn"`
+ EndColumn int `json:"endColumn"`
+ Snippet Snippet `json:"snippet"`
+}
+
+type Snippet struct {
+ Text string `json:"text"`
}
type Invocation struct {
@@ -204,7 +211,8 @@ func GenerateSARIFReport(inputFile string, policy Policy) (SARIFReport, error) {
},
},
Properties: map[string]string{
- "result-type": "detail", "observe-run-id": policy.RunID,
+ "result-type": "detail",
+ "observe-run-id": policy.RunID,
"result-timestamp": timestamp,
"name": policy.Metadata.Name,
"description": policy.Metadata.Description,
@@ -234,6 +242,9 @@ func GenerateSARIFReport(inputFile string, policy Policy) (SARIFReport, error) {
StartLine: rgOutput.Data.LineNumber,
StartColumn: strings.Index(rgOutput.Data.Lines.Text, matchText) + 1,
EndColumn: strings.Index(rgOutput.Data.Lines.Text, matchText) + len(matchText) + 1,
+ Snippet: Snippet{
+ Text: matchText,
+ },
},
},
},
@@ -545,13 +556,15 @@ func MergeSARIFReports(commandLine string, perf Performance, isScheduled bool) (
ExecutionSuccessful: true,
CommandLine: commandLine,
Properties: map[string]string{
- "run_id": intercept_run_id,
- "start_time": perf.StartTime.Format(time.RFC3339),
- "end_time": perf.EndTime.Format(time.RFC3339),
- "execution_time_ms": fmt.Sprintf("%d", perf.Delta.Milliseconds()),
+ "run-id": intercept_run_id,
+ "start-time": perf.StartTime.Format(time.RFC3339),
+ "end-time": perf.EndTime.Format(time.RFC3339),
+ "execution-time-ms": fmt.Sprintf("%d", perf.Delta.Milliseconds()),
"environment": environment,
"debug": fmt.Sprintf("%v", debugOutput),
"report-timestamp": timestamp,
+ "host-data": hostData,
+ "host-fingerprint": hostFingerprint,
},
},
},
@@ -634,7 +647,7 @@ func cleanupSARIFFolder() error {
sarifDir = filepath.Join(outputDir, sarifDir)
}
- err := filepath.WalkDir(sarifDir, func(path string, d fs.DirEntry, err error) error {
+ err := fastwalk.Walk(nil, sarifDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err // Error accessing the path
}
diff --git a/cmd/target.go b/cmd/target.go
index d3580bc7..e8e2d860 100644
--- a/cmd/target.go
+++ b/cmd/target.go
@@ -3,10 +3,13 @@ package cmd
import (
"encoding/json"
"fmt"
+ "io/fs"
"os"
"path/filepath"
"regexp"
"strings"
+
+ "github.com/charlievieth/fastwalk"
)
type FileInfo struct {
@@ -21,12 +24,12 @@ func CalculateFileHashes(targetDir string) ([]FileInfo, error) {
ignorePaths := policyData.Config.Flags.Ignore
- err := filepath.Walk(targetDir, func(path string, info os.FileInfo, err error) error {
+ err := fastwalk.Walk(nil, targetDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
- if !info.IsDir() && !isIgnored(ignorePaths, path) {
+ if !d.IsDir() && !isIgnored(ignorePaths, path) {
hash, err := calculateSHA256(path)
if err != nil {
diff --git a/cmd/watch.go b/cmd/watch.go
index d1390222..7bf24c36 100644
--- a/cmd/watch.go
+++ b/cmd/watch.go
@@ -1,13 +1,27 @@
package cmd
import (
+ "crypto/sha256"
+ "encoding/hex"
"fmt"
+ "net"
+ "os"
+ "runtime"
+ "strings"
"time"
"github.com/fsnotify/fsnotify"
"github.com/segmentio/ksuid"
)
+type HostInfo struct {
+ Hostname string
+ OS string
+ Architecture string
+ IPs []string
+ MAC string
+}
+
func watchPaths(paths ...string) {
if len(paths) < 1 {
log.Fatal().Msg("must specify at least one path to watch")
@@ -95,3 +109,72 @@ func processEvent(e fsnotify.Event) {
log.Error().Msgf("Policy not found in cache, watcher event [%s] didn't trigger policy process for: %s", e.Op.String(), e.Name)
}
}
+
+func GetHostInfo() (*HostInfo, error) {
+ hostInfo := &HostInfo{}
+
+ // Get hostname
+ hostname, err := os.Hostname()
+ if err != nil {
+ return nil, fmt.Errorf("failed to get hostname: %v", err)
+ }
+ hostInfo.Hostname = hostname
+
+ // Get OS and architecture
+ hostInfo.OS = runtime.GOOS
+ hostInfo.Architecture = runtime.GOARCH
+
+ // Get IPs and MAC addresses
+ interfaces, err := net.Interfaces()
+ if err != nil {
+ return nil, fmt.Errorf("failed to get network interfaces: %v", err)
+ }
+
+ for _, iface := range interfaces {
+ if iface.Flags&net.FlagUp == 0 {
+ continue // ignore interfaces that are down
+ }
+
+ addrs, err := iface.Addrs()
+ if err != nil {
+ return nil, fmt.Errorf("failed to get addresses for interface %v: %v", iface.Name, err)
+ }
+
+ for _, addr := range addrs {
+ ip, _, err := net.ParseCIDR(addr.String())
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse IP address %v: %v", addr.String(), err)
+ }
+
+ if ip.IsLoopback() {
+ continue // ignore loopback addresses
+ }
+
+ hostInfo.IPs = append(hostInfo.IPs, ip.String())
+ }
+ // main MAC
+ if iface.Flags&net.FlagUp != 0 && iface.HardwareAddr.String() != "" {
+ hostInfo.MAC = iface.HardwareAddr.String()
+ }
+
+ }
+
+ return hostInfo, nil
+}
+
+// FingerprintHost generates a fingerprint for the host using its identifiable information
+func FingerprintHost(hostInfo *HostInfo) (string, string, error) {
+ data := strings.Join([]string{
+ hostInfo.MAC,
+ hostInfo.OS,
+ hostInfo.Architecture,
+ hostInfo.Hostname,
+ }, "|")
+ hash := sha256.New()
+ _, err := hash.Write([]byte(data))
+ if err != nil {
+ return "", "", fmt.Errorf("failed to generate hash: %v", err)
+ }
+ fingerprint := hex.EncodeToString(hash.Sum(nil))
+ return data, fingerprint, nil
+}
diff --git a/go.mod b/go.mod
index cdaf56f8..2a5c7130 100644
--- a/go.mod
+++ b/go.mod
@@ -46,6 +46,7 @@ require (
require (
cuelang.org/go v0.10.0
github.com/adhocore/gronx v1.19.0
+ github.com/charlievieth/fastwalk v1.0.8
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/fsnotify/fsnotify v1.7.0
github.com/go-resty/resty/v2 v2.14.0
diff --git a/go.sum b/go.sum
index d7611624..b0ab4356 100644
--- a/go.sum
+++ b/go.sum
@@ -20,6 +20,8 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/charlievieth/fastwalk v1.0.8 h1:uaoH6cAKSk73aK7aKXqs0+bL+J3Txzd3NGH8tRXgHko=
+github.com/charlievieth/fastwalk v1.0.8/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=