unified2 0.5.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. data/ChangeLog.md +10 -0
  2. data/README.md +41 -35
  3. data/Rakefile +3 -2
  4. data/bin/ru2 +76 -0
  5. data/example/example.rb +10 -18
  6. data/example/example2.rb +44 -0
  7. data/example/seeds/classification.config +1 -1
  8. data/example/seeds/gen-msg.map +86 -9
  9. data/example/seeds/sid-msg.map +2849 -316
  10. data/example/seeds/unified2-current.log +0 -0
  11. data/example/seeds/{unified2.log → unified2-legacy.log} +0 -0
  12. data/gemspec.yml +2 -1
  13. data/lib/unified2/classification.rb +12 -0
  14. data/lib/unified2/config_file.rb +4 -1
  15. data/lib/unified2/constructor/construct.rb +52 -6
  16. data/lib/unified2/constructor/event_ip4.rb +18 -3
  17. data/lib/unified2/constructor/event_ip6.rb +22 -4
  18. data/lib/unified2/constructor/extra_construct.rb +46 -0
  19. data/lib/unified2/constructor/extra_data.rb +37 -0
  20. data/lib/unified2/constructor/extra_data_header.rb +28 -0
  21. data/lib/unified2/constructor/legacy_event_ip4.rb +54 -0
  22. data/lib/unified2/constructor/legacy_event_ip6.rb +52 -0
  23. data/lib/unified2/constructor/packet.rb +9 -1
  24. data/lib/unified2/constructor/primitive/ipv4.rb +9 -0
  25. data/lib/unified2/constructor/record_header.rb +9 -0
  26. data/lib/unified2/constructor.rb +2 -1
  27. data/lib/unified2/core_ext/string.rb +2 -1
  28. data/lib/unified2/event.rb +290 -165
  29. data/lib/unified2/exceptions/binary_read_error.rb +11 -0
  30. data/lib/unified2/exceptions/file_not_found.rb +4 -1
  31. data/lib/unified2/exceptions/file_not_readable.rb +4 -1
  32. data/lib/unified2/exceptions/unknown_load_type.rb +4 -1
  33. data/lib/unified2/exceptions.rb +2 -1
  34. data/lib/unified2/extra.rb +128 -0
  35. data/lib/unified2/packet.rb +211 -0
  36. data/lib/unified2/protocol.rb +54 -63
  37. data/lib/unified2/sensor.rb +14 -2
  38. data/lib/unified2/signature.rb +12 -0
  39. data/lib/unified2/version.rb +4 -1
  40. data/lib/unified2.rb +65 -81
  41. data/spec/event_spec.rb +40 -27
  42. data/spec/legacy_event_spec.rb +122 -0
  43. data/spec/spec_helper.rb +10 -21
  44. data/spec/unified2_spec.rb +3 -3
  45. metadata +124 -140
  46. data/lib/unified2/payload.rb +0 -114
data/lib/unified2.rb CHANGED
@@ -10,10 +10,10 @@ require 'unified2/exceptions'
10
10
  require 'unified2/version'
11
11
 
12
12
  #
13
- # Unified2 Namespace
13
+ # Unified2
14
14
  #
15
15
  module Unified2
16
-
16
+
17
17
  #
18
18
  # Configuration File Types
19
19
  #
@@ -107,78 +107,51 @@ module Unified2
107
107
  #
108
108
  # @raise [FileNotReadable] Path not readable
109
109
  # @raise [FileNotFound] File not found
110
+ # @raise [BinaryReadError] Invalid position or file
110
111
  #
111
112
  # @return [nil]
112
113
  #
113
114
  def self.watch(path, position=:first, &block)
115
+ validate_path(path)
114
116
 
