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

     1  package rfc5424
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/crowdsecurity/go-cs-lib/cstest"
     8  
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestPri(t *testing.T) {
    13  	tests := []struct {
    14  		input       string
    15  		expected    int
    16  		expectedErr string
    17  	}{
    18  		{"<0>", 0, ""},
    19  		{"<19>", 19, ""},
    20  		{"<200>", 200, ""},
    21  		{"<4999>", 0, "PRI must be up to 3 characters long"},
    22  		{"<123", 0, "PRI must end with '>'"},
    23  		{"123>", 0, "PRI must start with '<'"},
    24  		{"<abc>", 0, "PRI must be a number"},
    25  	}
    26  
    27  	for _, test := range tests {
    28  		test := test
    29  		t.Run(test.input, func(t *testing.T) {
    30  			r := &RFC5424{}
    31  			r.buf = []byte(test.input)
    32  			r.len = len(r.buf)
    33  			err := r.parsePRI()
    34  			cstest.RequireErrorMessage(t, err, test.expectedErr)
    35  		})
    36  	}
    37  }
    38  
    39  func TestHostname(t *testing.T) {
    40  	tests := []struct {
    41  		input          string
    42  		expected       string
    43  		expectedErr    string
    44  		strictHostname bool
    45  	}{
    46  		{"127.0.0.1", "127.0.0.1", "", false},
    47  		{"::1", "::1", "", false},
    48  		{"-", "", "", false},
    49  		{"foo.-bar", "", "hostname is not valid", true},
    50  		{"foo-.bar", "", "hostname is not valid", true},
    51  		{"foo123.bar", "foo123.bar", "", true},
    52  		{"a..", "", "hostname is not valid", true},
    53  		{"foo.bar", "foo.bar", "", false},
    54  		{"foo,bar", "foo,bar", "", false},
    55  		{"foo,bar", "", "hostname is not valid", true},
    56  		{".", ".", "", true},
    57  		{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "", "hostname is not valid", true},
    58  		{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bla", "", "hostname is not valid", true},
    59  		{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bla", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bla", "", false},
    60  		{"a.foo-", "", "hostname is not valid", true},
    61  	}
    62  
    63  	for _, test := range tests {
    64  		test := test
    65  		t.Run(test.input, func(t *testing.T) {
    66  			opts := []RFC5424Option{}
    67  			if test.strictHostname {
    68  				opts = append(opts, WithStrictHostname())
    69  			}
    70  			r := NewRFC5424Parser(opts...)
    71  			r.buf = []byte(test.input)
    72  			r.len = len(r.buf)
    73  			err := r.parseHostname()
    74  			cstest.RequireErrorMessage(t, err, test.expectedErr)
    75  		})
    76  	}
    77  }
    78  
    79  func TestParse(t *testing.T) {
    80  	type expected struct {
    81  		Timestamp time.Time
    82  		Hostname  string
    83  		Tag       string
    84  		PID       string
    85  		Message   string
    86  		PRI       int
    87  		MsgID     string
    88  	}
    89  
    90  	tests := []struct {
    91  		name        string
    92  		input       string
    93  		expected    expected
    94  		expectedErr string
    95  		opts        []RFC5424Option
    96  	}{
    97  		{
    98  			"valid msg",
    99  			`<13>1 2021-05-18T11:58:40.828081+02:42 mantis sshd 49340 - [timeQuality isSynced="0" tzKnown="1"] blabla`, expected{
   100  				Timestamp: time.Date(2021, 5, 18, 11, 58, 40, 828081000, time.FixedZone("+0242", 9720)),
   101  				Hostname:  "mantis",
   102  				Tag:       "sshd",
   103  				PID:       "49340",
   104  				MsgID:     "",
   105  				Message:   "blabla",
   106  				PRI:       13,
   107  			}, "", []RFC5424Option{},
   108  		},
   109  		{
   110  			"valid msg with msgid",
   111  			`<13>1 2021-05-18T11:58:40.828081+02:42 mantis foobar 49340 123123 [timeQuality isSynced="0" tzKnown="1"] blabla`, expected{
   112  				Timestamp: time.Date(2021, 5, 18, 11, 58, 40, 828081000, time.FixedZone("+0242", 9720)),
   113  				Hostname:  "mantis",
   114  				Tag:       "foobar",
   115  				PID:       "49340",
   116  				MsgID:     "123123",
   117  				Message:   "blabla",
   118  				PRI:       13,
   119  			}, "", []RFC5424Option{},
   120  		},
   121  		{
   122  			"valid msg with repeating SD",
   123  			`<13>1 2021-05-18T11:58:40.828081+02:42 mantis foobar 49340 123123 [timeQuality isSynced="0" tzKnown="1"][foo="bar][a] blabla`, expected{
   124  				Timestamp: time.Date(2021, 5, 18, 11, 58, 40, 828081000, time.FixedZone("+0242", 9720)),
   125  				Hostname:  "mantis",
   126  				Tag:       "foobar",
   127  				PID:       "49340",
   128  				MsgID:     "123123",
   129  				Message:   "blabla",
   130  				PRI:       13,
   131  			}, "", []RFC5424Option{},
   132  		},
   133  		{
   134  			"invalid SD",
   135  			`<13>1 2021-05-18T11:58:40.828081+02:00 mantis foobar 49340 123123 [timeQuality asd`, expected{}, "structured data must end with ']'", []RFC5424Option{},
   136  		},
   137  		{
   138  			"invalid version",
   139  			`<13>42 2021-05-18T11:58:40.828081+02:00 mantis foobar 49340 123123 [timeQuality isSynced="0" tzKnown="1"] blabla`, expected{}, "version must be 1", []RFC5424Option{},
   140  		},
   141  		{
   142  			"invalid message",
   143  			`<13>1`, expected{}, "version must be followed by a space", []RFC5424Option{},
   144  		},
   145  		{
   146  			"valid msg with empty fields",
   147  			`<13>1 - foo - - - - blabla`, expected{
   148  				Timestamp: time.Now().UTC(),
   149  				Hostname:  "foo",
   150  				PRI:       13,
   151  				Message:   "blabla",
   152  			}, "", []RFC5424Option{},
   153  		},
   154  		{
   155  			"valid msg with empty fields",
   156  			`<13>1 - - - - - - blabla`, expected{
   157  				Timestamp: time.Now().UTC(),
   158  				PRI:       13,
   159  				Message:   "blabla",
   160  			}, "", []RFC5424Option{},
   161  		},
   162  		{
   163  			"valid msg with escaped SD",
   164  			`<13>1 2022-05-24T10:57:39Z testhostname unknown - sn="msgid" [foo="\]" bar="a\""][a b="[\]" c] testmessage`,
   165  			expected{
   166  				PRI:       13,
   167  				Timestamp: time.Date(2022, 5, 24, 10, 57, 39, 0, time.UTC),
   168  				Tag:       "unknown",
   169  				Hostname:  "testhostname",
   170  				MsgID:     `sn="msgid"`,
   171  				Message:   `testmessage`,
   172  			}, "", []RFC5424Option{},
   173  		},
   174  		{
   175  			"valid complex msg",
   176  			`<13>1 2022-05-24T10:57:39Z myhostname unknown - sn="msgid" [all@0 request="/dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js" src_ip_geo_country="DE" MONTH="May" COMMONAPACHELOG="1.1.1.1 - - [24/May/2022:10:57:37 +0200\] \"GET /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js HTTP/2.0\" 304 0" auth="-" HOUR="10" gl2_remote_ip="172.31.32.142" ident="-" gl2_remote_port="43375" BASE10NUM="[2.0, 304, 0\]" pid="-1" program="nginx" gl2_source_input="623ed3440183476d61cff974" INT="+0200" is_private_ip="false" YEAR="2022" src_ip_geo_city="Achern" clientip="1.1.1.1" USERNAME="-" src_ip_geo_location="48.6306,8.0743" gl2_source_node="8620c2bb-dbb7-4535-b1ce-83df223acd8d" MINUTE="57" timestamp="2022-05-24T08:57:37.000Z" src_ip_asn="3320" level="5" IP="1.1.1.1" IPV4="1.1.1.1" verb="GET" gl2_message_id="01G3TMJFAMFS4H60QSF7M029R0" TIME="10:57:37" USER="-" src_ip_asn_owner="Deutsche Telekom AG" response="304" bytes="0" SECOND="37" httpversion="2.0" _id="906ce155-db3f-11ec-b25f-0a189ba2c64e" facility="user" MONTHDAY="24"] source: sn="www.foobar.com" | message: 1.1.1.1 - - [24/May/2022:10:57:37 +0200] "GET /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js HTTP/2.0" 304 0 "https://www.foobar.com/sw.js" "Mozilla/5.0 (Linux; Android 9; ANE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.61 Mobile Safari/537.36" "-" "www.foobar.com" sn="www.foobar.com" rt=0.000 ua="-" us="-" ut="-" ul="-" cs=HIT { request: /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js | src_ip_geo_country: DE | MONTH: May | COMMONAPACHELOG: 1.1.1.1 - - [24/May/2022:10:57:37 +0200] "GET /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js HTTP/2.0" 304 0 | auth: - | HOUR: 10 | gl2_remote_ip: 172.31.32.142 | ident: - | gl2_remote_port: 43375 | BASE10NUM: [2.0, 304, 0] | pid: -1 | program: nginx | gl2_source_input: 623ed3440183476d61cff974 | INT: +0200 | is_private_ip: false | YEAR: 2022 | src_ip_geo_city: Achern | clientip: 1.1.1.1 | USERNAME:`,
   177  			expected{
   178  				Timestamp: time.Date(2022, 5, 24, 10, 57, 39, 0, time.UTC),
   179  				Hostname:  "myhostname",
   180  				Tag:       "unknown",
   181  				PRI:       13,
   182  				MsgID:     `sn="msgid"`,
   183  				Message:   `source: sn="www.foobar.com" | message: 1.1.1.1 - - [24/May/2022:10:57:37 +0200] "GET /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js HTTP/2.0" 304 0 "https://www.foobar.com/sw.js" "Mozilla/5.0 (Linux; Android 9; ANE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.61 Mobile Safari/537.36" "-" "www.foobar.com" sn="www.foobar.com" rt=0.000 ua="-" us="-" ut="-" ul="-" cs=HIT { request: /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js | src_ip_geo_country: DE | MONTH: May | COMMONAPACHELOG: 1.1.1.1 - - [24/May/2022:10:57:37 +0200] "GET /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js HTTP/2.0" 304 0 | auth: - | HOUR: 10 | gl2_remote_ip: 172.31.32.142 | ident: - | gl2_remote_port: 43375 | BASE10NUM: [2.0, 304, 0] | pid: -1 | program: nginx | gl2_source_input: 623ed3440183476d61cff974 | INT: +0200 | is_private_ip: false | YEAR: 2022 | src_ip_geo_city: Achern | clientip: 1.1.1.1 | USERNAME:`,
   184  			}, "", []RFC5424Option{},
   185  		},
   186  		{
   187  			"partial message",
   188  			`<13>1 2022-05-24T10:57:39Z foo bar -`,
   189  			expected{},
   190  			"EOL after ProcID",
   191  			[]RFC5424Option{},
   192  		},
   193  		{
   194  			"partial message",
   195  			`<13>1 2022-05-24T10:57:39Z foo bar `,
   196  			expected{},
   197  			"EOL after appname",
   198  			[]RFC5424Option{},
   199  		},
   200  	}
   201  
   202  	for _, test := range tests {
   203  		test := test
   204  		t.Run(test.name, func(t *testing.T) {
   205  			r := NewRFC5424Parser(test.opts...)
   206  			err := r.Parse([]byte(test.input))
   207  			cstest.RequireErrorMessage(t, err, test.expectedErr)
   208  			if test.expectedErr != "" {
   209  				return
   210  			}
   211  			require.WithinDuration(t, test.expected.Timestamp, r.Timestamp, time.Second)
   212  			if r.Hostname != test.expected.Hostname {
   213  				t.Errorf("expected hostname '%s', got '%s'", test.expected.Hostname, r.Hostname)
   214  			}
   215  			if r.Tag != test.expected.Tag {
   216  				t.Errorf("expected tag '%s', got '%s'", test.expected.Tag, r.Tag)
   217  			}
   218  			if r.PID != test.expected.PID {
   219  				t.Errorf("expected pid '%s', got '%s'", test.expected.PID, r.PID)
   220  			}
   221  			if r.Message != test.expected.Message {
   222  				t.Errorf("expected message '%s', got '%s'", test.expected.Message, r.Message)
   223  			}
   224  			if r.PRI != test.expected.PRI {
   225  				t.Errorf("expected pri '%d', got '%d'", test.expected.PRI, r.PRI)
   226  			}
   227  			if r.MsgID != test.expected.MsgID {
   228  				t.Errorf("expected msgid '%s', got '%s'", test.expected.MsgID, r.MsgID)
   229  			}
   230  		})
   231  	}
   232  }