cef 1.0.0 → 2.1.1.pre

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.
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: []