115
- unless File.exists?(path)
116
- raise FileNotFound, "Error - #{path} not found."
117
- end
118
-
119
- if File.readable?(path)
120
- io = File.open(path)
121
-
122
- case position
123
- when Integer, Fixnum
124
-
125
- event_id = position.to_i.zero? ? 1 : position.to_i
126
- @event = Event.new(event_id)
127
-
128
- when Symbol, String
129
-
130
- case position.to_sym
131
- when :last
132
-
133
- until io.eof?
134
- event = Unified2::Constructor::Construct.read(io)
135
- event_id = event.data.event_id if event
136
- end
117
+ io = File.open(path)
137
118
 
138
- @event = Event.new(event_id + 1)
119
+ case position
120
+ when Integer
121
+ io.sysseek(position, IO::SEEK_CUR)
139
122
 
140
- # set event_id to false to catch
141
- # beginning loop and process
142
- event_id = false
143
-
144
- when :first
145
- begin
146
-
147
- first_open = File.open(path)
148
- first_event = Unified2::Constructor::Construct.read(first_open)
149
- first_open.close
150
- event_id = first_event.data.event_id
151
- @event = Event.new(event_id)
152
-
153
- rescue EOFError
154
- sleep 5
155
- retry
156
- end
157
-
158
- end
123
+ when Symbol, String
124
+
125
+ if position == :last
126
+ io.sysseek(0, IO::SEEK_END)
127
+ else
128
+ io.sysseek(0, IO::SEEK_SET)
159
129
  end
130
+
131
+ else
132
+ io.sysseek(0, IO::SEEK_SET)
133
+ end
160
134
 
161
- loop do
162
- begin
163
- event = Unified2::Constructor::Construct.read(io)
164
-
165
- if event_id
166
- if event.data.event_id.to_i > (event_id - 1)
167
- check_event(event, block)
168
- end
169
- else
170
- check_event(event, block)
171
- end
172
-
173
- rescue EOFError
174
- sleep 5
175
- retry
176
- end
177
- end
135
+ # Start with a null event.
136
+ # This will always be ignored.
137
+ @event = Event.new(0)
178
138
 
179
- else
180
- raise FileNotReadable, "Error - #{path} not readable."
139
+ loop do
140
+ begin
141
+ event = Unified2::Constructor::Construct.read(io)
142
+ check_event(event, block)
143
+ rescue EOFError
144
+ sleep 5
145
+ retry
146
+ end
181
147
  end
148
+
149
+ rescue RuntimeError
150
+ raise(BinaryReadError, "incorrect file format or position seek error")
151
+ rescue Interrupt
152
+ io.pos if io
153
+ ensure
154
+ io.close if io
182
155
  end
183
156
 
184
157
  #
@@ -196,40 +169,51 @@ module Unified2
196
169
  # @return [nil]
197
170
  #
198
171
  def self.read(path, &block)
172
+ validate_path(path)
199
173
 
200
- unless File.exists?(path)
201
- raise FileNotFound, "Error - #{path} not found."
202
- end
174
+ io = File.open(path)
175
+
176
+ # Start with a null event.
177
+ # This will always be ignored.
178
+ @event = Event.new(0)
203
179
 
204
- if File.readable?(path)
205
- io = File.open(path)
180
+ until io.eof?
181
+ event = Unified2::Constructor::Construct.read(io)
182
+ check_event(event, block)
183
+ end
206
184
 
207
- first_open = File.open(path)
208
- first_event = Unified2::Constructor::Construct.read(first_open)
209
- first_open.close
185
+ rescue Interrupt
186
+ ensure
187
+ io.close if io
188
+ end
210
189
 
211
- @event = Event.new(first_event.data.event_id)
190
+ private
212
191
 
213
- until io.eof?
214
- event = Unified2::Constructor::Construct.read(io)
215
- check_event(event, block)
216
- end
192
+ def self.validate_path(path)
193
+ unless File.exists?(path)
194
+ raise FileNotFound, "Error - #{path} not found."
195
+ end
217
196
 
218
- else
197
+ unless File.readable?(path)
219
198
  raise FileNotReadable, "Error - #{path} not readable."
220
- end
199
+ end
221
200
  end
222
201
 
223
- private
202
+ def self.check_event(event, block)
224
203
 
