Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
// start proc mon
if cmd == nil {
procMon := &ProcessMonitor{CorrelationId: config.CorrelationId, Repo: config.Repo,
ApiClient: apiclient, WorkingDirectory: config.WorkingDirectory, DisableFileMonitoring: config.DisableFileMonitoring, DNSProxy: &dnsProxy}
ApiClient: apiclient, WorkingDirectory: config.WorkingDirectory, DisableFileMonitoring: config.DisableFileMonitoring, ExemptFiles: config.ExemptFiles, DNSProxy: &dnsProxy}
go procMon.MonitorProcesses(errc)
WriteLog("started process monitor")
}
Expand Down
16 changes: 16 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type config struct {
DisableSudo bool
DisableSudoAndContainers bool
DisableFileMonitoring bool
ExemptFiles []string
Private bool
}

Expand All @@ -46,6 +47,7 @@ type configFile struct {
DisableSudo bool `json:"disable_sudo"`
DisableSudoAndContainers bool `json:"disable_sudo_and_containers"`
DisableFileMonitoring bool `json:"disable_file_monitoring"`
ExemptFiles string `json:"exempt_files"`
Private bool `json:"private"`
}

Expand Down Expand Up @@ -77,11 +79,25 @@ func (c *config) init(configFilePath string) error {
c.DisableSudo = configFile.DisableSudo
c.DisableSudoAndContainers = configFile.DisableSudoAndContainers
c.DisableFileMonitoring = configFile.DisableFileMonitoring
c.ExemptFiles = parseExemptFiles(configFile.ExemptFiles)
c.Private = configFile.Private
c.OneTimeKey = configFile.OneTimeKey
return nil
}

// parseExemptFiles splits the exempt_files config value into individual paths.
// It splits on newlines only so that paths containing spaces are preserved.
func parseExemptFiles(exemptFiles string) []string {
var files []string
for _, line := range strings.Split(exemptFiles, "\n") {
trimmed := strings.TrimSpace(line)
if len(trimmed) > 0 {
files = append(files, trimmed)
}
}
return files
}

func parseEndpoints(allowedEndpoints string) map[string][]Endpoint {
endpoints := make(map[string][]Endpoint)
endpointsArray := strings.Split(allowedEndpoints, " ")
Expand Down
24 changes: 24 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,30 @@ func Test_config_init(t *testing.T) {
}
}

func Test_parseExemptFiles(t *testing.T) {
type args struct {
exemptFiles string
}
tests := []struct {
name string
args args
want []string
}{
{name: "empty", args: args{exemptFiles: ""}, want: nil},
{name: "one path per line", args: args{exemptFiles: "dist/index.js\npackage-lock.json"}, want: []string{"dist/index.js", "package-lock.json"}},
{name: "trims whitespace and blank lines", args: args{exemptFiles: " dist/index.js \n\n go.sum "}, want: []string{"dist/index.js", "go.sum"}},
{name: "preserves spaces within a path", args: args{exemptFiles: "my dir/file.txt\nother.txt"}, want: []string{"my dir/file.txt", "other.txt"}},
{name: "handles CRLF", args: args{exemptFiles: "dist/index.js\r\npackage-lock.json\r\n"}, want: []string{"dist/index.js", "package-lock.json"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := parseExemptFiles(tt.args.exemptFiles); !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseExemptFiles() = %v, want %v", got, tt.want)
}
})
}
}

func Test_parseEndpoints(t *testing.T) {
type args struct {
allowedEndpoints string
Expand Down
16 changes: 15 additions & 1 deletion eventhandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type EventHandler struct {
ProcessMap map[string]*Process
SourceCodeMap map[string][]*Event
FileOverwriteCounterMap map[string]int // to count file overwrites by an exe
ExemptFiles []string // files exempted from overwrite detection
netMutex sync.RWMutex
fileMutex sync.RWMutex
procMutex sync.RWMutex
Expand Down Expand Up @@ -60,7 +61,7 @@ func (eventHandler *EventHandler) handleFileEvent(event *Event) {
// Uncomment to log file writes (only uncomment in INT env)
// WriteLog(fmt.Sprintf("file write %s, syscall %s", event.FileName, event.Syscall))

if isSourceCodeFile(event.FileName) {
if isSourceCodeFile(event.FileName) && !eventHandler.isExemptFile(event.FileName) {
eventHandler.fileMutex.Lock()
defer eventHandler.fileMutex.Unlock()

Expand Down Expand Up @@ -103,6 +104,19 @@ func isSourceCodeFile(fileName string) bool {
return false
}

// isExemptFile returns true when the file matches one of the configured exempt
// paths. Exempt entries are usually repo-relative while events carry absolute
// paths, so we match on the full path or a trailing path segment.
func (eventHandler *EventHandler) isExemptFile(fileName string) bool {
for _, exempt := range eventHandler.ExemptFiles {
if fileName == exempt || strings.HasSuffix(fileName, "/"+exempt) {
return true
}
}

return false
}

func (eventHandler *EventHandler) handleProcessEvent(event *Event) {
eventHandler.procMutex.Lock()
_, found := eventHandler.ProcessMap[event.Pid]
Expand Down
26 changes: 26 additions & 0 deletions eventhandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,32 @@ func TestEventHandler_HandleEvent(t *testing.T) {
}
}

func TestEventHandler_isExemptFile(t *testing.T) {
eventHandler := &EventHandler{
ExemptFiles: []string{"dist/index.js", "package-lock.json", "my dir/notes.txt"},
}

tests := []struct {
name string
fileName string
want bool
}{
{name: "repo-relative path suffix match", fileName: "/home/runner/work/repo/repo/dist/index.js", want: true},
{name: "single filename suffix match", fileName: "/home/runner/work/repo/repo/package-lock.json", want: true},
{name: "exact match", fileName: "dist/index.js", want: true},
{name: "path with space preserved", fileName: "/home/runner/work/repo/my dir/notes.txt", want: true},
{name: "non-exempt file", fileName: "/home/runner/work/repo/repo/src/main.go", want: false},
{name: "partial name is not exempted", fileName: "/home/runner/work/repo/repo/not-package-lock.json", want: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := eventHandler.isExemptFile(tt.fileName); got != tt.want {
t.Errorf("isExemptFile(%q) = %v, want %v", tt.fileName, got, tt.want)
}
})
}
}

func TestGetContainerIdByPid(t *testing.T) {
type args struct {
cgroupPath string
Expand Down
1 change: 1 addition & 0 deletions procmon.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type ProcessMonitor struct {
DNSProxy *DNSProxy
WorkingDirectory string
DisableFileMonitoring bool
ExemptFiles []string
Events map[int]*Event
mutex sync.RWMutex
}
Expand Down
2 changes: 1 addition & 1 deletion procmon_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (p *ProcessMonitor) MonitorProcesses(errc chan error) {
func (p *ProcessMonitor) receive(r *libaudit.AuditClient) error {

p.Events = make(map[int]*Event)
eventHandler := EventHandler{CorrelationId: p.CorrelationId, Repo: p.Repo, ApiClient: p.ApiClient, DNSProxy: p.DNSProxy}
eventHandler := EventHandler{CorrelationId: p.CorrelationId, Repo: p.Repo, ApiClient: p.ApiClient, DNSProxy: p.DNSProxy, ExemptFiles: p.ExemptFiles}
eventHandler.ProcessConnectionMap = make(map[string]bool)
eventHandler.ProcessFileMap = make(map[string]bool)
eventHandler.SourceCodeMap = make(map[string][]*Event)
Expand Down