@@ -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,32 @@ 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.FileScanningConfig {
146+ Enabled : cfg .FilesystemScanning .Enabled ,
147+ ScanInterval : cfg .FilesystemScanning .ScanInterval .Duration ,
148+ MaxFileSize : cfg .FilesystemScanning .MaxFileSize ,
149+ WorkingArea : cfg .FilesystemScanning .WorkingArea ,
150+ }
151+
152+ // Create independent filesystem classifier (no dependency on process classifier)
153+ filesystemClass , err = cfg .Blocklists .FileClassifier ()
154+ if err != nil {
155+ log .WithError (err ).Error ("failed to create filesystem classifier" )
156+ } else {
157+ filesystemDetec , err = detector .NewfileDetector (fsConfig , filesystemClass )
158+ if err != nil {
159+ log .WithError (err ).Error ("failed to create filesystem detector" )
160+ } else {
161+ log .Info ("Filesystem detector created successfully with independent classifier" )
162+ }
163+ }
164+ }
165+
138166 m := newAgentMetrics ()
139167 res := & Smith {
140168 EnforcementRules : map [string ]config.EnforcementRules {
@@ -150,8 +178,10 @@ func NewAgentSmith(cfg config.Config) (*Smith, error) {
150178
151179 wsman : wsman ,
152180
153- detector : detec ,
154- classifier : class ,
181+ detector : detec ,
182+ classifier : class ,
183+ fileDetector : filesystemDetec ,
184+ fileClassifier : filesystemClass ,
155185
156186 notifiedInfringements : lru .New (notificationCacheSize ),
157187 metrics : m ,
@@ -227,17 +257,34 @@ type classifiedProcess struct {
227257 Err error
228258}
229259
260+ type classifiedFile struct {
261+ F detector.File
262+ C * classifier.Classification
263+ Err error
264+ }
265+
230266// Start gets a stream of Infringements from Run and executes a callback on them to apply a Penalty
231267func (agent * Smith ) Start (ctx context.Context , callback func (InfringingWorkspace , []config.PenaltyKind )) {
232268 ps , err := agent .detector .DiscoverProcesses (ctx )
233269 if err != nil {
234270 log .WithError (err ).Fatal ("cannot start process detector" )
235271 }
236272
273+ // Start filesystem detection if enabled
274+ var fs <- chan detector.File
275+ if agent .fileDetector != nil {
276+ fs , err = agent .fileDetector .DiscoverFiles (ctx )
277+ if err != nil {
278+ log .WithError (err ).Warn ("cannot start filesystem detector" )
279+ }
280+ }
281+
237282 var (
238283 wg sync.WaitGroup
239284 cli = make (chan detector.Process , 500 )
240285 clo = make (chan classifiedProcess , 50 )
286+ fli = make (chan detector.File , 100 )
287+ flo = make (chan classifiedFile , 25 )
241288 )
242289 agent .metrics .RegisterClassificationQueues (cli , clo )
243290
@@ -268,6 +315,25 @@ func (agent *Smith) Start(ctx context.Context, callback func(InfringingWorkspace
268315 }()
269316 }
270317
318+ // Filesystem classification workers (fewer than process workers)
319+ if agent .fileClassifier != nil {
320+ for i := 0 ; i < 5 ; i ++ {
321+ wg .Add (1 )
322+ go func () {
323+ defer wg .Done ()
324+ for file := range fli {
325+ class , err := agent .fileClassifier .MatchesFile (file .Path )
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 <- classifiedFile {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,32 @@ 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+
405+ log .WithField ("path" , file .Path ).WithField ("severity" , cl .Level ).WithField ("message" , cl .Message ).
406+ WithFields (log .OWI (file .Workspace .OwnerID , file .Workspace .WorkspaceID , file .Workspace .InstanceID )).
407+ Info ("filesystem signature detected" )
408+
409+ _ , _ = agent .Penalize (InfringingWorkspace {
410+ SupervisorPID : file .Workspace .PID ,
411+ Owner : file .Workspace .OwnerID ,
412+ InstanceID : file .Workspace .InstanceID ,
413+ WorkspaceID : file .Workspace .WorkspaceID ,
414+ GitRemoteURL : []string {file .Workspace .GitURL },
415+ Infringements : []Infringement {
416+ {
417+ Kind : config .GradeKind (config .InfringementExec , common .Severity (cl .Level )), // Reuse exec for now
418+ Description : fmt .Sprintf ("filesystem signature: %s" , cl .Message ),
419+ CommandLine : []string {file .Path }, // Use file path as "command"
420+ },
421+ },
422+ })
322423 }
323424 }
324425}
0 commit comments