225
- def self.check_event(event, block)
204
+ if event.data.respond_to?(:event_id)
226
205
  if @event.id == event.data.event_id
227
206
  @event.load(event)
228
207
  else
229
- block.call(@event)
208
+ block.call(@event) unless @event.id.zero?
209
+ @extra_data = false
230
210
  @event = Event.new(event.data.event_id)
231
211
  @event.load(event)
232
212
  end
213
+ else
214
+ @event.load(event)
233
215
  end
234
216
 
217
+ end
218
+
235
219
  end
data/spec/event_spec.rb CHANGED
@@ -4,15 +4,15 @@ require 'unified2'
4
4
  describe Event do
5
5
 
6
6
  before(:all) do
7
- @event = Unified2.first('example/seeds/unified2.log')
7
+ @event = Unified2.first('example/seeds/unified2-current.log')
8
8
  end
9
9
 
10
10
  it "should have an event_id" do
11
- @event.to_i.should == 1
11
+ @event.id.should == 1
12
12
  end
13
13
 
14
14
  it "should have an event time" do
15
- @event.timestamp.to_s.should == '2010-10-05 22:50:18 -0400'
15
+ @event.timestamp.to_s.should == '2011-11-12 16:04:25 -0500'
16
16
  end
17
17
 
18
18
  it "should have a sensor id" do
@@ -32,35 +32,35 @@ describe Event do
32
32
  end
33
33
 
34
34
  it "should have a source address" do
35
- @event.source_ip.should == "24.19.7.110"
35
+ @event.source_ip.should == "10.0.1.6"
36
36
  end
37
37
 
38
38
  it "should have a source port" do
39
- @event.source_port.should == 0
39
+ @event.source_port.should == 52378
40
40
  end
41
41
 
42
42
  it "should have a destination address" do
43
- @event.destination_ip.should == "10.0.1.6"
43
+ @event.destination_ip.should == "199.47.216.149"
44
44
  end
45
45
 
46
46
  it "should have a destination port" do
47
- @event.destination_port.should == 0
47
+ @event.destination_port.should == 80
48
48
  end
49
49
 
50
50
  it "should have a protocol" do
51
- @event.protocol.to_s.should == 'ICMP'
51
+ @event.protocol.to_s.should == 'TCP'
52
52
  end
53
53
 
54
54
  it "should have a severity" do
55
- @event.severity.should == 3
55
+ @event.severity.should == 1
56
56
  end
57
57
 
58
58
  it "should have an event checksum" do
59
- @event.checksum.should == "6e96db6e8fe649c939711400ea4625eb"
59
+ @event.checksum.should == "01af8a5245fce250989b00990406fa63"
60
60
  end
61
61
 
62
62
  it "should have a signature id" do
63
- @event.signature.id.should == 485
63
+ @event.signature.id.should == 18608
64
64
  end
65
65
 
66
66
  it "should have a signature generator id" do
@@ -68,7 +68,7 @@ describe Event do
68
68
  end
69
69
 
70
70
  it "should have a signature revision" do
71
- @event.signature.revision.should == 5
71
+ @event.signature.revision.should == 3
72
72
  end
73
73
 
74
74
  it "should have a signature thats not blank" do
@@ -76,37 +76,50 @@ describe Event do
76
76
  end
77
77
 
78
78
  it "should have a signature name" do
79
- @event.signature.name.should == "DELETED ICMP Destination Unreachable" \
80
- " Communication Administratively Prohibited"
79
+ @event.signature.name.should == "POLICY Dropbox desktop software in use"
81
80
  end
82
81
 
83
82
  it "should have a classification id" do
84
- @event.classification.id.should == 29
83
+ @event.classification.id.should == 33
85
84
  end
86
85
 
87
86
  it "should have a classification short name" do
88
- @event.classification.short.should == "misc-activity"
87
+ @event.classification.short.should == "policy-violation"
89
88
  end
90
89
 
91
90
  it "should have a classification severity" do
92
- @event.classification.severity.should == 3
91
+ @event.classification.severity.should == 1
93
92
  end
