github.com/crowdsecurity/crowdsec@v1.6.1/pkg/acquisition/modules/syslog/internal/parser/rfc3164/parse_test.go (about)

     1  package rfc3164
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  )
     8  
     9  func TestPri(t *testing.T) {
    10  	tests := []struct {
    11  		input       string
    12  		expected    int
    13  		expectedErr string
    14  	}{
    15  		{"<0>", 0, ""},
    16  		{"<19>", 19, ""},
    17  		{"<200>", 200, ""},
    18  		{"<4999>", 0, "PRI must be up to 3 characters long"},
    19  		{"<123", 0, "PRI must end with '>'"},
    20  		{"123>", 0, "PRI must start with '<'"},
    21  		{"<abc>", 0, "PRI must be a number"},
    22  	}
    23  
    24  	for _, test := range tests {
    25  		test := test
    26  		t.Run(test.input, func(t *testing.T) {
    27  			r := &RFC3164{}
    28  			r.buf = []byte(test.input)
    29  			r.len = len(r.buf)
    30  			err := r.parsePRI()
    31  			if err != nil {
    32  				if test.expectedErr != "" {
    33  					if err.Error() != test.expectedErr {
    34  						t.Errorf("expected error %s, got %s", test.expectedErr, err)
    35  					}
    36  				} else {
    37  					t.Errorf("unexpected error: %s", err)
    38  				}
    39  			} else {
    40  				if test.expectedErr != "" {
    41  					t.Errorf("expected error %s, got no error", test.expectedErr)
    42  				} else if r.PRI != test.expected {
    43  					t.Errorf("expected %d, got %d", test.expected, r.PRI)
    44  				}
    45  			}
    46  		})
    47  	}
    48  }
    49  
    50  func TestTimestamp(t *testing.T) {
    51  
    52  	tests := []struct {
    53  		input       string
    54  		expected    string
    55  		expectedErr string
    56  		currentYear bool
    57  	}{
    58  		{"May 20 09:33:54", "0000-05-20T09:33:54Z", "", false},
    59  		{"May 20 09:33:54", fmt.Sprintf("%d-05-20T09:33:54Z", time.Now().Year()), "", true},
    60  		{"May 20 09:33:54 2022", "2022-05-20T09:33:54Z", "", false},
    61  		{"May  1 09:33:54 2022", "2022-05-01T09:33:54Z", "", false},
    62  		{"May 01 09:33:54 2021", "2021-05-01T09:33:54Z", "", true},
    63  		{"foobar", "", "timestamp is not valid", false},
    64  	}
    65  
    66  	for _, test := range tests {
    67  		test := test
    68  		t.Run(test.input, func(t *testing.T) {
    69  			opts := []RFC3164Option{}
    70  			if test.currentYear {
    71  				opts = append(opts, WithCurrentYear())
    72  			}
    73  			r := NewRFC3164Parser(opts...)
    74  			r.buf = []byte(test.input)
    75  			r.len = len(r.buf)
    76  			err := r.parseTimestamp()
    77  			if err != nil {
    78  				if test.expectedErr != "" {
    79  					if err.Error() != test.expectedErr {
    80  						t.Errorf("expected error %s, got %s", test.expectedErr, err)
    81  					}
    82  				} else {
    83  					t.Errorf("unexpected error: %s", err)
    84  				}
    85  			} else {
    86  				if test.expectedErr != "" {
    87  					t.Errorf("expected error %s, got no error", test.expectedErr)
    88  				} else if r.Timestamp.Format(time.RFC3339) != test.expected {
    89  					t.Errorf("expected %s, got %s", test.expected, r.Timestamp.Format(time.RFC3339))
    90  				}
    91  			}
    92  		})
    93  	}
    94  }
    95  
    96  func TestHostname(t *testing.T) {
    97  	tests := []struct {
    98  		input          string
    99  		expected       string
   100  		expectedErr    string
   101  		strictHostname bool
   102  	}{
   103  		{"127.0.0.1", "127.0.0.1", "", false},
   104  		{"::1", "::1", "", false},
   105  		{"foo.-bar", "", "hostname is not valid", true},
   106  		{"foo-.bar", "", "hostname is not valid", true},
   107  		{"foo123.bar", "foo123.bar", "", true},
   108  		{"a..", "", "hostname is not valid", true},
   109  		{"foo.bar", "foo.bar", "", false},
   110  		{"foo,bar", "foo,bar", "", false},
   111  		{"foo,bar", "", "hostname is not valid", true},
   112  		{"", "", "hostname is empty", false},
   113  		{".", ".", "", true},
   114  		{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "", "hostname is not valid", true},
   115  		{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bla", "", "hostname is not valid", true},
   116  		{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bla", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bla", "", false},
   117  		{"a.foo-", "", "hostname is not valid", true},
   118  	}
   119  
   120  	for _, test := range tests {
   121  		test := test
   122  		t.Run(test.input, func(t *testing.T) {
   123  			opts := []RFC3164Option{}
   124  			if test.strictHostname {
   125  				opts = append(opts, WithStrictHostname())
   126  			}
   127  			r := NewRFC3164Parser(opts...)
   128  			r.buf = []byte(test.input)
   129  			r.len = len(r.buf)
   130  			err := r.parseHostname()
   131  			if err != nil {
   132  				if test.expectedErr != "" {
   133  					if err.Error() != test.expectedErr {
   134  						t.Errorf("expected error %s, got %s", test.expectedErr, err)
   135  					}
   136  				} else {
   137  					t.Errorf("unexpected error: %s", err)
   138  				}
   139  			} else {
   140  				if test.expectedErr != "" {
   141  					t.Errorf("expected error %s, got no error", test.expectedErr)
   142  				} else if r.Hostname != test.expected {
   143  					t.Errorf("expected %s, got %s", test.expected, r.Hostname)
   144  				}
   145  			}
   146  		})
   147  	}
   148  }
   149  
   150  func TestTag(t *testing.T) {
   151  	tests := []struct {
   152  		input       string
   153  		expected    string
   154  		expectedPID string
   155  		expectedErr string
   156  	}{
   157  		{"foobar", "foobar", "", ""},
   158  		{"foobar[42]", "foobar", "42", ""},
   159  		{"", "", "", "tag is empty"},
   160  		{"foobar[", "", "", "pid inside tag must be closed with ']'"},
   161  		{"foobar[42", "", "", "pid inside tag must be closed with ']'"},
   162  		{"foobar[asd]", "foobar", "", "pid inside tag must be a number"},
   163  	}
   164  
   165  	for _, test := range tests {
   166  		test := test
   167  		t.Run(test.input, func(t *testing.T) {
   168  			r := &RFC3164{}
   169  			r.buf = []byte(test.input)
   170  			r.len = len(r.buf)
   171  			err := r.parseTag()
   172  			if err != nil {
   173  				if test.expectedErr != "" {
   174  					if err.Error() != test.expectedErr {
   175  						t.Errorf("expected error %s, got %s", test.expectedErr, err)
   176  					}
   177  				} else {
   178  					t.Errorf("unexpected error: %s", err)
   179  				}
   180  			} else {
   181  				if test.expectedErr != "" {
   182  					t.Errorf("expected error %s, got no error", test.expectedErr)
   183  				} else {
   184  					if r.Tag != test.expected {
   185  						t.Errorf("expected %s, got %s", test.expected, r.Tag)
   186  					}
   187  					if r.PID != test.expectedPID {
   188  						t.Errorf("expected %s, got %s", test.expected, r.Message)
   189  					}
   190  				}
   191  			}
   192  		})
   193  	}
   194  }
   195  
   196  func TestMessage(t *testing.T) {
   197  	tests := []struct {
   198  		input       string
   199  		expected    string
   200  		expectedErr string
   201  	}{
   202  		{"foobar: pouet", "pouet", ""},
   203  		{"foobar[42]: test", "test", ""},
   204  		{"foobar[123]: this is a test", "this is a test", ""},
   205  		{"foobar[123]: ", "", "message is empty"},
   206  		{"foobar[123]:", "", "message is empty"},
   207  	}
   208  
   209  	for _, test := range tests {
   210  		test := test
   211  		t.Run(test.input, func(t *testing.T) {
   212  			r := &RFC3164{}
   213  			r.buf = []byte(test.input)
   214  			r.len = len(r.buf)
   215  			err := r.parseMessage()
   216  			if err != nil {
   217  				if test.expectedErr != "" {
   218  					if err.Error() != test.expectedErr {
   219  						t.Errorf("expected error %s, got %s", test.expectedErr, err)
   220  					}
   221  				} else {
   222  					t.Errorf("unexpected error: %s", err)
   223  				}
   224  			} else {
   225  				if test.expectedErr != "" {
   226  					t.Errorf("expected error %s, got no error", test.expectedErr)
   227  				} else if r.Message != test.expected {
   228  					t.Errorf("expected message %s, got %s", test.expected, r.Tag)
   229  				}
   230  			}
   231  		})
   232  	}
   233  }
   234  
   235  func TestParse(t *testing.T) {
   236  	type expected struct {
   237  		Timestamp time.Time
   238  		Hostname  string
   239  		Tag       string
   240  		PID       string
   241  		Message   string
   242  		PRI       int
   243  	}
   244  	tests := []struct {
   245  		input       string
   246  		expected    expected
   247  		expectedErr string
   248  		opts        []RFC3164Option
   249  	}{
   250  		{
   251  			"<12>May 20 09:33:54 UDMPRO,a2edd0c6ae48,udm-1.10.0.3686 kernel: foo", expected{
   252  				Timestamp: time.Date(0, time.May, 20, 9, 33, 54, 0, time.UTC),
   253  				Hostname:  "UDMPRO,a2edd0c6ae48,udm-1.10.0.3686",
   254  				Tag:       "kernel",
   255  				PID:       "",
   256  				Message:   "foo",
   257  				PRI:       12,
   258  			}, "", []RFC3164Option{},
   259  		},
   260  		{
   261  			"<12>May 20 09:33:54 UDMPRO,a2edd0c6ae48,udm-1.10.0.3686 kernel: foo", expected{
   262  				Timestamp: time.Date(time.Now().Year(), time.May, 20, 9, 33, 54, 0, time.UTC),
   263  				Hostname:  "UDMPRO,a2edd0c6ae48,udm-1.10.0.3686",
   264  				Tag:       "kernel",
   265  				PID:       "",
   266  				Message:   "foo",
   267  				PRI:       12,
   268  			}, "", []RFC3164Option{WithCurrentYear()},
   269  		},
   270  		{
   271  			"<12>May 20 09:33:54 UDMPRO,a2edd0c6ae48,udm-1.10.0.3686 kernel: foo", expected{}, "hostname is not valid", []RFC3164Option{WithStrictHostname()},
   272  		},
   273  		{
   274  			"foobar", expected{}, "PRI must start with '<'", []RFC3164Option{},
   275  		},
   276  		{
   277  			"<12>", expected{}, "timestamp is not valid", []RFC3164Option{},
   278  		},
   279  		{
   280  			"<12 May 02 09:33:54 foo.bar", expected{}, "PRI must be a number", []RFC3164Option{},
   281  		},
   282  		{
   283  			"<12>May 02 09:33:54", expected{}, "hostname is empty", []RFC3164Option{},
   284  		},
   285  		{
   286  			"<12>May 02 09:33:54 foo.bar", expected{}, "tag is empty", []RFC3164Option{},
   287  		},
   288  		{
   289  			"<12>May 02 09:33:54 foo.bar bla[42", expected{}, "pid inside tag must be closed with ']'", []RFC3164Option{},
   290  		},
   291  		{
   292  			"<12>May 02 09:33:54 foo.bar bla[42]", expected{}, "message is empty", []RFC3164Option{},
   293  		},
   294  		{
   295  			"<12>May 02 09:33:54 foo.bar bla[42]:   ", expected{}, "message is empty", []RFC3164Option{},
   296  		},
   297  		{
   298  			"<12>May 02 09:33:54 foo.bar bla", expected{}, "message is empty", []RFC3164Option{},
   299  		},
   300  		{
   301  			"<12>May 02 09:33:54 foo.bar bla:", expected{}, "message is empty", []RFC3164Option{},
   302  		},
   303  		{
   304  			"", expected{}, "message is empty", []RFC3164Option{},
   305  		},
   306  		{
   307  			`<13>1 2021-05-18T11:58:40.828081+02:00 mantis sshd 49340 - [timeQuality isSynced="0" tzKnown="1"] blabla`, expected{}, "timestamp is not valid", []RFC3164Option{},
   308  		},
   309  		{
   310  			`<46>Jun  2 06:55:39 localhost haproxy[27213]: Connect from 100.100.100.99:52611 to 100.100.100.99:443 (https_shared-merged/HTTP)\\n 10.0.0.1}`, expected{
   311  				Timestamp: time.Date(time.Now().Year(), time.June, 2, 6, 55, 39, 0, time.UTC),
   312  				Hostname:  "localhost",
   313  				Tag:       "haproxy",
   314  				PID:       "27213",
   315  				Message:   `Connect from 100.100.100.99:52611 to 100.100.100.99:443 (https_shared-merged/HTTP)\\n 10.0.0.1}`,
   316  				PRI:       46,
   317  			}, "", []RFC3164Option{WithCurrentYear()},
   318  		},
   319  		{
   320  			`<46>Jun  2 06:55:39 2022 localhost haproxy[27213]: Connect from 100.100.100.99:52611 to 100.100.100.99:443 (https_shared-merged/HTTP)\\n 10.0.0.1}`, expected{
   321  				Timestamp: time.Date(2022, time.June, 2, 6, 55, 39, 0, time.UTC),
   322  				Hostname:  "localhost",
   323  				Tag:       "haproxy",
   324  				PID:       "27213",
   325  				Message:   `Connect from 100.100.100.99:52611 to 100.100.100.99:443 (https_shared-merged/HTTP)\\n 10.0.0.1}`,
   326  				PRI:       46,
   327  			}, "", []RFC3164Option{},
   328  		},
   329  	}
   330  
   331  	for _, test := range tests {
   332  		test := test
   333  		t.Run(test.input, func(t *testing.T) {
   334  			r := NewRFC3164Parser(test.opts...)
   335  			err := r.Parse([]byte(test.input))
   336  			if err != nil {
   337  				if test.expectedErr != "" {
   338  					if err.Error() != test.expectedErr {
   339  						t.Errorf("expected error '%s', got '%s'", test.expectedErr, err)
   340  					}
   341  				} else {
   342  					t.Errorf("unexpected error: '%s'", err)
   343  				}
   344  			} else {
   345  				if test.expectedErr != "" {
   346  					t.Errorf("expected error '%s', got no error", test.expectedErr)
   347  				} else {
   348  					if r.Timestamp != test.expected.Timestamp {
   349  						t.Errorf("expected timestamp '%s', got '%s'", test.expected.Timestamp, r.Timestamp)
   350  					}
   351  					if r.Hostname != test.expected.Hostname {
   352  						t.Errorf("expected hostname '%s', got '%s'", test.expected.Hostname, r.Hostname)
   353  					}
   354  					if r.Tag != test.expected.Tag {
   355  						t.Errorf("expected tag '%s', got '%s'", test.expected.Tag, r.Tag)
   356  					}
   357  					if r.PID != test.expected.PID {
   358  						t.Errorf("expected pid '%s', got '%s'", test.expected.PID, r.PID)
   359  					}
   360  					if r.Message != test.expected.Message {
   361  						t.Errorf("expected message '%s', got '%s'", test.expected.Message, r.Message)
   362  					}
   363  					if r.PRI != test.expected.PRI {
   364  						t.Errorf("expected pri '%d', got '%d'", test.expected.PRI, r.PRI)
   365  					}
   366  				}
   367  			}
   368  		})
   369  	}
   370  }