cef 1.0.0 → 2.1.1.pre

Sign up to get free protection for your applications and to get access to all the features.
data/lib/cef.rb CHANGED
@@ -1,10 +1,46 @@
1
+ require 'json'
1
2
  require 'date'
2
3
  require 'socket'
4
+ require 'hashie'
5
+ require 'ipaddr'
6
+
3
7
  require 'cef/version'
4
- require 'cef/constants'
8
+ require 'cef/time_extensions'
5
9
  require 'cef/event'
6
- require 'cef/sender'
10
+ require 'cef/loggers/cef_file'
11
+ require 'cef/loggers/cef_syslog_udp'
7
12
 
8
13
 
9
14
  module CEF
10
- end
15
+ def self.configure(path=File.join(ENV['HOME'],'.cef.json'))
16
+ configuration=Hashie.symbolize_keys(JSON.parse(File.read(path)))
17
+ self.logger(configuration)
18
+ end
19
+
20
+ def self.logger(config: {}, type: CEF::Loggers::SyslogUdp)
21
+ configuration = case config
22
+ when String
23
+ JSON.parse(File.read(config))
24
+ when Hash
25
+ config
26
+ when File,IO
27
+ JSON.parse(config.read)
28
+ else
29
+ fail ArgumentError, "#{config} is not a valid CEF configuration"
30
+ end
31
+
32
+ logger_type = case type
33
+ when Class
34
+ type
35
+ when String
36
+ Module.const_get(type)
37
+ else
38
+ fail ArgumentError, "#{type} is not a valid class"
39
+ end
40
+
41
+ logger_type.new(configuration)
42
+ end
43
+ def self.event(*args)
44
+ CEF::Event.new(*args)
45
+ end
46
+ end
@@ -1,152 +1,100 @@
1
1
  module CEF
2
- class Event
3
- attr_accessor :syslog_pri, :event_time, :my_hostname
4
- # set up accessors for all of the CEF event attributes. ruby meta magic.
5
- CEF::ATTRIBUTES.each do |k,v|
6
- self.instance_eval do
7
- attr_accessor k
2
+ class Event < Hashie::Dash
3
+ include Hashie::Extensions::Coercion
4
+ LOG_FORMAT = '<%d>%s %s CEF:0|%s|%s'
5
+ LOG_TIME_FORMAT = '%b %d %Y %H:%M:%S'
6
+
7
+ PREFIX_KEYS = [ :deviceVendor, :deviceProduct, :deviceVersion, :deviceEventClassId, :name, :deviceSeverity]
8
+ EXTENSION_ESCAPES = {
9
+ %r{=} => '\=',
10
+ %r{\n} => '\n',
11
+ %r{\r} => '\r',
12
+ %r{\\} => '\\'
13
+ }
14
+
15
+ PREFIX_ESCAPES = {
16
+ %r{(\||\\)} => '\\\\\&'
17
+ }
18
+
19
+ SCHEMA_CONFIG_FILE = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','conf','cef-schema.json')
20
+ @@schema = config = JSON.parse(File.read(SCHEMA_CONFIG_FILE))
21
+ .extend(Hashie::Extensions::DeepMerge)
22
+
23
+ config['prefix'].each do |json_key, default_val|
24
+ self.class_eval do
25
+ key = json_key.to_sym
26
+ property key, { default: default_val, required: true }
27
+ end
28
+ end
29
+
30
+ config['extension'].each do |key|
31
+ self.class_eval { property key.to_sym }
8
32
  end
33
+
34
+ config['types'].each do |key,klass_name|
35
+ self.class_eval { coerce_key key.to_sym, Module.const_get(klass_name) }
36
+ end
37
+
38
+ def self.schema
39
+ @@schema
9
40
  end
10
41
 
11
- def attrs
12
- CEF::ATTRIBUTES
42
+ def to_cef
43
+ if extension_data == {}
44
+ format('%s|',format_prefix)
45
+ else
46
+ format('%s|%s',format_prefix,format_extension)
47
+ end
13
48
  end