94
93
 
95
94
  it "should have a classification name" do
96
- @event.classification.name.should == "Misc activity"
95
+ @event.classification.name.should == "Potential Corporate Privacy Violation"
97
96
  end
98
-
99
- it "should have a payload thats not blank" do
100
- @event.payload.blank?.should == false
97
+
98
+ it "should have zero packets associated with this event" do
99
+ @event.packets.count.should == 0
101
100
  end
102
-
103
- it "should have a hex payload" do
104
- p = "000000004520008323bc000032113a080a0001061813076e90c84fac006fe498"
105
- @event.payload.hex.should == p
101
+
102
+ it "event extras count should equal 2" do
103
+ @event.extras.count.should == 2
104
+ end
105
+
106
+ it "should have extra data thats not blank" do
107
+ @event.extras.first.blank?.should == false
106
108
  end
107
109
 
108
- it "should have a payload length" do
109
- @event.payload.length.should == 70
110
+ it "extra data should have the correct value" do
111
+ @event.extras.first.value.should == "/subscribe?host_int=26273724&ns_map=2895792_52721831662858160,15287777_4310255073,2027874_776915740822270306,2816020_68722292756,564088_4146784271384222584,555213_5414107641578813645&ts=1321131865"
112
+ @event.extras.last.value.should == "notify9.dropbox.com"
113
+ end
114
+
115
+ it "extra data should have a value length" do
116
+ @event.extras.first.length.should == 204
117
+ end
118
+
119
+ it "extra data should have a header" do
120
+ @event.extras.first.header[:event_type].should == 4
121
+ @event.extras.first.header[:event_length].should == 228
110
122
  end
111
123
 
112
124
  end
125
+
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+ require 'unified2'
3
+
4
+ describe "LegacyEvent" do
5
+
6
+ before(:all) do
7
+ @event = Unified2.first('example/seeds/unified2-legacy.log')
8
+ end
9
+
10
+ it "should have an event_id" do
11
+ @event.to_i.should == 1
12
+ end
13
+
14
+ it "should have an event time" do
15
+ @event.timestamp.to_s.should == '2010-10-05 22:50:18 -0400'
16
+ end
17
+
18
+ it "should have a sensor id" do
19
+ @event.sensor.id.should == 50000000000
20
+ end
21
+
22
+ it "should have a sensor name" do
23
+ @event.sensor.name.should == "Example Sensor"
24
+ end
25
+
26
+ it "should have a sensor interface" do
27
+ @event.sensor.interface.should == "en1"
28
+ end
29
+
30
+ it "should have a sensor hostname" do
31
+ @event.sensor.hostname.should == "W0ots.local"
32
+ end
33
+
34
+ it "should have a source address" do
35
+ @event.source_ip.should == "24.19.7.110"
36
+ end
37
+
38
+ it "should have a source port" do
39
+ @event.source_port.should == 3
40
+ end
41
+
42
+ it "should have a destination address" do
43
+ @event.destination_ip.should == "10.0.1.6"
44
+ end
45
+
46
+ it "should have a destination port" do
47
+ @event.destination_port.should == 13
48
+ end
49
+
50
+ it "should have a protocol" do
51
+ @event.protocol.to_s.should == 'ICMP'
52
+ end
53
+
54
+ it "should have a severity" do
55
+ @event.severity.should == 3
56
+ end
57
+
58
+ it "should have an event checksum" do
59
+ @event.checksum.should == "6e96db6e8fe649c939711400ea4625eb"
60
+ end
61
+
62
+ it "should have a signature id" do
63
+ @event.signature.id.should == 485
64
+ end
65
+
66
+ it "should have a signature generator id" do
67
+ @event.signature.generator.should == 1
68
+ end
69
+
70
+ it "should have a signature revision" do
71
+ @event.signature.revision.should == 5
72
+ end
73
+
74
+ it "should have a signature thats not blank" do
75
+ @event.signature.blank.should == false
76
+ end
77
+
78
+ it "should have a signature name" do
79
+ @event.signature.name.should == "DELETED ICMP Destination Unreachable" \
80
+ " Communication Administratively Prohibited"
81
+ end
82
+
83
+ it "should have a classification id" do
84
+ @event.classification.id.should == 29
85
+ end
86
+
87
+ it "should have a classification short name" do
88
+ @event.classification.short.should == "misc-activity"
89
+ end
90
+
91
+ it "should have a classification severity" do
92
+ @event.classification.severity.should == 3
93
+ end
94
+
95
+ it "should have a classification name" do
96
+ @event.classification.name.should == "Misc activity"
97
+ end
98
+
99
+ it "should have a one packet associated with this event" do
100
+ @event.packets.count.should == 1
101
+ end
102
+
103
+ it "should have a payload thats not blank" do
104
+ @event.packets.first.blank?.should == false
105
+ end
106
+
107
+ it "should have a hex payload without the header" do
108
+ p = "000000004520008323bc000032113a080a0001061813076e90c84fac006fe498"
109
+ @event.packets.first.hex(false).should == p
110
+ end
111
+
112
+ it "should have a hex payload with the header" do
113
+ p = "0026bb1b75d30016cbc32b43080045000038c3e80000f001dc551813076e0a000106" +
114
+ "030d3776000000004520008323bc000032113a080a0001061813076e90c84fac006fe498"
115
+ @event.packets.first.hex(true).should == p
116
+ end
117
+
118
+ it "should have a payload length" do
119
+ @event.packets.first.length.should == 70
120
+ end
121
+
122
+ end
data/spec/spec_helper.rb CHANGED
@@ -7,31 +7,20 @@ include Unified2
7
7
  module Unified2
