@@ -51,8 +51,10 @@ type Smith struct {
5151	timeElapsedHandler     func (t  time.Time ) time.Duration 
5252	notifiedInfringements  * lru.Cache 
5353
54- 	detector    detector.ProcessDetector 
55- 	classifier  classifier.ProcessClassifier 
54+ 	detector        detector.ProcessDetector 
55+ 	classifier      classifier.ProcessClassifier 
56+ 	fileDetector    detector.FileDetector 
57+ 	FileClassifier  classifier.FileClassifier 
5658}
5759
5860// NewAgentSmith creates a new agent smith 
@@ -135,6 +137,30 @@ func NewAgentSmith(cfg config.Config) (*Smith, error) {
135137		return  nil , err 
136138	}
137139
140+ 	// Initialize filesystem detection if enabled 
141+ 	var  filesystemDetec  detector.FileDetector 
142+ 	var  filesystemClass  classifier.FileClassifier 
143+ 	if  cfg .FilesystemScanning  !=  nil  &&  cfg .FilesystemScanning .Enabled  {
144+ 		// Create filesystem detector config 
145+ 		fsConfig  :=  detector.FilesystemScanningConfig {
146+ 			Enabled :      cfg .FilesystemScanning .Enabled ,
147+ 			ScanInterval : cfg .FilesystemScanning .ScanInterval .Duration ,
148+ 			MaxFileSize :  cfg .FilesystemScanning .MaxFileSize ,
149+ 			WorkingArea :  cfg .FilesystemScanning .WorkingArea ,
150+ 		}
151+ 
152+ 		// Check if the main classifier supports filesystem detection 
153+ 		if  fsc , ok  :=  class .(classifier.FileClassifier ); ok  {
154+ 			filesystemClass  =  fsc 
155+ 			filesystemDetec , err  =  detector .NewfileDetector (fsConfig , filesystemClass )
156+ 			if  err  !=  nil  {
157+ 				log .WithError (err ).Warn ("failed to create filesystem detector" )
158+ 			}
159+ 		} else  {
160+ 			log .Warn ("classifier does not support filesystem detection, filesystem scanning disabled" )
161+ 		}
162+ 	}
163+ 
138164	m  :=  newAgentMetrics ()
139165	res  :=  & Smith {
140166		EnforcementRules : map [string ]config.EnforcementRules {
@@ -150,8 +176,10 @@ func NewAgentSmith(cfg config.Config) (*Smith, error) {
150176
151177		wsman : wsman ,
152178
153- 		detector :   detec ,
154- 		classifier : class ,
179+ 		detector :       detec ,
180+ 		classifier :     class ,
181+ 		fileDetector :   filesystemDetec ,
182+ 		FileClassifier : filesystemClass ,
155183
156184		notifiedInfringements : lru .New (notificationCacheSize ),
157185		metrics :               m ,
@@ -227,17 +255,34 @@ type classifiedProcess struct {
227255	Err  error 
228256}
229257
258+ type  classifiedFilesystemFile  struct  {
259+ 	F    detector.File 
260+ 	C    * classifier.Classification 
261+ 	Err  error 
262+ }
263+ 
230264// Start gets a stream of Infringements from Run and executes a callback on them to apply a Penalty 
231265func  (agent  * Smith ) Start (ctx  context.Context , callback  func (InfringingWorkspace , []config.PenaltyKind )) {
232266	ps , err  :=  agent .detector .DiscoverProcesses (ctx )
233267	if  err  !=  nil  {
234268		log .WithError (err ).Fatal ("cannot start process detector" )
235269	}
236270
271+ 	// Start filesystem detection if enabled 
272+ 	var  fs  <- chan  detector.File 
273+ 	if  agent .fileDetector  !=  nil  {
274+ 		fs , err  =  agent .fileDetector .DiscoverFiles (ctx )
275+ 		if  err  !=  nil  {
276+ 			log .WithError (err ).Warn ("cannot start filesystem detector" )
277+ 		}
278+ 	}
279+ 
237280	var  (
238281		wg   sync.WaitGroup 
239282		cli  =  make (chan  detector.Process , 500 )
240283		clo  =  make (chan  classifiedProcess , 50 )
284+ 		fli  =  make (chan  detector.File , 100 )
285+ 		flo  =  make (chan  classifiedFilesystemFile , 25 )
241286	)
242287	agent .metrics .RegisterClassificationQueues (cli , clo )
243288
@@ -268,6 +313,27 @@ func (agent *Smith) Start(ctx context.Context, callback func(InfringingWorkspace
268313		}()
269314	}
270315
316+ 	// Filesystem classification workers (fewer than process workers) 
317+ 	if  agent .FileClassifier  !=  nil  {
318+ 		for  i  :=  0 ; i  <  5 ; i ++  {
319+ 			wg .Add (1 )
320+ 			go  func () {
321+ 				defer  wg .Done ()
322+ 				for  file  :=  range  fli  {
323+ 					log .Infof ("Classifying filesystem file: %s" , file .Path )
324+ 					class , err  :=  agent .FileClassifier .MatchesFile (file .Path )
325+ 					// Early out for no matches 
326+ 					if  err  ==  nil  &&  class .Level  ==  classifier .LevelNoMatch  {
327+ 						log .Infof ("File classification: no match - %s" , file .Path )
328+ 						continue 
329+ 					}
330+ 					log .Infof ("File classification result: %s (level: %s, err: %v)" , file .Path , class .Level , err )
331+ 					flo  <-  classifiedFilesystemFile {F : file , C : class , Err : err }
332+ 				}
333+ 			}()
334+ 		}
335+ 	}
336+ 
271337	defer  log .Info ("agent smith main loop ended" )
272338
273339	// We want to fill the classifier in a Go routine seaparete from using the classification 
@@ -288,6 +354,15 @@ func (agent *Smith) Start(ctx context.Context, callback func(InfringingWorkspace
288354					// we're overfilling the classifier worker 
289355					agent .metrics .classificationBackpressureInDrop .Inc ()
290356				}
357+ 			case  file , ok  :=  <- fs :
358+ 				if  ! ok  {
359+ 					continue 
360+ 				}
361+ 				select  {
362+ 				case  fli  <-  file :
363+ 				default :
364+ 					// filesystem queue full, skip this file 
365+ 				}
291366			}
292367		}
293368	}()
@@ -319,6 +394,33 @@ func (agent *Smith) Start(ctx context.Context, callback func(InfringingWorkspace
319394					},
320395				},
321396			})
397+ 		case  fileClass  :=  <- flo :
398+ 			log .Infof ("Received classified file from flo channel" )
399+ 			file , cl , err  :=  fileClass .F , fileClass .C , fileClass .Err 
400+ 			if  err  !=  nil  {
401+ 				log .WithError (err ).WithFields (log .OWI (file .Workspace .OwnerID , file .Workspace .WorkspaceID , file .Workspace .InstanceID )).WithField ("path" , file .Path ).Error ("cannot classify filesystem file" )
402+ 				continue 
403+ 			}
404+ 			if  cl  ==  nil  ||  cl .Level  ==  classifier .LevelNoMatch  {
405+ 				log .Warn ("filesystem signature not detected" , "path" , file .Path , "workspace" , file .Workspace .WorkspaceID )
406+ 				continue 
407+ 			}
408+ 
409+ 			log .Info ("filesystem signature detected" , "path" , file .Path , "workspace" , file .Workspace .WorkspaceID , "severity" , cl .Level , "message" , cl .Message )
410+ 			_ , _  =  agent .Penalize (InfringingWorkspace {
411+ 				SupervisorPID : file .Workspace .PID ,
412+ 				Owner :         file .Workspace .OwnerID ,
413+ 				InstanceID :    file .Workspace .InstanceID ,
414+ 				WorkspaceID :   file .Workspace .WorkspaceID ,
415+ 				GitRemoteURL :  []string {file .Workspace .GitURL },
416+ 				Infringements : []Infringement {
417+ 					{
418+ 						Kind :        config .GradeKind (config .InfringementExec , common .Severity (cl .Level )), // Reuse exec for now 
419+ 						Description : fmt .Sprintf ("filesystem signature: %s" , cl .Message ),
420+ 						CommandLine : []string {file .Path }, // Use file path as "command" 
421+ 					},
422+ 				},
423+ 			})
322424		}
323425	}
324426}
@@ -420,10 +522,22 @@ func (agent *Smith) Describe(d chan<- *prometheus.Desc) {
420522	agent .metrics .Describe (d )
421523	agent .classifier .Describe (d )
422524	agent .detector .Describe (d )
525+ 	if  agent .fileDetector  !=  nil  {
526+ 		agent .fileDetector .Describe (d )
527+ 	}
528+ 	if  agent .FileClassifier  !=  nil  {
529+ 		agent .FileClassifier .Describe (d )
530+ 	}
423531}
424532
425533func  (agent  * Smith ) Collect (m  chan <-  prometheus.Metric ) {
426534	agent .metrics .Collect (m )
427535	agent .classifier .Collect (m )
428536	agent .detector .Collect (m )
537+ 	if  agent .fileDetector  !=  nil  {
538+ 		agent .fileDetector .Collect (m )
539+ 	}
540+ 	if  agent .FileClassifier  !=  nil  {
541+ 		agent .FileClassifier .Collect (m )
542+ 	}
429543}
0 commit comments