14
-
15
- # so we can CEF::Event.new(:foo=>"bar")
16
- def initialize( *params )
17
- @event_time = Time.new
18
- @deviceVendor = "breed.org"
19
- @deviceProduct = "CEF"
20
- @deviceVersion = CEF::VERSION
21
- @deviceEventClassId = "0:event"
22
- @deviceSeverity = CEF::SEVERITY_LOW
23
- @name = "unnamed event"
24
- # used to avoid requiring syslog.h on windoze
25
- #syslog_pri= Syslog::LOG_LOCAL0 | Syslog::LOG_NOTICE
26
- @syslog_pri = 131
27
- @my_hostname = Socket::gethostname
28
- @other_attrs={}
29
- @additional={}
30
- Hash[*params].each { |k,v| self.send("%s="%k,v) }
31
- yield self if block_given?
32
- self
49
+
50
+ def format_prefix
51
+ prefix_values.map { |value| escape_prefix_value(value) }.join('|')
33
52
  end
34
-
35
- # returns a cef formatted string
36
- def to_s
37
- log_time=event_time.strftime(CEF::LOG_TIME_FORMAT)
38
-
39
- sprintf(
40
- CEF::LOG_FORMAT,
41
- syslog_pri.to_s,
42
- log_time,
43
- my_hostname,
44
- format_prefix,
45
- format_extension
46
- )
53
+
54
+ def prefix_values
55
+ [ 'CEF:0', *PREFIX_KEYS.map {|k| self.send(k) } ]
47
56
  end
48
57
 
49
- # used for non-schema fields
50
- def set_additional(k,v)
51
- @additional[k]=v
58
+ def format_extension
59
+ extension_keys.zip(extension_values)
60
+ .map {|k,v| format('%s=%s',k,v)}
61
+ .join(' ')
52
62
  end
53
- def get_additional(k,v)
54
- @additional[k]
63
+
64
+ def extension_keys
65
+ extension_data.keys.map {|k| self.class.schema["key_names"].fetch(k.to_s,k.to_s)}
55
66
  end
56
67
 
57
- #private
58
- # make a guess as to how the time was set. parse strings and convert
59
- # them to epoch milliseconds, or leave it alone if it looks like a number
60
- # bigger than epoch milliseconds when i wrote this.
61
- # def time_convert(val)
62
- #
63
- # converted=case val
64
- # when String
65
- # if val.match(%r{\A[0-9]+\Z})
66
- # converted=val.to_i
67
- # else
68
- # res=Chronic.parse(val)
69
- # converted=Time.at(res).to_i * 1000
70
- # end
71
- # when Integer,Bignum
72
- # if val < 1232589621000 #Wed Jan 21 20:00:21 -0600 2009
73
- # val * 1000
74
- # else
75
- # val
76
- # end
77
- # end
78
- #
79
- # end
80
-
81
- # escape only pipes and backslashes in the prefix. you bet your sweet
82
- # ass there's a lot of backslashes in the substitution. you can thank
83
- # the three levels of lexical analysis/substitution in the ruby interpreter
84
- # for that.
85
-
86
- def escape_prefix_value(val)
87
- escapes={
88
- %r{(\||\\)} => '\\\\\&'
89
- }
90
- escapes.reduce(val) do|memo,replace|
91
- memo=memo.gsub(*replace)
92
- end
93
- end
68
+ def extension_values
69
+ extension_data.values.map {|v| escape_extension_value(v)}
70
+ end
94
71
 
95
- # only equals signs need to be escaped in the extension. i think.
96
- # TODO: something in the spec about \n and some others.
97
- def escape_extension_value(val)
98
- escapes = {
99
- %r{=} => '\=',
100
- %r{\n} => ' ',
101
- %r{\\} => '\\'
102
- }
103
- escapes.reduce(val) do |memo,replace|
104
- memo=memo.gsub(*replace)
105
- end
106
- end
72
+ def extension_data
73
+ self.to_h.reject {|k,v| PREFIX_KEYS.include?(k)}
74
+ end
107
75
 
108
- # returns a pipe-delimeted list of prefix attributes
109
- def format_prefix
110
- values = CEF::PREFIX_ATTRIBUTES.keys.map { |k| self.send(k) }
111
- escaped = values.map do |value|
112
- escape_prefix_value(value)
113
- end
114
- escaped.join('|')
76
+ def escape_prefix_value(val)
77
+ case val
78
+ when String
79
+ PREFIX_ESCAPES.reduce(val) do|memo,replace|
80
+ memo=memo.gsub(*replace)
81
+ end
82
+ else
83
+ val.to_s
115
84
  end
