@@ -51,8 +51,10 @@ type Smith struct {
51
51
timeElapsedHandler func (t time.Time ) time.Duration
52
52
notifiedInfringements * lru.Cache
53
53
54
- detector detector.ProcessDetector
55
- classifier classifier.ProcessClassifier
54
+ detector detector.ProcessDetector
55
+ classifier classifier.ProcessClassifier
56
+ fileDetector detector.FileDetector
57
+ FileClassifier classifier.FileClassifier
56
58
}
57
59
58
60
// NewAgentSmith creates a new agent smith
@@ -135,6 +137,30 @@ func NewAgentSmith(cfg config.Config) (*Smith, error) {
135
137
return nil , err
136
138
}
137
139
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
+
138
164
m := newAgentMetrics ()
139
165
res := & Smith {
140
166
EnforcementRules : map [string ]config.EnforcementRules {
@@ -150,8 +176,10 @@ func NewAgentSmith(cfg config.Config) (*Smith, error) {
150
176
151
177
wsman : wsman ,
152
178
153
- detector : detec ,
154
- classifier : class ,
179
+ detector : detec ,
180
+ classifier : class ,
181
+ fileDetector : filesystemDetec ,
182
+ FileClassifier : filesystemClass ,
155
183
156
184
notifiedInfringements : lru .New (notificationCacheSize ),
157
185
metrics : m ,
@@ -227,17 +255,34 @@ type classifiedProcess struct {
227
255
Err error
228
256
}
229
257
258
+ type classifiedFilesystemFile struct {
259
+ F detector.File
260
+ C * classifier.Classification
261
+ Err error
262
+ }
263
+
230
264
// Start gets a stream of Infringements from Run and executes a callback on them to apply a Penalty
231
265
func (agent * Smith ) Start (ctx context.Context , callback func (InfringingWorkspace , []config.PenaltyKind )) {
232
266
ps , err := agent .detector .DiscoverProcesses (ctx )
233
267
if err != nil {
234
268
log .WithError (err ).Fatal ("cannot start process detector" )
235
269
}
236
270
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
+
237
280
var (
238
281
wg sync.WaitGroup
239
282
cli = make (chan detector.Process , 500 )
240
283
clo = make (chan classifiedProcess , 50 )
284
+ fli = make (chan detector.File , 100 )
285
+ flo = make (chan classifiedFilesystemFile , 25 )
241
286
)
242
287
agent .metrics .RegisterClassificationQueues (cli , clo )
243
288
@@ -268,6 +313,27 @@ func (agent *Smith) Start(ctx context.Context, callback func(InfringingWorkspace
268
313
}()
269
314
}
270
315
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
+
271
337
defer log .Info ("agent smith main loop ended" )
272
338
273
339
// 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
288
354
// we're overfilling the classifier worker
289
355
agent .metrics .classificationBackpressureInDrop .Inc ()
290
356
}
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
+ }
291
366
}
292
367
}
293
368
}()
@@ -319,6 +394,33 @@ func (agent *Smith) Start(ctx context.Context, callback func(InfringingWorkspace
319
394
},
320
395
},
321
396
})
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
+ })
322
424
}
323
425
}
324
426
}
@@ -420,10 +522,22 @@ func (agent *Smith) Describe(d chan<- *prometheus.Desc) {
420
522
agent .metrics .Describe (d )
421
523
agent .classifier .Describe (d )
422
524
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
+ }
423
531
}
424
532
425
533
func (agent * Smith ) Collect (m chan <- prometheus.Metric ) {
426
534
agent .metrics .Collect (m )
427
535
agent .classifier .Collect (m )
428
536
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
+ }
429
543
}
0 commit comments