8
8
 
9
9
  def self.first(path)
10
- unless File.exists?(path)
11
- raise FileNotFound, "Error - #{path} not found."
12
- end
13
-
14
- if File.readable?(path)
15
- io = File.open(path)
10
+ validate_path(path)
16
11
 
17
- first_open = File.open(path)
18
- first_event = Unified2::Constructor::Construct.read(first_open)
19
- first_open.close
12
+ io = File.open(path)
13
+ io.sysseek(0, IO::SEEK_SET)
20
14
 
21
- @event = Event.new(first_event.data.event_id)
15
+ @event = Event.new(1)
22
16
 
23
- loop do
24
- event = Unified2::Constructor::Construct.read(io)
25
-
26
- if event.data.event_id.to_i == @event.id.to_i
27
- @event.load(event)
28
- else
29
- return @event
30
- end
17
+ loop do
18
+ event = Unified2::Constructor::Construct.read(io)
19
+ if event.data.respond_to?(:event_id)
20
+ return @event if event.data.event_id != @event.id
31
21
  end
32
22
 
33
- else
34
- raise FileNotReadable, "Error - #{path} not readable."
23
+ @event.load(event)
35
24
  end
36
25
  end
37
26
 
@@ -46,4 +35,4 @@ Unified2.configuration do
46
35
  load :signatures, 'example/seeds/sid-msg.map'
47
36
  load :generators, 'example/seeds/gen-msg.map'
48
37
  load :classifications, 'example/seeds/classification.config'
49
- end
38
+ end
@@ -12,7 +12,7 @@ describe Unified2 do
12
12
  end
13
13
 
14
14
  it "should have the correct signature size" do
15
- Unified2.signatures.size.should == 16710
15
+ Unified2.signatures.size.should == 19243
16
16
  end
17
17
 
18
18
  it "should have the correct signature name" do
@@ -48,7 +48,7 @@ describe Unified2 do
48
48
  end
49
49
 
50
50
  it "should have the correct generator size" do
51
- Unified2.generators.size.should == 388
51
+ Unified2.generators.size.should == 461
52
52
  end
53
53
 
54
54
  it "should have the correct generator id" do
@@ -91,4 +91,4 @@ describe Unified2 do
91
91
  Unified2.sensor.name.should == "Mephux FTW!"
92
92
  end
93
93
 
94
- end
94
+ end