github.com/chainreactors/fingers@v1.2.1/fingers/level_send_data_test.go (about)

     1  package fingers
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  )
     7  
     8  func rawHTTP(body string) []byte {
     9  	return []byte("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" + body)
    10  }
    11  
    12  func contains(xs []string, want string) bool {
    13  	for _, x := range xs {
    14  		if x == want {
    15  			return true
    16  		}
    17  	}
    18  	return false
    19  }
    20  
    21  // TestLevelSendDataRecognition verifies that:
    22  // 1) level 0 performs no active probing,
    23  // 2) level 1 uses finger-level send_data only,
    24  // 3) level 2 additionally uses rule-level send_data.
    25  func TestLevelSendDataRecognition(t *testing.T) {
    26  	const (
    27  		fingerPath = "/finger-level"
    28  		rulePath   = "/rule-level"
    29  	)
    30  
    31  	finger := &Finger{
    32  		Name:        "level-test",
    33  		Protocol:    HTTPProtocol,
    34  		DefaultPort: []string{"80"},
    35  		SendDataStr: fingerPath,
    36  		Rules: Rules{
    37  			{
    38  				Regexps: &Regexps{
    39  					Body: []string{"finger-hit"},
    40  				},
    41  				Level: 1,
    42  			},
    43  			{
    44  				Regexps: &Regexps{
    45  					Body: []string{"rule-hit"},
    46  				},
    47  				SendDataStr: rulePath,
    48  				Level:       2,
    49  			},
    50  		},
    51  	}
    52  
    53  	if err := finger.Compile(false); err != nil {
    54  		t.Fatalf("failed to compile finger: %v", err)
    55  	}
    56  
    57  	responses := map[string][]byte{
    58  		fingerPath: rawHTTP("<title>finger-hit</title>"),
    59  		rulePath:   rawHTTP("<title>rule-hit</title>"),
    60  	}
    61  
    62  	makeSender := func(sent *[]string) Sender {
    63  		return Sender(func(data []byte) ([]byte, bool) {
    64  			path := string(data)
    65  			*sent = append(*sent, path)
    66  			resp, ok := responses[path]
    67  			return resp, ok
    68  		})
    69  	}
    70  
    71  	// level 0: passive only, should not send and should not match.
    72  	{
    73  		var sent []string
    74  		frame, _, ok := finger.Match(NewContent(rawHTTP("no-hit"), "", true), 0, makeSender(&sent))
    75  		if ok || frame != nil {
    76  			t.Fatalf("level 0 should not match, got ok=%v frame=%v", ok, frame)
    77  		}
    78  		if len(sent) != 0 {
    79  			t.Fatalf("level 0 should not send, sent=%v", sent)
    80  		}
    81  	}
    82  
    83  	// level 1: should send finger-level only and match via finger-level response.
    84  	{
    85  		var sent []string
    86  		frame, _, ok := finger.Match(NewContent(rawHTTP("no-hit"), "", true), 1, makeSender(&sent))
    87  		if !ok || frame == nil {
    88  			t.Fatalf("level 1 should match via finger send_data")
    89  		}
    90  		if !contains(sent, fingerPath) {
    91  			t.Fatalf("level 1 should send finger path %q, sent=%v", fingerPath, sent)
    92  		}
    93  		if contains(sent, rulePath) {
    94  			t.Fatalf("level 1 should not send rule path %q, sent=%v", rulePath, sent)
    95  		}
    96  	}
    97  
    98  	// level 2: should send both finger-level and rule-level.
    99  	{
   100  		var sent []string
   101  		frame, _, ok := finger.Match(NewContent(rawHTTP("no-hit"), "", true), 2, makeSender(&sent))
   102  		if !ok || frame == nil {
   103  			t.Fatalf("level 2 should match via active probing")
   104  		}
   105  		if !contains(sent, fingerPath) {
   106  			t.Fatalf("level 2 should send finger path %q, sent=%v", fingerPath, sent)
   107  		}
   108  		if !contains(sent, rulePath) {
   109  			t.Fatalf("level 2 should send rule path %q, sent=%v", rulePath, sent)
   110  		}
   111  
   112  		// Sanity-check that the level 2 run actually used active content.
   113  		if frame.Version == "" && !strings.Contains(frame.Name, "level-test") {
   114  			t.Fatalf("unexpected framework returned at level 2: %#v", frame)
   115  		}
   116  	}
   117  }
   118  
   119  func TestMatchDetail_Passive(t *testing.T) {
   120  	finger := &Finger{
   121  		Name:              "rule-tag-test",
   122  		Protocol:          HTTPProtocol,
   123  		EnableMatchDetail: true,
   124  		Rules: Rules{
   125  			{
   126  				Regexps: &Regexps{
   127  					Body: []string{"first-hit"},
   128  				},
   129  			},
   130  			{
   131  				Regexps: &Regexps{
   132  					Body: []string{"second-hit"},
   133  				},
   134  			},
   135  		},
   136  	}
   137  
   138  	if err := finger.Compile(false); err != nil {
   139  		t.Fatalf("failed to compile finger: %v", err)
   140  	}
   141  
   142  	frame, _, ok := finger.Match(NewContent(rawHTTP("second-hit"), "", true), 0, nil)
   143  	if !ok || frame == nil {
   144  		t.Fatalf("expected passive match to succeed")
   145  	}
   146  	if frame.MatchDetail == nil {
   147  		t.Fatalf("expected match detail to be populated")
   148  	}
   149  	if frame.MatchDetail.RuleIndex != 1 {
   150  		t.Fatalf("expected rule index 1, got %d", frame.MatchDetail.RuleIndex)
   151  	}
   152  	if frame.MatchDetail.MatcherType != "body" {
   153  		t.Fatalf("expected matcher type body, got %q", frame.MatchDetail.MatcherType)
   154  	}
   155  	if frame.MatchDetail.MatcherIndex != 0 {
   156  		t.Fatalf("expected matcher index 0, got %d", frame.MatchDetail.MatcherIndex)
   157  	}
   158  	if frame.MatchDetail.SendData != "" {
   159  		t.Fatalf("expected passive match detail send_data to be empty, got %q", frame.MatchDetail.SendData)
   160  	}
   161  }
   162  
   163  func TestMatchDetail_ActiveSendData(t *testing.T) {
   164  	const (
   165  		fingerPath = "/finger-active"
   166  		rulePath   = "/rule-active"
   167  	)
   168  
   169  	finger := &Finger{
   170  		Name:              "active-detail-test",
   171  		Protocol:          HTTPProtocol,
   172  		EnableMatchDetail: true,
   173  		SendDataStr:       fingerPath,
   174  		Rules: Rules{
   175  			{
   176  				Regexps: &Regexps{
   177  					Body: []string{"rule-hit"},
   178  				},
   179  				SendDataStr: rulePath,
   180  				Level:       2,
   181  			},
   182  		},
   183  	}
   184  
   185  	if err := finger.Compile(false); err != nil {
   186  		t.Fatalf("failed to compile finger: %v", err)
   187  	}
   188  
   189  	sender := Sender(func(data []byte) ([]byte, bool) {
   190  		switch string(data) {
   191  		case fingerPath:
   192  			return rawHTTP("no-hit"), true
   193  		case rulePath:
   194  			return rawHTTP("rule-hit"), true
   195  		default:
   196  			return nil, false
   197  		}
   198  	})
   199  
   200  	frame, _, ok := finger.ActiveMatch(2, sender)
   201  	if !ok || frame == nil {
   202  		t.Fatalf("expected active match to succeed")
   203  	}
   204  	if frame.MatchDetail == nil {
   205  		t.Fatalf("expected match detail to be populated")
   206  	}
   207  	if frame.MatchDetail.SendData != rulePath {
   208  		t.Fatalf("expected send_data %q, got %q", rulePath, frame.MatchDetail.SendData)
   209  	}
   210  }