85
+ end
116
86
 
117
- # returns a space-delimeted list of attribute=value pairs for all optionals
118
- def format_extension
119
- extensions = CEF::EXTENSION_ATTRIBUTES.keys.map do |meth|
120
- value = self.send(meth)
121
- next if value.nil?
122
- shortname = CEF::EXTENSION_ATTRIBUTES[meth]
123
- [shortname, escape_extension_value(value)].join("=")
124
- end
125
-
126
- # make sure time comes out as milliseconds since epoch
127
- times = CEF::TIME_ATTRIBUTES.keys.map do |meth|
128
- value = self.send(meth)
129
- next if value.nil?
130
- shortname = CEF::TIME_ATTRIBUTES[meth]
131
- [shortname, escape_extension_value(value)].join("=")
132
- end
133
- (extensions + times).compact.join(" ")
87
+ def escape_extension_value(val)
88
+ case val
89
+ when String
90
+ EXTENSION_ESCAPES.reduce(val) do |memo,replace|
91
+ memo=memo.gsub(*replace)
92
+ end
93
+ when Time
94
+ val.to_i * 1000
95
+ else
96
+ val.to_s
134
97
  end
98
+ end
135
99
  end
136
100
  end
137
-
138
- # vendor= self.deviceVendor || "Breed"
139
- # product= self.deviceProduct || "CEF Sender"
140
- # version= self.deviceVersion || CEF::VERSION
141
- # declid= self.deviceEventClassId || "generic:0"
142
- # name= self.name || "Generic Event"
143
- # sev= self.deviceSeverity || "1"
144
- # %w{ deviceVendor deviceProduct deviceVersion deviceEvent}
145
- # cef_prefix="%s|%s|%s|%s|%s|%s" % [
146
- # prefix_escape(vendor),
147
- # prefix_escape(product),
148
- # prefix_escape(version),
149
- # prefix_escape(declid),
150
- # prefix_escape(name),
151
- # prefix_escape(sev),
152
- # ]
@@ -0,0 +1,19 @@
1
+ module CEF
2
+ module Loggers
3
+ class CefFile
4
+ attr_accessor :path, :defaults, :append
5
+ attr_reader :fh
6
+ def initialize(*args)
7
+ Hash[*args].each {|k,v| self.send(format('%s=',k),v)}
8
+ @defaults ||= {}
9
+ @path ||= 'cef.log'
10
+ @append = false if @append.nil?
11
+ @fh = File.open(path, append ? 'w+' : 'w')
12
+ self
13
+ end
14
+ def emit(*events)
15
+ events.each {|e| fh.puts(e.to_cef)}
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,40 @@
1
+ module CEF
2
+ module Loggers
3
+ class SyslogUdp
4
+ SYSLOG_TIME_FORMAT = '%b %d %Y %H:%M:%S'
5
+ SYSLOG_HEADER = '<%d>%s %s'
6
+
7
+
8
+ attr_accessor :receiver, :port, :defaults
9
+ attr_reader :sock
10
+
11
+ def initialize(*args)
12
+ Hash[*args].each {|k,v| self.send(format('%s=',k),v)}
13
+ @receiver ||= '127.0.0.1'
14
+ @port ||= 514
15
+ @defaults ||= {}
16
+ @sock = UDPSocket.new
17
+ @sock.connect(@receiver, @port)
18
+ self
19
+ end
20
+
21
+ def header(facility=0,priority=0)
22
+ format(SYSLOG_HEADER,
23
+ 131, # facility|priority
24
+ Time.new.strftime(SYSLOG_TIME_FORMAT), # now
25
+ Socket::gethostname) # hostname
26
+ end
27
+
28
+ def formatted_message(event)
29
+ format('%s %s', header, event.to_cef )
30
+ end
31
+
32
+ def emit(*events)
33
+ events.each do |event|
34
+ defaults.each {|k,v| event.send(format("%s=",k),v) }
35
+ sock.send(formatted_message(event),0)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ module CEF
2
+ module TimeExtensions
3
+ def coerce(val)
4
+ case val
5
+ when Integer
6
+ Time.at(val)
7
+ when String
8
+ Time.parse(val)
9
+ else
10
+ val
11
+ end
12
+ end
13
+ end
14
+ end
15
+ Time.extend(CEF::TimeExtensions)
@@ -1,3 +1,3 @@
1
1
  module CEF
