parse_test.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. package rfc5424
  2. import (
  3. "testing"
  4. "time"
  5. "github.com/stretchr/testify/require"
  6. "github.com/crowdsecurity/crowdsec/pkg/cstest"
  7. )
  8. func TestPri(t *testing.T) {
  9. tests := []struct {
  10. input string
  11. expected int
  12. expectedErr string
  13. }{
  14. {"<0>", 0, ""},
  15. {"<19>", 19, ""},
  16. {"<200>", 200, ""},
  17. {"<4999>", 0, "PRI must be up to 3 characters long"},
  18. {"<123", 0, "PRI must end with '>'"},
  19. {"123>", 0, "PRI must start with '<'"},
  20. {"<abc>", 0, "PRI must be a number"},
  21. }
  22. for _, test := range tests {
  23. test := test
  24. t.Run(test.input, func(t *testing.T) {
  25. r := &RFC5424{}
  26. r.buf = []byte(test.input)
  27. r.len = len(r.buf)
  28. err := r.parsePRI()
  29. cstest.RequireErrorMessage(t, err, test.expectedErr)
  30. })
  31. }
  32. }
  33. func TestHostname(t *testing.T) {
  34. tests := []struct {
  35. input string
  36. expected string
  37. expectedErr string
  38. strictHostname bool
  39. }{
  40. {"127.0.0.1", "127.0.0.1", "", false},
  41. {"::1", "::1", "", false},
  42. {"-", "", "", false},
  43. {"foo.-bar", "", "hostname is not valid", true},
  44. {"foo-.bar", "", "hostname is not valid", true},
  45. {"foo123.bar", "foo123.bar", "", true},
  46. {"a..", "", "hostname is not valid", true},
  47. {"foo.bar", "foo.bar", "", false},
  48. {"foo,bar", "foo,bar", "", false},
  49. {"foo,bar", "", "hostname is not valid", true},
  50. {".", ".", "", true},
  51. {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "", "hostname is not valid", true},
  52. {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bla", "", "hostname is not valid", true},
  53. {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bla", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bla", "", false},
  54. {"a.foo-", "", "hostname is not valid", true},
  55. }
  56. for _, test := range tests {
  57. test := test
  58. t.Run(test.input, func(t *testing.T) {
  59. opts := []RFC5424Option{}
  60. if test.strictHostname {
  61. opts = append(opts, WithStrictHostname())
  62. }
  63. r := NewRFC5424Parser(opts...)
  64. r.buf = []byte(test.input)
  65. r.len = len(r.buf)
  66. err := r.parseHostname()
  67. cstest.RequireErrorMessage(t, err, test.expectedErr)
  68. })
  69. }
  70. }
  71. func TestParse(t *testing.T) {
  72. type expected struct {
  73. Timestamp time.Time
  74. Hostname string
  75. Tag string
  76. PID string
  77. Message string
  78. PRI int
  79. MsgID string
  80. }
  81. tests := []struct {
  82. name string
  83. input string
  84. expected expected
  85. expectedErr string
  86. opts []RFC5424Option
  87. }{
  88. {
  89. "valid msg",
  90. `<13>1 2021-05-18T11:58:40.828081+02:42 mantis sshd 49340 - [timeQuality isSynced="0" tzKnown="1"] blabla`, expected{
  91. Timestamp: time.Date(2021, 5, 18, 11, 58, 40, 828081000, time.FixedZone("+0242", 9720)),
  92. Hostname: "mantis",
  93. Tag: "sshd",
  94. PID: "49340",
  95. MsgID: "",
  96. Message: "blabla",
  97. PRI: 13,
  98. }, "", []RFC5424Option{},
  99. },
  100. {
  101. "valid msg with msgid",
  102. `<13>1 2021-05-18T11:58:40.828081+02:42 mantis foobar 49340 123123 [timeQuality isSynced="0" tzKnown="1"] blabla`, expected{
  103. Timestamp: time.Date(2021, 5, 18, 11, 58, 40, 828081000, time.FixedZone("+0242", 9720)),
  104. Hostname: "mantis",
  105. Tag: "foobar",
  106. PID: "49340",
  107. MsgID: "123123",
  108. Message: "blabla",
  109. PRI: 13,
  110. }, "", []RFC5424Option{},
  111. },
  112. {
  113. "valid msg with repeating SD",
  114. `<13>1 2021-05-18T11:58:40.828081+02:42 mantis foobar 49340 123123 [timeQuality isSynced="0" tzKnown="1"][foo="bar][a] blabla`, expected{
  115. Timestamp: time.Date(2021, 5, 18, 11, 58, 40, 828081000, time.FixedZone("+0242", 9720)),
  116. Hostname: "mantis",
  117. Tag: "foobar",
  118. PID: "49340",
  119. MsgID: "123123",
  120. Message: "blabla",
  121. PRI: 13,
  122. }, "", []RFC5424Option{},
  123. },
  124. {
  125. "invalid SD",
  126. `<13>1 2021-05-18T11:58:40.828081+02:00 mantis foobar 49340 123123 [timeQuality asd`, expected{}, "structured data must end with ']'", []RFC5424Option{},
  127. },
  128. {
  129. "invalid version",
  130. `<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{},
  131. },
  132. {
  133. "invalid message",
  134. `<13>1`, expected{}, "version must be followed by a space", []RFC5424Option{},
  135. },
  136. {
  137. "valid msg with empty fields",
  138. `<13>1 - foo - - - - blabla`, expected{
  139. Timestamp: time.Now().UTC(),
  140. Hostname: "foo",
  141. PRI: 13,
  142. Message: "blabla",
  143. }, "", []RFC5424Option{},
  144. },
  145. {
  146. "valid msg with empty fields",
  147. `<13>1 - - - - - - blabla`, expected{
  148. Timestamp: time.Now().UTC(),
  149. PRI: 13,
  150. Message: "blabla",
  151. }, "", []RFC5424Option{},
  152. },
  153. {
  154. "valid msg with escaped SD",
  155. `<13>1 2022-05-24T10:57:39Z testhostname unknown - sn="msgid" [foo="\]" bar="a\""][a b="[\]" c] testmessage`,
  156. expected{
  157. PRI: 13,
  158. Timestamp: time.Date(2022, 5, 24, 10, 57, 39, 0, time.UTC),
  159. Tag: "unknown",
  160. Hostname: "testhostname",
  161. MsgID: `sn="msgid"`,
  162. Message: `testmessage`,
  163. }, "", []RFC5424Option{},
  164. },
  165. {
  166. "valid complex msg",
  167. `<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:`,
  168. expected{
  169. Timestamp: time.Date(2022, 5, 24, 10, 57, 39, 0, time.UTC),
  170. Hostname: "myhostname",
  171. Tag: "unknown",
  172. PRI: 13,
  173. MsgID: `sn="msgid"`,
  174. 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:`,
  175. }, "", []RFC5424Option{},
  176. },
  177. {
  178. "partial message",
  179. `<13>1 2022-05-24T10:57:39Z foo bar -`,
  180. expected{},
  181. "EOL after ProcID",
  182. []RFC5424Option{},
  183. },
  184. {
  185. "partial message",
  186. `<13>1 2022-05-24T10:57:39Z foo bar `,
  187. expected{},
  188. "EOL after appname",
  189. []RFC5424Option{},
  190. },
  191. }
  192. for _, test := range tests {
  193. test := test
  194. t.Run(test.name, func(t *testing.T) {
  195. r := NewRFC5424Parser(test.opts...)
  196. err := r.Parse([]byte(test.input))
  197. cstest.RequireErrorMessage(t, err, test.expectedErr)
  198. if test.expectedErr != "" {
  199. return
  200. }
  201. require.WithinDuration(t, test.expected.Timestamp, r.Timestamp, time.Second)
  202. if r.Hostname != test.expected.Hostname {
  203. t.Errorf("expected hostname '%s', got '%s'", test.expected.Hostname, r.Hostname)
  204. }
  205. if r.Tag != test.expected.Tag {
  206. t.Errorf("expected tag '%s', got '%s'", test.expected.Tag, r.Tag)
  207. }
  208. if r.PID != test.expected.PID {
  209. t.Errorf("expected pid '%s', got '%s'", test.expected.PID, r.PID)
  210. }
  211. if r.Message != test.expected.Message {
  212. t.Errorf("expected message '%s', got '%s'", test.expected.Message, r.Message)
  213. }
  214. if r.PRI != test.expected.PRI {
  215. t.Errorf("expected pri '%d', got '%d'", test.expected.PRI, r.PRI)
  216. }
  217. if r.MsgID != test.expected.MsgID {
  218. t.Errorf("expected msgid '%s', got '%s'", test.expected.MsgID, r.MsgID)
  219. }
  220. })
  221. }
  222. }