snort-rule 1.4.1 → 1.5.2
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.
- checksums.yaml +4 -4
- data/README.md +42 -0
- data/lib/snort/rule.rb +28 -1
- data/lib/snort/rule/version.rb +1 -1
- data/lib/snort/ruleset.rb +165 -0
- data/test/helper.rb +1 -0
- data/test/test_snort-community-rules.rb +60 -10
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b475f0c004d1ff722c739e102fc44b8de4ae57b0
|
4
|
+
data.tar.gz: 16ceb5394563f2d3c6cbde7ed83c7e5a733cfbaf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3d74558797835687eb71aa1cb662a89f3d634e063c977bfc9829fc1a28e378e6060b13b281de97069decfe3632e427d2be215d65e3694899d103817fbe70cde
|
7
|
+
data.tar.gz: e9a47d83b028989f5cb91900acad0fb10f162984b9e44fd6c5f3139885719cf358b53e4a97b0a993937e6314944d790bd362576f01d963e794b776e7154cc322
|
data/README.md
CHANGED
@@ -47,6 +47,48 @@ Or install it yourself as:
|
|
47
47
|
rule = Snort::Rule.parse("pass udp 192.168.0.1 any <> any 53 ( sid:48; threshold:type limit,track by_src,count 1,seconds 3600; ref:ref1; ref:ref2;)")
|
48
48
|
rule.to_s # => "pass udp 192.168.0.1 any <> any 53 (sid:48; threshold:type limit,track by_src,count 1,seconds 3600; ref:ref1; ref:ref2;)"
|
49
49
|
|
50
|
+
## Snort::RuleSet Usage
|
51
|
+
|
52
|
+
require 'snort/ruleset'
|
53
|
+
ruleset = Snort::RuleSet::from_file("community.rules")
|
54
|
+
ruleset.length # => 3127
|
55
|
+
rules.length
|
56
|
+
rules.count{|r| ! r.enabled} # => 2522
|
57
|
+
rules.count{|r| r.enabled} # => 605
|
58
|
+
rules.disable_all
|
59
|
+
rules.count{|r| r.enabled} # => 0
|
60
|
+
rules.count{|r| ! r.enabled} # => 3127
|
61
|
+
rules.enable_all
|
62
|
+
rules.count{|r| r.enabled} # => 3127
|
63
|
+
rules.count{|r| ! r.enabled} # => 0
|
64
|
+
rules.disable do |r|
|
65
|
+
r.get_option_first("msg").match(/^"MALWARE\-CNC/)
|
66
|
+
end
|
67
|
+
rules.count{|r| ! r.enabled} # => 392
|
68
|
+
rules.count{|r| r.enabled} # => 2735
|
69
|
+
rules.delete do |r|
|
70
|
+
options = r.get_options("content")
|
71
|
+
if options
|
72
|
+
options.find { |o|
|
73
|
+
o.arguments.find { |a|
|
74
|
+
a.match(/"POST"/)
|
75
|
+
}
|
76
|
+
}
|
77
|
+
else
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
rules.count{|r| ! r.enabled} # => 343
|
82
|
+
rules.count{|r| r.enabled} # 2726
|
83
|
+
rules.delete_all
|
84
|
+
rules.length # => 0
|
85
|
+
rules.count{|r| r.enabled} # => 0
|
86
|
+
rules.count{|r| ! r.enabled} # => 0
|
87
|
+
|
88
|
+
ruleset = Snort::RuleSet::from_file("community.rules")
|
89
|
+
ruleset.to_file("new_community.rules")
|
90
|
+
|
91
|
+
|
50
92
|
## Contributing
|
51
93
|
|
52
94
|
1. Fork it
|
data/lib/snort/rule.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require "snort/rule/version"
|
2
2
|
require "snort/rule/option"
|
3
|
-
require 'pp'
|
4
3
|
# Generates and parses snort rules
|
5
4
|
#
|
6
5
|
# Authors:: Chris Lee (mailto:rubygems@chrislee.dhs.org),
|
@@ -10,6 +9,22 @@ require 'pp'
|
|
10
9
|
# Copyright:: Copyright (c) 2011 Chris Lee
|
11
10
|
# License:: Distributes under the same terms as Ruby
|
12
11
|
module Snort
|
12
|
+
|
13
|
+
class Comment
|
14
|
+
def initialize(comment)
|
15
|
+
@comment = comment
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
@comment
|
20
|
+
end
|
21
|
+
|
22
|
+
def enable
|
23
|
+
end
|
24
|
+
|
25
|
+
def disable
|
26
|
+
end
|
27
|
+
end
|
13
28
|
|
14
29
|
# This class stores and generates the features of a snort rule
|
15
30
|
class Rule
|
@@ -64,6 +79,14 @@ module Snort
|
|
64
79
|
rule
|
65
80
|
end
|
66
81
|
|
82
|
+
def enable
|
83
|
+
@enabled = true
|
84
|
+
end
|
85
|
+
|
86
|
+
def disable
|
87
|
+
@enabled = false
|
88
|
+
end
|
89
|
+
|
67
90
|
def add_option(option)
|
68
91
|
if option.class == Array
|
69
92
|
option = Snort::RuleOption.new(option[0], option[1,100])
|
@@ -97,6 +120,10 @@ module Snort
|
|
97
120
|
end
|
98
121
|
nil
|
99
122
|
end
|
123
|
+
|
124
|
+
def get_options(option_name)
|
125
|
+
@options_hash[option_name]
|
126
|
+
end
|
100
127
|
|
101
128
|
def get_option_first(option_name)
|
102
129
|
if @options_hash[option_name]
|
data/lib/snort/rule/version.rb
CHANGED
@@ -0,0 +1,165 @@
|
|
1
|
+
require "snort/rule/version"
|
2
|
+
require "open-uri"
|
3
|
+
|
4
|
+
# Generates and parses snort rules
|
5
|
+
#
|
6
|
+
# Authors:: Chris Lee (mailto:rubygems@chrislee.dhs.org),
|
7
|
+
# Will Green (will[ at ]hotgazpacho[ dot ]org),
|
8
|
+
# Justin Knox (jknox[ at ]indexzero[ dot ]org)
|
9
|
+
# Ryan Barnett (rbarnett[ at ]modsecurity[ dot ]org)
|
10
|
+
# Copyright:: Copyright (c) 2011 Chris Lee
|
11
|
+
# License:: Distributes under the same terms as Ruby
|
12
|
+
module Snort
|
13
|
+
# This class stores a set of rules and allows actions against them
|
14
|
+
class RuleSet
|
15
|
+
|
16
|
+
def RuleSet::from_file(file)
|
17
|
+
if file.class == File
|
18
|
+
fh = file
|
19
|
+
else
|
20
|
+
fh = open(file.to_s, 'r')
|
21
|
+
end
|
22
|
+
RuleSet::from_filehandle(fh)
|
23
|
+
end
|
24
|
+
|
25
|
+
def RuleSet::from_url(url)
|
26
|
+
RuleSet::from_file(url)
|
27
|
+
end
|
28
|
+
|
29
|
+
def RuleSet::from_filehandle(fh)
|
30
|
+
rules = RuleSet.new
|
31
|
+
fh.each_line do |line|
|
32
|
+
if line =~ /(alert|drop|pass|reject|activate|dynamic|activate|sdrop)/
|
33
|
+
begin
|
34
|
+
rule = Snort::Rule.parse(line)
|
35
|
+
if rule
|
36
|
+
rules << rule
|
37
|
+
else
|
38
|
+
rules << Snort::Comment.new(line.strip)
|
39
|
+
end
|
40
|
+
rescue ArgumentError => e
|
41
|
+
rescue NoMethodError => e
|
42
|
+
end
|
43
|
+
else
|
44
|
+
rules << Snort::Comment.new(line.strip)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
rules
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_filehandle(fh)
|
51
|
+
@ruleset.each do |rule|
|
52
|
+
fh.puts rule.to_s
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_file(file)
|
57
|
+
i_opened_it = false
|
58
|
+
if file.class == File
|
59
|
+
fh = file
|
60
|
+
else
|
61
|
+
i_opened_it = true
|
62
|
+
fh = open(file.to_s, 'w')
|
63
|
+
end
|
64
|
+
to_filehandle(fh)
|
65
|
+
if i_opened_it
|
66
|
+
fh.close
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize(ruleset=[])
|
71
|
+
@ruleset = ruleset
|
72
|
+
end
|
73
|
+
|
74
|
+
def <<(rule)
|
75
|
+
@ruleset << rule
|
76
|
+
end
|
77
|
+
|
78
|
+
def -(rule)
|
79
|
+
@ruleset -= rule
|
80
|
+
end
|
81
|
+
|
82
|
+
def length
|
83
|
+
@ruleset.find_all {|r| r.class == Snort::Rule}.length
|
84
|
+
end
|
85
|
+
|
86
|
+
def count(&block)
|
87
|
+
@ruleset.find_all {|r| r.class == Snort::Rule}.count(&block)
|
88
|
+
end
|
89
|
+
|
90
|
+
def enable(&block)
|
91
|
+
count = 0
|
92
|
+
@ruleset.find_all {|r| r.class == Snort::Rule}.each do |rule|
|
93
|
+
if block.call(rule)
|
94
|
+
rule.enable
|
95
|
+
count += 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
count
|
99
|
+
end
|
100
|
+
|
101
|
+
def disable(&block)
|
102
|
+
count = 0
|
103
|
+
@ruleset.find_all {|r| r.class == Snort::Rule}.each do |rule|
|
104
|
+
if block.call(rule)
|
105
|
+
rule.disable
|
106
|
+
count += 1
|
107
|
+
end
|
108
|
+
end
|
109
|
+
count
|
110
|
+
end
|
111
|
+
|
112
|
+
def delete(&block)
|
113
|
+
len = @ruleset.length
|
114
|
+
@ruleset.each do |rule|
|
115
|
+
next if rule.class == Snort::Comment
|
116
|
+
if block.call(rule)
|
117
|
+
@ruleset -= [rule]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
len - @ruleset.length
|
121
|
+
end
|
122
|
+
|
123
|
+
def enable_all
|
124
|
+
enable do |r|
|
125
|
+
true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def disable_all
|
130
|
+
disable do |r|
|
131
|
+
true
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def delete_all
|
136
|
+
delete do |r|
|
137
|
+
true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def enable_by_name(name)
|
142
|
+
enable do |r|
|
143
|
+
if r.name =~ name
|
144
|
+
true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def disable_by_name(name)
|
150
|
+
disable do |r|
|
151
|
+
if r.name =~ name
|
152
|
+
true
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def delete_by_name(name)
|
158
|
+
delete do |r|
|
159
|
+
if r.name =~ name
|
160
|
+
true
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
data/test/helper.rb
CHANGED
@@ -7,6 +7,7 @@ unless Kernel.respond_to?(:require_relative)
|
|
7
7
|
end
|
8
8
|
|
9
9
|
require_relative 'helper'
|
10
|
+
require 'pp'
|
10
11
|
|
11
12
|
class TestSnortCommunityRules < Minitest::Test
|
12
13
|
def setup
|
@@ -45,20 +46,69 @@ class TestSnortCommunityRules < Minitest::Test
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def test_complete_rules_file
|
48
|
-
rules =
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
49
|
+
rules = Snort::RuleSet::from_file("test/community-rules/community.rules")
|
50
|
+
assert_equal 3127, rules.length
|
51
|
+
assert_equal 2522, rules.count{|r| ! r.enabled}
|
52
|
+
assert_equal 605, rules.count{|r| r.enabled}
|
53
|
+
rules.disable_all
|
54
|
+
assert_equal 0, rules.count{|r| r.enabled}
|
55
|
+
assert_equal 3127, rules.count{|r| ! r.enabled}
|
56
|
+
rules.enable_all
|
57
|
+
assert_equal 3127, rules.count{|r| r.enabled}
|
58
|
+
assert_equal 0, rules.count{|r| ! r.enabled}
|
59
|
+
rules.disable do |r|
|
60
|
+
r.get_option_first("msg").match(/^"MALWARE\-CNC/)
|
61
|
+
end
|
62
|
+
assert_equal 392, rules.count{|r| ! r.enabled}
|
63
|
+
assert_equal 2735, rules.count{|r| r.enabled}
|
64
|
+
rules.delete do |r|
|
65
|
+
options = r.get_options("content")
|
66
|
+
if options
|
67
|
+
options.find { |o|
|
68
|
+
o.arguments.find { |a|
|
69
|
+
a.match(/"POST"/)
|
70
|
+
}
|
71
|
+
}
|
72
|
+
else
|
73
|
+
nil
|
58
74
|
end
|
59
75
|
end
|
76
|
+
assert_equal 343, rules.count{|r| ! r.enabled}
|
77
|
+
assert_equal 2726, rules.count{|r| r.enabled}
|
78
|
+
rules.delete_all
|
79
|
+
assert_equal 0, rules.length
|
80
|
+
assert_equal 0, rules.count{|r| r.enabled}
|
81
|
+
assert_equal 0, rules.count{|r| ! r.enabled}
|
82
|
+
end
|
83
|
+
|
84
|
+
# def test_ruleset_load_from_url
|
85
|
+
# rules = Snort::RuleSet::from_file("http://test.com/community.rules")
|
86
|
+
# assert_equal 3127, rules.length
|
87
|
+
# assert_equal 2522, rules.count{|r| ! r.enabled}
|
88
|
+
# assert_equal 605, rules.count{|r| r.enabled}
|
89
|
+
# end
|
90
|
+
|
91
|
+
def test_writing_file
|
92
|
+
rules = Snort::RuleSet::from_file("test/community-rules/community.rules")
|
60
93
|
assert_equal 3127, rules.length
|
61
94
|
assert_equal 2522, rules.count{|r| ! r.enabled}
|
62
95
|
assert_equal 605, rules.count{|r| r.enabled}
|
96
|
+
rules.to_file("test/community-rules/community.rules.test")
|
97
|
+
assert File.exist?("test/community-rules/community.rules.test")
|
98
|
+
total = enabled = disabled = 0
|
99
|
+
open("test/community-rules/community.rules.test", 'r').each_line do |l|
|
100
|
+
if l =~ /^(#)?\s*(alert|drop|pass|reject|activate|dynamic|activate|sdrop)/
|
101
|
+
total += 1
|
102
|
+
if l =~ /^#/
|
103
|
+
disabled += 1
|
104
|
+
else
|
105
|
+
enabled += 1
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
assert_equal 3127, total
|
110
|
+
assert_equal 2522, disabled
|
111
|
+
assert_equal 605, enabled
|
63
112
|
end
|
113
|
+
|
64
114
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: snort-rule
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- chrislee35
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- lib/snort/rule.rb
|
85
85
|
- lib/snort/rule/option.rb
|
86
86
|
- lib/snort/rule/version.rb
|
87
|
+
- lib/snort/ruleset.rb
|
87
88
|
- snort-rule.gemspec
|
88
89
|
- test/helper.rb
|
89
90
|
- test/test_snort-community-rules.rb
|