threshold 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1 -0
- data/CONTRIBUTING.md +119 -0
- data/LICENSE +21 -0
- data/README.md +7 -0
- data/Rakefile +11 -0
- data/lib/threshold/builder.rb +22 -0
- data/lib/threshold/event_filter.rb +134 -0
- data/lib/threshold/parser.rb +58 -0
- data/lib/threshold/patterns/base +91 -0
- data/lib/threshold/patterns/java +3 -0
- data/lib/threshold/patterns/ruby +3 -0
- data/lib/threshold/rate_filter.rb +172 -0
- data/lib/threshold/suppression.rb +114 -0
- data/lib/threshold/thresholds.rb +124 -0
- data/lib/threshold/version.rb +4 -0
- data/lib/threshold.rb +18 -0
- metadata +159 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 80c9df2ec5e67d9e05e5424a136adf096550cfb2
|
4
|
+
data.tar.gz: c3e99e5725ba57c4f55c254348fc7a2168655018
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6c8a93ec39c051fefa5f0b72a5f058ba9387f8fdfb11218c91315af3086ac8673229b08f0356b99870b98260e8918ff09ee9b15f5d5fae57eee4984694c57e6b
|
7
|
+
data.tar.gz: da6daf4ccea9b2581a9b9f3e6f043b4b29eec4d215db777e391e288181344383904116b5a0bc01289206dd383ccddce0e7bb516b621f6d40228a5b6ad7c18ad0
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*Next Release*
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
Contributing to Snort-thresholds
|
2
|
+
============================
|
3
|
+
|
4
|
+
You're encouraged to submit [pull requests](https://github.com/shadowbq/snort-thresholds/pulls), [propose features and discuss issues](https://github.com/shadowbq/snort-thresholds/issues).
|
5
|
+
|
6
|
+
#### Fork the Project
|
7
|
+
|
8
|
+
Fork the [project on Github](https://github.com/shadowbq/snort-thresholds) and check out your copy.
|
9
|
+
|
10
|
+
```
|
11
|
+
git clone https://github.com/contributor/snort-thresholds.git
|
12
|
+
cd snort-thresholds
|
13
|
+
git remote add upstream https://github.com/shadowbq/snort-thresholds.git
|
14
|
+
```
|
15
|
+
|
16
|
+
#### Create a Topic Branch
|
17
|
+
|
18
|
+
All active development is based off of our 'master' branch. So, make sure your references are up to date, and create your topic branch off of upstream/master.
|
19
|
+
|
20
|
+
```
|
21
|
+
git fetch upstream
|
22
|
+
git checkout -b my-feature-branch upstream/master
|
23
|
+
```
|
24
|
+
|
25
|
+
#### Write Tests
|
26
|
+
|
27
|
+
If the area of code that you're working on supports unit tests, add tests.
|
28
|
+
Also, be sure to run all tests to make sure you didn't break anything.
|
29
|
+
|
30
|
+
#### Write Code
|
31
|
+
|
32
|
+
Implement your feature or bug fix.
|
33
|
+
|
34
|
+
#### Write Documentation
|
35
|
+
|
36
|
+
Document any external behavior in the [README](README.md).
|
37
|
+
|
38
|
+
#### Update Changelog
|
39
|
+
|
40
|
+
Add a line to [CHANGELOG](CHANGELOG.md) under *Next Release*. Make it look like every other line, including your name and link to your Github account.
|
41
|
+
|
42
|
+
#### Commit Changes
|
43
|
+
|
44
|
+
Make sure git knows your name and email address:
|
45
|
+
|
46
|
+
```
|
47
|
+
git config --global user.name "Your Name"
|
48
|
+
git config --global user.email "contributor@example.com"
|
49
|
+
```
|
50
|
+
|
51
|
+
Make commits of logical units. This might mean making multiple commits if you're changing distinct bits of functionality.
|
52
|
+
```
|
53
|
+
git add ...
|
54
|
+
git commit
|
55
|
+
```
|
56
|
+
|
57
|
+
It's very important that you write a good commit log! It should describe what changed and why. If you're fixing an issue that is already tracked within Github, mention it!
|
58
|
+
|
59
|
+
````
|
60
|
+
Make a commit title that is brief and to the point
|
61
|
+
|
62
|
+
- Describe the change you made, and why you made it.
|
63
|
+
- Context is vital to a good commit message. Try to get future developers
|
64
|
+
up to speed with what your change does so that they don't bother you
|
65
|
+
offline.
|
66
|
+
- Mention any relevant Snort-thresholds issues by mentioning the number like this #55
|
67
|
+
- Github will automatically close any issues when your pull request is
|
68
|
+
accepted if you do something like this: closes #123
|
69
|
+
|
70
|
+
````
|
71
|
+
|
72
|
+
#### Rebase
|
73
|
+
|
74
|
+
It's easy to for your feature branch to fall behind any changes that have been made in the main repository. This is to be expected on actively developed projects.
|
75
|
+
In order to make sure that your changes will still work when merged into the main repo, it's a good idea to rebase your feature branch. This basically peels the commits off of your feature branch, pulls in any updates that have been made to upstream/master, and then re-applies your commits. If all goes well, your commits will apply cleanly. If, however, someone else has made changes to the same code as you, git will report the conflicts and give you a chance to fix anything.
|
76
|
+
|
77
|
+
Basically, this will help ensure that your code will merge properly with any changes that may have been made in the main repository. If there are any conflicts, it will be easier to deal with those now, rather than finding out that your pull request can't merge.
|
78
|
+
|
79
|
+
```
|
80
|
+
git fetch upstream
|
81
|
+
git rebase upstream/master
|
82
|
+
```
|
83
|
+
|
84
|
+
#### Push
|
85
|
+
|
86
|
+
```
|
87
|
+
git push origin my-feature-branch
|
88
|
+
```
|
89
|
+
|
90
|
+
#### Make a Pull Request
|
91
|
+
|
92
|
+
Go to https://github.com/contributor/snort-thresholds and select your feature branch. Click the 'Pull Request' button and fill out the form. Pull requests are usually reviewed within a few days.
|
93
|
+
|
94
|
+
#### Update CHANGELOG Again
|
95
|
+
|
96
|
+
Update the [CHANGELOG](CHANGELOG.md) with the pull request number. A typical entry looks as follows.
|
97
|
+
|
98
|
+
```
|
99
|
+
* [#123](https://github.com/shadowbq/snort-thresholds/pull/123): Reticulated splines - [@contributor](https://github.com/contributor).
|
100
|
+
```
|
101
|
+
|
102
|
+
Amend your previous commit and force push the changes.
|
103
|
+
|
104
|
+
```
|
105
|
+
git commit --amend
|
106
|
+
git push origin my-feature-branch -f
|
107
|
+
```
|
108
|
+
|
109
|
+
#### Check on Your Pull Request
|
110
|
+
|
111
|
+
Go back to your pull request after a few minutes and see whether it passed muster with Travis-CI. Everything should look green, otherwise fix issues and amend your commit as described above.
|
112
|
+
|
113
|
+
#### Be Patient
|
114
|
+
|
115
|
+
It's likely that your change will not be merged and that the nitpicky maintainers will ask you to do more, or fix seemingly benign problems. Hang on there!
|
116
|
+
|
117
|
+
#### Thank You
|
118
|
+
|
119
|
+
Please do know that we really appreciate and value your time and work. We love you, really.
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 shadowbq
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# snort-thresholds
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/shadowbq/snort-thresholds.svg?branch=master)](https://travis-ci.org/shadowbq/snort-thresholds) [![Code Climate](https://codeclimate.com/github/shadowbq/snort-thresholds/badges/gpa.svg)](https://codeclimate.com/github/shadowbq/snort-thresholds) [![Test Coverage](https://codeclimate.com/github/shadowbq/snort-thresholds/badges/coverage.svg)](https://codeclimate.com/github/shadowbq/snort-thresholds)
|
4
|
+
|
5
|
+
Work in progress
|
6
|
+
|
7
|
+
https://github.com/jasonish/snort/blob/master/doc/README.filters
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Threshold
|
2
|
+
|
3
|
+
#Returns an Array of Grok Captures from the input file matching Threshold Conf standards
|
4
|
+
class Builder
|
5
|
+
def initialize(parsed_data)
|
6
|
+
@parsed_data = parsed_data
|
7
|
+
@parsed_data.reject! {|k,v| v.compact.first == nil }
|
8
|
+
end
|
9
|
+
|
10
|
+
def build
|
11
|
+
#Strip out NIL Stuctures
|
12
|
+
if @parsed_data.key?("SUPPRESSION")
|
13
|
+
return Threshold::Suppression.new(@parsed_data)
|
14
|
+
elsif @parsed_data.key?("RATEFILTER")
|
15
|
+
return Threshold::RateFilter.new(@parsed_data)
|
16
|
+
else @parsed_data.key?("EVENTFILTER")
|
17
|
+
return Threshold::EventFilter.new(@parsed_data)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
|
2
|
+
# In Snort 2.8.5, a new command event_filter was added with the following format.
|
3
|
+
# It functions the same as the global threshold command does in Snort 2.8.4 and
|
4
|
+
# earlier, except that it is not permitted within a rule.
|
5
|
+
|
6
|
+
# event_filter \
|
7
|
+
# gen_id <gid>, sig_id <sid>, \
|
8
|
+
# type <limit|threshold|both>, \
|
9
|
+
# track <by_src|by_dst>, \
|
10
|
+
# count <c>, seconds <s>
|
11
|
+
|
12
|
+
# This format supports the following options - all are required:
|
13
|
+
|
14
|
+
# * gen_id, sig_id: specify generator and signature ids of an associated rule. A
|
15
|
+
# sig_id of 0 will apply to all sig_ids for the given gen_id. If both gen_id
|
16
|
+
# and sig_id are zero, the event_filter applies to all events. Only one
|
17
|
+
# event_filter may be defined for a given gen_id, sig_id.
|
18
|
+
|
19
|
+
# * type <limit|threshold|both>: type limit alerts on the 1st m events during the
|
20
|
+
# time interval, then ignores events for the rest of the time interval. Type
|
21
|
+
# threshold alerts every m times we see this event during the time interval.
|
22
|
+
# Type both alerts once per time interval after seeing m occurrences of the
|
23
|
+
# event, then ignores any additional events during the time interval.
|
24
|
+
|
25
|
+
# * track by_src|by_dst: rate is tracked either by source IP address, or
|
26
|
+
# destination IP address. This means count is maintained at either by unique
|
27
|
+
# source IP addresses, or unique destination IP addresses.
|
28
|
+
|
29
|
+
# * count c: number of rule matching in s seconds that will cause event filter
|
30
|
+
# limit to exceed. C must be nonzero value. A count of -1 disables the
|
31
|
+
# event filter and can be used to override the global event_filter.
|
32
|
+
|
33
|
+
# * seconds s: time period over which count is accrued. S must be nonzero value.=end
|
34
|
+
|
35
|
+
# Example - rule event_filter - limit to logging 1 event per 60 seconds:
|
36
|
+
|
37
|
+
# event_filter \
|
38
|
+
# gen_id 1, sig_id 1851, \
|
39
|
+
# type limit, track by_src, \
|
40
|
+
# count 1, seconds 60
|
41
|
+
|
42
|
+
|
43
|
+
# Create a Standard Error Wrapper
|
44
|
+
|
45
|
+
module Threshold
|
46
|
+
|
47
|
+
class InvalidEventFilterObject < StandardError; end
|
48
|
+
|
49
|
+
# Create an Event Filter validator
|
50
|
+
class EventFilterValidator
|
51
|
+
include Veto.validator
|
52
|
+
|
53
|
+
validates :gid, :presence => true, :integer => true
|
54
|
+
validates :sid, :presence => true, :integer => true
|
55
|
+
validates :type, :presence => true, :inclusion => ['limit', 'threshold', 'both']
|
56
|
+
validates :track_by, :presence => true, :inclusion => ['src', 'dst']
|
57
|
+
validates :count, :presence => true, :integer => true
|
58
|
+
validates :seconds, :presence => true, :integer => true
|
59
|
+
validates :comment, :if => :comment_set?, :format => /^\s*?#.*/
|
60
|
+
|
61
|
+
def comment_set?(entity)
|
62
|
+
entity.comment
|
63
|
+
end
|
64
|
+
|
65
|
+
def track_by_set?(entity)
|
66
|
+
entity.track_by
|
67
|
+
end
|
68
|
+
|
69
|
+
def type_set?(type)
|
70
|
+
entity.type
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class EventFilter
|
75
|
+
|
76
|
+
attr_accessor :gid, :sid, :type, :track_by, :count, :seconds, :comment
|
77
|
+
|
78
|
+
include Veto.model(EventFilterValidator.new)
|
79
|
+
include Comparable
|
80
|
+
|
81
|
+
def initialize(line="")
|
82
|
+
transform(line) unless line.empty?
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
if self.valid?
|
87
|
+
if defined?(@comment)
|
88
|
+
"event_filter gen_id #{@gid}, sig_id #{@sid}, type #{@type}, track by_#{@track_by}, count #{@count}, seconds #{@seconds} #{@comment}"
|
89
|
+
else
|
90
|
+
"event_filter gen_id #{@gid}, sig_id #{@sid}, type #{@type}, track by_#{@track_by}, count #{@count}, seconds #{@seconds}"
|
91
|
+
end
|
92
|
+
else
|
93
|
+
raise InvalidEventFilterObject, 'Event Filter did not validate'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
#Comparable
|
98
|
+
def <=>(anOther)
|
99
|
+
#gid <=> anOther.gid
|
100
|
+
c = self.class.to_s <=> anOther.class.to_s
|
101
|
+
if c == 0 then
|
102
|
+
d = self.gid <=> anOther.gid
|
103
|
+
if d == 0 then
|
104
|
+
self.sid <=> anOther.sid
|
105
|
+
else
|
106
|
+
return d
|
107
|
+
end
|
108
|
+
else
|
109
|
+
return c
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def transform(result)
|
116
|
+
begin
|
117
|
+
self.gid = result["GID"].compact.first.to_i
|
118
|
+
self.sid = result["SID"].compact.first.to_i
|
119
|
+
self.type = result["ETYPE"].compact.first
|
120
|
+
self.track_by = result["TRACK"].compact.first.split('_')[1]
|
121
|
+
self.count = result["COUNT"].compact.first.to_i
|
122
|
+
self.seconds = result["SECONDS"].compact.first.to_i
|
123
|
+
if result.key?("COMMENT")
|
124
|
+
self.comment = result["COMMENT"].compact.first
|
125
|
+
end
|
126
|
+
raise InvalidEventFilterObject unless self.valid?
|
127
|
+
rescue
|
128
|
+
raise InvalidEventFilterObject, 'Failed transformation from parser'
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Threshold
|
2
|
+
|
3
|
+
#Returns an Array of Grok Captures from the input file matching Threshold Conf standards
|
4
|
+
class Parser
|
5
|
+
|
6
|
+
attr_reader :caps, :filehash
|
7
|
+
|
8
|
+
def initialize(file)
|
9
|
+
|
10
|
+
@file = file
|
11
|
+
@caps = []
|
12
|
+
|
13
|
+
patterns = {}
|
14
|
+
patterns["SUPPRESSION"] = "^suppress gen_id %{ID:GID}, sig_id %{ID:SID}(%{SUPPRESSIONOPTIONS})?(%{COMMENT})?"
|
15
|
+
patterns["EVENTFILTER"] = "^event_filter gen_id %{ID:GID}, sig_id %{ID:SID}, type %{ETYPE}, track %{TRACK}, count %{COUNT}, seconds %{SECONDS}(%{COMMENT})?"
|
16
|
+
patterns["RATEFILTER"] = "^rate_filter gen_id %{ID:GID}, sig_id %{ID:SID}, track %{TRACK}, count %{COUNT}, seconds %{SECONDS}, new_action %{NEW_ACTION}, timeout %{COUNT:TIMEOUT}(%{RATEFILTEROPTIONS})?(%{COMMENT})?"
|
17
|
+
|
18
|
+
patterns["SUPPRESSIONOPTIONS"] = ", track %{TRACK}, ip %{IP}"
|
19
|
+
patterns["RATEFILTEROPTIONS"] = ", apply_to %{IPCIDR}"
|
20
|
+
|
21
|
+
patterns["ID"] = '\\d+'
|
22
|
+
patterns["ETYPE"] = "limit|threshold|both"
|
23
|
+
patterns["COUNT"] = "\\d+"
|
24
|
+
patterns["SECONDS"] = "\\d+"
|
25
|
+
patterns["TRACK"] = "by_src|by_dst|by_rule"
|
26
|
+
patterns["COMMENT"] = "\s*?#.*"
|
27
|
+
patterns["NEW_ACTION"] = 'alert|drop|pass|log|sdrop|reject'
|
28
|
+
patterns["IPCIDR"] = '(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([1-9]|[1-2][0-9]|3[0-2]))?'
|
29
|
+
|
30
|
+
@grok = Grok.new
|
31
|
+
patterns.each {|k,v| @grok.add_pattern(k,v)}
|
32
|
+
custom_path = File.join(File.dirname(File.expand_path(__FILE__)), "patterns/base")
|
33
|
+
@grok.add_patterns_from_file(custom_path)
|
34
|
+
|
35
|
+
# Remember to call result["GID"].compact because of the PIPE or below in grok compile
|
36
|
+
@grok.compile("^%{SUPPRESSION}|%{EVENTFILTER}|%{RATEFILTER}")
|
37
|
+
|
38
|
+
loadfile(@file)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def loadfile(file)
|
44
|
+
# FLOCK this and hash it..
|
45
|
+
handler = File.open(file)
|
46
|
+
handler.flock(File::LOCK_EX)
|
47
|
+
handler.each do |line|
|
48
|
+
match = @grok.match(line)
|
49
|
+
@caps << match.captures if match
|
50
|
+
end
|
51
|
+
hash = Digest::MD5.file file
|
52
|
+
handler.close
|
53
|
+
@filehash = hash
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,91 @@
|
|
1
|
+
USERNAME [a-zA-Z0-9_-]+
|
2
|
+
USER %{USERNAME}
|
3
|
+
INT (?:[+-]?(?:[0-9]+))
|
4
|
+
BASE10NUM (?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\.[0-9]+)?)|(?:\.[0-9]+)))
|
5
|
+
NUMBER (?:%{BASE10NUM})
|
6
|
+
BASE16NUM (?<![0-9A-Fa-f])(?:[+-]?(?:0x)?(?:[0-9A-Fa-f]+))
|
7
|
+
BASE16FLOAT \b(?<![0-9A-Fa-f.])(?:[+-]?(?:0x)?(?:(?:[0-9A-Fa-f]+(?:\.[0-9A-Fa-f]*)?)|(?:\.[0-9A-Fa-f]+)))\b
|
8
|
+
|
9
|
+
POSINT \b(?:[0-9]+)\b
|
10
|
+
WORD \b\w+\b
|
11
|
+
NOTSPACE \S+
|
12
|
+
DATA .*?
|
13
|
+
GREEDYDATA .*
|
14
|
+
#QUOTEDSTRING (?:(?<!\\)(?:"(?:\\.|[^\\"])*"|(?:'(?:\\.|[^\\'])*')|(?:`(?:\\.|[^\\`])*`)))
|
15
|
+
QUOTEDSTRING (?:(?<!\\)(?:"(?>[^\\"]+|\\.)*")|(?:'(?>[^\\']+|\\.)*')|(?:`(?>[^\\`]+|\\.)*`))
|
16
|
+
|
17
|
+
# Networking
|
18
|
+
MAC (?:%{CISCOMAC}|%{WINDOWSMAC}|%{COMMONMAC})
|
19
|
+
CISCOMAC (?:(?:[A-Fa-f0-9]{4}\.){2}[A-Fa-f0-9]{4})
|
20
|
+
WINDOWSMAC (?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2})
|
21
|
+
COMMONMAC (?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2})
|
22
|
+
IP (?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])
|
23
|
+
HOSTNAME \b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\.?|\b)
|
24
|
+
HOST %{HOSTNAME}
|
25
|
+
IPORHOST (?:%{HOSTNAME}|%{IP})
|
26
|
+
HOSTPORT (?:%{IPORHOST=~/\./}:%{POSINT})
|
27
|
+
|
28
|
+
# paths
|
29
|
+
PATH (?:%{UNIXPATH}|%{WINPATH})
|
30
|
+
UNIXPATH (?:/(?:[\w_%!$@:.,-]+|\\.)*)+
|
31
|
+
#UNIXPATH (?<![\w\/])(?:/[^\/\s?*]*)+
|
32
|
+
LINUXTTY (?:/dev/pts/%{POSINT})
|
33
|
+
BSDTTY (?:/dev/tty[pq][a-z0-9])
|
34
|
+
TTY (?:%{BSDTTY}|%{LINUXTTY})
|
35
|
+
WINPATH (?:[A-Za-z]+:|\\)(?:\\[^\\?*]*)+
|
36
|
+
URIPROTO [A-Za-z]+(\+[A-Za-z+]+)?
|
37
|
+
URIHOST %{IPORHOST}(?::%{POSINT:port})?
|
38
|
+
# uripath comes loosely from RFC1738, but mostly from what Firefox
|
39
|
+
# doesn't turn into %XX
|
40
|
+
URIPATH (?:/[A-Za-z0-9$.+!*'(),~:#%_-]*)+
|
41
|
+
#URIPARAM \?(?:[A-Za-z0-9]+(?:=(?:[^&]*))?(?:&(?:[A-Za-z0-9]+(?:=(?:[^&]*))?)?)*)?
|
42
|
+
URIPARAM \?[A-Za-z0-9$.+!*'(),~#%&/=:;_-]*
|
43
|
+
URIPATHPARAM %{URIPATH}(?:%{URIPARAM})?
|
44
|
+
URI %{URIPROTO}://(?:%{USER}(?::[^@]*)?@)?(?:%{URIHOST})?(?:%{URIPATHPARAM})?
|
45
|
+
|
46
|
+
# Months: January, Feb, 3, 03, 12, December
|
47
|
+
MONTH \b(?:[Jj]an(?:uary)?|[Ff]eb(?:ruary)?|[Mm]ar(?:ch)?|[Aa]pr(?:il)?|[Mm]ay|[Jj]un(?:e)?|[Jj]ul(?:y)?|[Aa]ug(?:ust)?|[Ss]ep(?:tember)?|[Oo]ct(?:ober)?|[Nn]ov(?:ember)?|[Dd]ec(?:ember)?)\b
|
48
|
+
MONTHNUM (?:0?[1-9]|1[0-2])
|
49
|
+
MONTHDAY (?:3[01]|[1-2]?[0-9]|0?[1-9])
|
50
|
+
|
51
|
+
# Days: Monday, Tue, Thu, etc...
|
52
|
+
DAY (?:[Mm]on(?:day)?|[Tt]ue(?:sday)?|[Ww]ed(?:nesday)?|[Tt]hu(?:rsday)?|[Ff]ri(?:day)?|[Ss]at(?:urday)?|[Ss]un(?:day)?)
|
53
|
+
|
54
|
+
# Years?
|
55
|
+
YEAR [0-9]+
|
56
|
+
# Time: HH:MM:SS
|
57
|
+
#TIME \d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?
|
58
|
+
# I'm still on the fence about using grok to perform the time match,
|
59
|
+
# since it's probably slower.
|
60
|
+
# TIME %{POSINT<24}:%{POSINT<60}(?::%{POSINT<60}(?:\.%{POSINT})?)?
|
61
|
+
HOUR (?:2[0123]|[01][0-9])
|
62
|
+
MINUTE (?:[0-5][0-9])
|
63
|
+
# '60' is a leap second in most time standards and thus is valid.
|
64
|
+
SECOND (?:(?:[0-5][0-9]|60)(?:[.,][0-9]+)?)
|
65
|
+
TIME (?<![0-9])%{HOUR}:%{MINUTE}(?::%{SECOND})(?![0-9])
|
66
|
+
# datestamp is YYYY/MM/DD-HH:MM:SS.UUUU (or something like it)
|
67
|
+
DATE_US %{MONTHNUM}[/-]%{MONTHDAY}[/-]%{YEAR}
|
68
|
+
DATE_EU %{YEAR}[/-]%{MONTHNUM}[/-]%{MONTHDAY}
|
69
|
+
ISO8601_TIMEZONE (?:Z|[+-]%{HOUR}(?::?%{MINUTE}))
|
70
|
+
ISO8601_SECOND (?:%{SECOND}|60)
|
71
|
+
TIMESTAMP_ISO8601 %{YEAR}-%{MONTHNUM}-%{MONTHDAY}[T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})?%{ISO8601_TIMEZONE}?
|
72
|
+
DATE %{DATE_US}|%{DATE_EU}
|
73
|
+
DATESTAMP %{DATE}[- ]%{TIME}
|
74
|
+
TZ (?:[PMCE][SD]T)
|
75
|
+
DATESTAMP_RFC822 %{DAY} %{MONTH} %{MONTHDAY} %{YEAR} %{TIME} %{TZ}
|
76
|
+
DATESTAMP_OTHER %{DAY} %{MONTH} %{MONTHDAY} %{TIME} %{TZ} %{YEAR}
|
77
|
+
|
78
|
+
# Syslog Dates: Month Day HH:MM:SS
|
79
|
+
SYSLOGTIMESTAMP %{MONTH} +%{MONTHDAY} %{TIME}
|
80
|
+
PROG (?:[\w._/-]+)
|
81
|
+
SYSLOGPROG %{PROG:program}(?:\[%{POSINT:pid}\])?
|
82
|
+
SYSLOGHOST %{IPORHOST}
|
83
|
+
SYSLOGFACILITY <%{POSINT:facility}.%{POSINT:priority}>
|
84
|
+
HTTPDATE %{MONTHDAY}/%{MONTH}/%{YEAR}:%{TIME} %{INT:ZONE}
|
85
|
+
|
86
|
+
# Shortcuts
|
87
|
+
QS %{QUOTEDSTRING}
|
88
|
+
|
89
|
+
# Log formats
|
90
|
+
SYSLOGBASE %{SYSLOGTIMESTAMP:timestamp} (?:%{SYSLOGFACILITY} )?%{SYSLOGHOST:logsource} %{SYSLOGPROG}:
|
91
|
+
COMBINEDAPACHELOG %{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] "%{WORD:verb} %{URIPATHPARAM:request} HTTP/%{NUMBER:httpversion}" %{NUMBER:response} (?:%{NUMBER:bytes}|-) "(?:%{URI:referrer}|-)" %{QS:agent}
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# rate_filter provides rate based attack prevention by allowing users to
|
2
|
+
# configure a new action to take for a specified time when a given rate is
|
3
|
+
# exceeded. Multiple rate filters can be defined on the same rule, in which case
|
4
|
+
# they are evaluated in the order they appear in the configuration file, and the
|
5
|
+
# first applicable action is taken.
|
6
|
+
|
7
|
+
# Rate filters are used as standalone commands (outside any rule) and have the
|
8
|
+
# following format:
|
9
|
+
|
10
|
+
# rate_filter \
|
11
|
+
# gen_id <gid>, sig_id <sid>, \
|
12
|
+
# track <by_src|by_dst|by_rule>, \
|
13
|
+
# count <c>, seconds <s>, \
|
14
|
+
# new_action alert|drop|pass|log|sdrop|reject, \
|
15
|
+
# timeout <seconds>, \
|
16
|
+
# apply_to <ip-list>
|
17
|
+
|
18
|
+
# This format has the following options - all are required except apply_to, which
|
19
|
+
# is optional:
|
20
|
+
|
21
|
+
# * Track by_src|by_dst|by_rule: rate is tracked either by source IP address,
|
22
|
+
# destination IP address, or by rule. This means the match statistics are
|
23
|
+
# maintained for each unique source IP address, for each unique destination IP
|
24
|
+
# address, or they are aggregated at rule level. For rules related to Stream5
|
25
|
+
# sessions, source and destination means client and server respectively. track
|
26
|
+
# by_rule and apply_to may not be used together.
|
27
|
+
|
28
|
+
# * Count c: the maximum number of rule matches in s seconds before the rate
|
29
|
+
# filter limit to is exceeded. C must be positive.
|
30
|
+
|
31
|
+
# * seconds s: the time period over which count is accrued. 0 seconds means
|
32
|
+
# count is a total count instead of a specific rate. For example, rate filter
|
33
|
+
# may be used to detect if the number of connections to a specific server
|
34
|
+
# exceed a specific count. 0 seconds only applies to internal rules (gen_id 135)
|
35
|
+
# and other use will produce a fatal error by Snort.
|
36
|
+
|
37
|
+
# * new_action <alert|drop|pass|log|sdrop|reject>: new_action replaces rule
|
38
|
+
# action with alert|drop| pass|log|sdrop for r (timeout) seconds. Drop, reject
|
39
|
+
# and sdrop can be used only when snort is used in inline mode. sdrop and
|
40
|
+
# reject are conditionally compiled with #ifdef GIDS.
|
41
|
+
|
42
|
+
# * timeout r: revert to the original rule action after r seconds. If r is 0,
|
43
|
+
# then rule action is never reverted back. Event filter may be used to manage
|
44
|
+
# number of alerts after the rule action is enabled by rate filter.
|
45
|
+
|
46
|
+
# * apply_to <ip-list>: restrict the configuration to only to source or
|
47
|
+
# destination IP address (indicated by track parameter) determined by
|
48
|
+
# <ip-list>. track by_rule and apply_to may not be used together. Note that
|
49
|
+
# events are generated during the timeout period, even if the rate falls below
|
50
|
+
# the configured limit.
|
51
|
+
|
52
|
+
|
53
|
+
# Example - allow a maximum of 100 connection attempts per second from any one
|
54
|
+
# IP address, and block further connection attempts from that IP address for 10
|
55
|
+
# seconds:
|
56
|
+
|
57
|
+
# rate_filter \
|
58
|
+
# gen_id 135, sig_id 1, \
|
59
|
+
# track by_src, \
|
60
|
+
# count 100, seconds 1, \
|
61
|
+
# new_action drop, timeout 10
|
62
|
+
|
63
|
+
|
64
|
+
# Create a Standard Error Wrapper
|
65
|
+
|
66
|
+
module Threshold
|
67
|
+
|
68
|
+
class InvalidRateFilterObject < StandardError; end
|
69
|
+
|
70
|
+
# Create a Rate Filter validator
|
71
|
+
class RateFilterValidator
|
72
|
+
include Veto.validator
|
73
|
+
|
74
|
+
validates :gid, :presence => true, :integer => true
|
75
|
+
validates :sid, :presence => true, :integer => true
|
76
|
+
validates :track_by, :presence =>true, :inclusion => ['src', 'dst', 'rule']
|
77
|
+
validates :count, :presence => true, :integer => true
|
78
|
+
validates :seconds, :presence => true, :integer => true
|
79
|
+
validates :new_action, :presence => true, :inclusion => ['alert', 'drop', 'pass', 'log', 'sdrop', 'reject']
|
80
|
+
validates :timeout, :presence => true, :integer => true
|
81
|
+
validates :apply_to, :if => :not_track_by_rule?, :format => /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([1-9]|[1-2][0-9]|3[0-2]))?$/
|
82
|
+
validates :comment, :if => :comment_set?, :format => /^\s*#.*/
|
83
|
+
|
84
|
+
def comment_set?(entity)
|
85
|
+
entity.comment
|
86
|
+
end
|
87
|
+
|
88
|
+
def not_track_by_rule?(entity)
|
89
|
+
if entity.apply_to == nil
|
90
|
+
entity.track_by == false
|
91
|
+
else
|
92
|
+
entity.track_by != 'rule'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
class RateFilter
|
99
|
+
|
100
|
+
attr_accessor :gid, :sid, :track_by, :count, :seconds, :new_action, :timeout, :apply_to, :comment
|
101
|
+
|
102
|
+
include Veto.model(RateFilterValidator.new)
|
103
|
+
include Comparable
|
104
|
+
|
105
|
+
def initialize(line="")
|
106
|
+
transform(line) unless line.empty?
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_s
|
110
|
+
if self.valid?
|
111
|
+
if apply_to == nil then
|
112
|
+
if defined?(@comment)
|
113
|
+
"rate_filter gen_id #{@gid}, sig_id #{@sid}, track by_#{@track_by}, count #{@count}, seconds #{@seconds}, new_action #{@new_action}, timeout #{@timeout} #{@comment}"
|
114
|
+
else
|
115
|
+
"rate_filter gen_id #{@gid}, sig_id #{@sid}, track by_#{@track_by}, count #{@count}, seconds #{@seconds}, new_action #{@new_action}, timeout #{@timeout}"
|
116
|
+
end
|
117
|
+
else
|
118
|
+
if defined?(@comment)
|
119
|
+
"rate_filter gen_id #{@gid}, sig_id #{@sid}, count #{@count}, seconds #{@seconds}, new_action #{@new_action}, timeout #{@timeout} apply_to #{@apply_to} #{@comment}"
|
120
|
+
else
|
121
|
+
"rate_filter gen_id #{@gid}, sig_id #{@sid}, count #{@count}, seconds #{@seconds}, new_action #{@new_action}, timeout #{@timeout} apply_to #{@apply_to}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
else
|
125
|
+
raise InvalidRateFilterObject, 'Rate Filter did not validate'
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#Comparable
|
130
|
+
def <=>(anOther)
|
131
|
+
#gid <=> anOther.gid
|
132
|
+
c = self.class.to_s <=> anOther.class.to_s
|
133
|
+
if c == 0 then
|
134
|
+
d = self.gid <=> anOther.gid
|
135
|
+
if d == 0 then
|
136
|
+
self.sid <=> anOther.sid
|
137
|
+
else
|
138
|
+
return d
|
139
|
+
end
|
140
|
+
else
|
141
|
+
return c
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def transform(result)
|
148
|
+
begin
|
149
|
+
self.gid = result["GID"].compact.first.to_i
|
150
|
+
self.sid = result["SID"].compact.first.to_i
|
151
|
+
self.track_by = result["TRACK"].compact.first.split('_')[1]
|
152
|
+
|
153
|
+
self.count = result["COUNT"].compact.first.to_i
|
154
|
+
self.seconds = result["SECONDS"].compact.first.to_i
|
155
|
+
self.timeout = result["TIMEOUT"].compact.first.to_i
|
156
|
+
self.new_action = result["NEW_ACTION"].compact.first
|
157
|
+
|
158
|
+
if result.key("IPCIDR")
|
159
|
+
self.apply_to = result["IPCIDR"].compact.first
|
160
|
+
end
|
161
|
+
if result.key?("COMMENT")
|
162
|
+
self.comment = result["COMMENT"].compact.first
|
163
|
+
end
|
164
|
+
raise InvalidRateFilterObject unless self.valid?
|
165
|
+
rescue
|
166
|
+
raise InvalidRateFilterObject, 'Failed transformation from parser'
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
|
2
|
+
# suppress \
|
3
|
+
# gen_id <gid>, sig_id <sid>
|
4
|
+
#
|
5
|
+
# suppress \
|
6
|
+
# gen_id <gid>, sig_id <sid>, \
|
7
|
+
# track by_src|by_dst, \
|
8
|
+
# ip <ip-list>
|
9
|
+
# Note: ip can be 1.2.3.4 or 1.2.4.5/32
|
10
|
+
|
11
|
+
# Create a Standard Error Wrapper
|
12
|
+
|
13
|
+
module Threshold
|
14
|
+
|
15
|
+
class InvalidSuppressionObject < StandardError; end
|
16
|
+
|
17
|
+
# Create a Suppression validator
|
18
|
+
class SuppressionValidator
|
19
|
+
include Veto.validator
|
20
|
+
|
21
|
+
validates :comment, :if => :comment_set?, :format => /^\s*?#.*/
|
22
|
+
|
23
|
+
validates :gid, :presence => true, :integer => true
|
24
|
+
validates :sid, :presence => true, :integer => true
|
25
|
+
|
26
|
+
validates :track_by, :presence => true, :if => :ip_set?, :inclusion => ['src', 'dst']
|
27
|
+
validates :ip, :presence => true, :if => :track_by_set?, :format => /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([1-9]|[1-2][0-9]|3[0-2]))?$/
|
28
|
+
|
29
|
+
def comment_set?(entity)
|
30
|
+
entity.comment
|
31
|
+
end
|
32
|
+
|
33
|
+
def track_by_set?(entity)
|
34
|
+
entity.track_by
|
35
|
+
end
|
36
|
+
|
37
|
+
def ip_set?(entity)
|
38
|
+
entity.ip
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Suppression
|
43
|
+
|
44
|
+
attr_accessor :gid, :sid, :track_by, :ip, :comment
|
45
|
+
|
46
|
+
include Veto.model(SuppressionValidator.new)
|
47
|
+
include Comparable
|
48
|
+
|
49
|
+
def initialize(line="")
|
50
|
+
transform(line) unless line.empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
#Comparable
|
54
|
+
def <=>(anOther)
|
55
|
+
#gid <=> anOther.gid
|
56
|
+
c = self.class.to_s <=> anOther.class.to_s
|
57
|
+
if c == 0 then
|
58
|
+
d = self.gid <=> anOther.gid
|
59
|
+
if d == 0 then
|
60
|
+
self.sid <=> anOther.sid
|
61
|
+
else
|
62
|
+
return d
|
63
|
+
end
|
64
|
+
else
|
65
|
+
return c
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_s
|
70
|
+
if self.valid?
|
71
|
+
if track_by == nil then
|
72
|
+
if defined?(@comment)
|
73
|
+
"suppress gen_id #{@gid}, sig_id #{@sid} #{@comment}"
|
74
|
+
else
|
75
|
+
"suppress gen_id #{@gid}, sig_id #{@sid}"
|
76
|
+
end
|
77
|
+
else
|
78
|
+
if defined?(@comment)
|
79
|
+
"suppress gen_id #{@gid}, sig_id #{@sid}, track by_#{@track_by}, ip #{@ip} #{@comment}"
|
80
|
+
else
|
81
|
+
"suppress gen_id #{@gid}, sig_id #{@sid}, track by_#{@track_by}, ip #{@ip}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
else
|
85
|
+
raise InvalidSuppressionObject, 'Suppression did not validate'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def transform(result)
|
92
|
+
begin
|
93
|
+
self.gid = result["GID"].compact.first.to_i
|
94
|
+
self.sid = result["SID"].compact.first.to_i
|
95
|
+
if result.key?("TRACK")
|
96
|
+
self.track_by = result["TRACK"].compact.first.split('_')[1]
|
97
|
+
end
|
98
|
+
if result.key?("IP")
|
99
|
+
self.ip = result["IP"].compact.first
|
100
|
+
end
|
101
|
+
if result.key?("COMMENT")
|
102
|
+
self.comment = result["COMMENT"].compact.first
|
103
|
+
end
|
104
|
+
raise InvalidSuppressionObject unless self.valid?
|
105
|
+
rescue
|
106
|
+
raise InvalidSuppressionObject, 'Failed transformation from parser'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Threshold
|
2
|
+
|
3
|
+
class InvalidThresholdsObject < StandardError; end
|
4
|
+
class ReadOnlyThresholdFile < StandardError; end
|
5
|
+
class NonExistantThresholdFile < StandardError; end
|
6
|
+
class MissingThresholdFileConfiguration < StandardError; end
|
7
|
+
class ThresholdAtomicLockFailure < StandardError; end
|
8
|
+
|
9
|
+
class Thresholds < Array
|
10
|
+
|
11
|
+
attr_accessor :file, :readonly
|
12
|
+
|
13
|
+
# Write changes to the file
|
14
|
+
def flush
|
15
|
+
begin
|
16
|
+
valid_existing_file?(@file)
|
17
|
+
raise ReadOnlyThresholdsFile if @readonly
|
18
|
+
hash = current_hash
|
19
|
+
|
20
|
+
file = File.open(@file, 'w+')
|
21
|
+
raise ThresholdAtomicLockFailure, 'The @file state/hash changed before we could flush the file' unless stored_hash == hash
|
22
|
+
file.write self.sort.to_s
|
23
|
+
file.close
|
24
|
+
|
25
|
+
rescue NonExistantThresholdFile
|
26
|
+
raise ReadOnlyThresholdsFile if @readonly
|
27
|
+
|
28
|
+
file = File.open(@file, 'w')
|
29
|
+
file.write self.sort.to_s
|
30
|
+
file.close
|
31
|
+
end
|
32
|
+
return true
|
33
|
+
end
|
34
|
+
|
35
|
+
# Clears current collection and Read in the thresholds.conf file
|
36
|
+
def loadfile!
|
37
|
+
self.clear
|
38
|
+
loadfile
|
39
|
+
end
|
40
|
+
|
41
|
+
# Append in the thresholds.conf file to current collection
|
42
|
+
def loadfile
|
43
|
+
valid_existing_file?(@file)
|
44
|
+
|
45
|
+
results = Threshold::Parser.new(@file)
|
46
|
+
@stored_hash= results.filehash
|
47
|
+
#puts stored_hash
|
48
|
+
results.caps.each do |result|
|
49
|
+
builder = Threshold::Builder.new(result)
|
50
|
+
self << builder.build
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def valid?
|
56
|
+
begin
|
57
|
+
self.each do |threshold|
|
58
|
+
if threshold.respond_to?(:valid?)
|
59
|
+
return false unless threshold.valid?
|
60
|
+
else
|
61
|
+
raise InvalidThresholdsObject, "Container object has unknown objects"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
return true
|
65
|
+
rescue InvalidThresholdsObject
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# This should transpose? back to a Thresholds class not return as an Array. (super)
|
71
|
+
def sort
|
72
|
+
raise InvalidThresholdsObject unless valid?
|
73
|
+
new_temp = super
|
74
|
+
temp = Thresholds.new
|
75
|
+
new_temp.each {|item| temp << item}
|
76
|
+
return temp
|
77
|
+
end
|
78
|
+
|
79
|
+
def sort!
|
80
|
+
raise InvalidThresholdsObject unless valid?
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_s
|
85
|
+
output = ""
|
86
|
+
|
87
|
+
raise InvalidThresholdsObject, "Container object has unknown objects" unless valid?
|
88
|
+
|
89
|
+
self.each do |threshold|
|
90
|
+
output << threshold.to_s + "\n"
|
91
|
+
end
|
92
|
+
return output
|
93
|
+
end
|
94
|
+
|
95
|
+
def stored_hash
|
96
|
+
@stored_hash
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def stored_hash=(foo)
|
102
|
+
@stored_hash=foo
|
103
|
+
end
|
104
|
+
|
105
|
+
def current_hash
|
106
|
+
file = File.open(@file, 'rb+')
|
107
|
+
file.flock(File::LOCK_EX)
|
108
|
+
hash = Digest::MD5.file @file
|
109
|
+
file.close
|
110
|
+
return hash
|
111
|
+
end
|
112
|
+
|
113
|
+
def valid_existing_file?(file)
|
114
|
+
if file !=nil
|
115
|
+
raise NonExistantThresholdFile, "Missing threshold.conf" unless (File.file?(file) and File.exists?(file))
|
116
|
+
else
|
117
|
+
raise MissingThresholdFileConfiguration, "Missing threshold.conf path. See README for Usage."
|
118
|
+
end
|
119
|
+
return true
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
data/lib/threshold.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'forwardable' # Needed by veto
|
3
|
+
require 'veto'
|
4
|
+
require 'grok-pure'
|
5
|
+
require 'digest'
|
6
|
+
|
7
|
+
module Threshold
|
8
|
+
$:.unshift(File.dirname(__FILE__))
|
9
|
+
|
10
|
+
#require library
|
11
|
+
require 'threshold/suppression'
|
12
|
+
require 'threshold/event_filter'
|
13
|
+
require 'threshold/thresholds'
|
14
|
+
require 'threshold/rate_filter'
|
15
|
+
require 'threshold/builder'
|
16
|
+
require 'threshold/parser'
|
17
|
+
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: threshold
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Shadowbq
|
8
|
+
- Yabbo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-01-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: veto
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: jls-grok
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.11.0
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 0.11.0
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: fivemat
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rspec
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '3.1'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '3.1'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: simplecov
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: codeclimate-test-reporter
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
description: An ORM to map to Snort 2.9.x threshold.conf files
|
113
|
+
email: shadowbq@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- CHANGELOG.md
|
119
|
+
- CONTRIBUTING.md
|
120
|
+
- LICENSE
|
121
|
+
- README.md
|
122
|
+
- Rakefile
|
123
|
+
- lib/threshold.rb
|
124
|
+
- lib/threshold/builder.rb
|
125
|
+
- lib/threshold/event_filter.rb
|
126
|
+
- lib/threshold/parser.rb
|
127
|
+
- lib/threshold/patterns/base
|
128
|
+
- lib/threshold/patterns/java
|
129
|
+
- lib/threshold/patterns/ruby
|
130
|
+
- lib/threshold/rate_filter.rb
|
131
|
+
- lib/threshold/suppression.rb
|
132
|
+
- lib/threshold/thresholds.rb
|
133
|
+
- lib/threshold/version.rb
|
134
|
+
homepage: http://github.com/shadowbq/snort-thresholds
|
135
|
+
licenses:
|
136
|
+
- MIT
|
137
|
+
metadata: {}
|
138
|
+
post_install_message:
|
139
|
+
rdoc_options:
|
140
|
+
- "--charset=UTF-8"
|
141
|
+
require_paths:
|
142
|
+
- lib
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: 1.9.3
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
requirements: []
|
154
|
+
rubyforge_project:
|
155
|
+
rubygems_version: 2.2.2
|
156
|
+
signing_key:
|
157
|
+
specification_version: 4
|
158
|
+
summary: An ORM to map to Snort 2.9.x threshold.conf files
|
159
|
+
test_files: []
|