2
- VERSION = "1.0.0"
2
+ VERSION = '2.1.1.pre'
3
3
  end
metadata CHANGED
@@ -1,87 +1,143 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cef
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.1.1.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Breed
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2011-03-30 00:00:00.000000000 Z
11
+ date: 2015-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hashie
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rake
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
- - - '>='
31
+ - - ">="
18
32
  - !ruby/object:Gem::Version
19
33
  version: '0'
20
34
  type: :development
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
- - - '>='
38
+ - - ">="
25
39
  - !ruby/object:Gem::Version
26
40
  version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rspec
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
- - - '>='
45
+ - - ">="
32
46
  - !ruby/object:Gem::Version
33
47
  version: '0'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
- - - '>='
52
+ - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - '>='
59
+ - - ">="
46
60
  - !ruby/object:Gem::Version
47
61
  version: '0'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - '>='
66
+ - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: simplecov
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - '>='
73
+ - - ">="
60
74
  - !ruby/object:Gem::Version
61
75
  version: '0'
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - '>='
80
+ - - ">="
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: pry
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - '>='
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: guard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: guard-bundler
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: guard-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
74
130
  - !ruby/object:Gem::Version
75
131
  version: '0'
76
132
  type: :development
77
133
  prerelease: false
78
134
  version_requirements: !ruby/object:Gem::Requirement
79
135
  requirements:
80
- - - '>='
136
+ - - ">="
81
137
  - !ruby/object:Gem::Version
82
138
  version: '0'
83
- description: ' format/send CEF logs via API+syslog or client program '
84
- email: ' opensource@breed.org '
139
+ description: " format/send CEF logs via API+syslog or client program "
140
+ email: " opensource@breed.org "
85
141
  executables:
86
142
  - cef_sender
87
143
  extensions: []
@@ -89,25 +145,24 @@ extra_rdoc_files:
89
145
  - LICENSE.txt
90
146
  - README.rdoc
91
147
  files:
92
- - .document
93
- - .gitignore
94
- - .rspec
148
+ - ".document"
149
+ - ".gitignore"
150
+ - ".rspec"
95
151
  - Gemfile
152
+ - Guardfile
96
153
  - LICENSE.txt
97
154
  - README.rdoc
98
155
  - Rakefile
99
156
  - VERSION
100
157
  - bin/cef_sender
101
158
  - cef.gemspec
159
+ - conf/cef-schema.json
102
160
  - lib/cef.rb
103
- - lib/cef/constants.rb
104
161
  - lib/cef/event.rb
105
- - lib/cef/sender.rb
162
+ - lib/cef/loggers/cef_file.rb
163
+ - lib/cef/loggers/cef_syslog_udp.rb
164
+ - lib/cef/time_extensions.rb
106
165
  - lib/cef/version.rb
107
- - spec/lib/cef/event_spec.rb
108
- - spec/lib/cef/sender_spec.rb
109
- - spec/lib/cef_spec.rb
110
- - spec/spec_helper.rb
111
166
  homepage: http://github.com/ryanbreed/cef
112
167
  licenses:
113
168
  - MIT
@@ -118,23 +173,18 @@ require_paths:
118
173
  - lib
119
174
  required_ruby_version: !ruby/object:Gem::Requirement
120
175
  requirements:
121
- - - '>='
176
+ - - ">="
122
177
  - !ruby/object:Gem::Version
123
178
  version: '0'
124
179
  required_rubygems_version: !ruby/object:Gem::Requirement
125
180
  requirements:
126
- - - '>='
181
+ - - ">"
127
182
  - !ruby/object:Gem::Version
128
- version: '0'
183
+ version: 1.3.1
129
184
  requirements: []
130
185
  rubyforge_project:
131
- rubygems_version: 2.0.14
186
+ rubygems_version: 2.4.5.1
132
187
  signing_key:
133
188
  specification_version: 4
134
189
  summary: CEF Generation Library and Client
135
- test_files:
136
- - spec/lib/cef/event_spec.rb
137
- - spec/lib/cef/sender_spec.rb
138
- - spec/lib/cef_spec.rb
139
- - spec/spec_helper.rb
140
- has_rdoc:
190
+ test_files: []