rodsec 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3477d1c971df8c9705a43c33d749da2bcf32957b
4
+ data.tar.gz: 7a4533d4fbd215efaab0bad3c880c13765dcd77a
5
+ SHA512:
6
+ metadata.gz: eaa19ddd357c6b7689c4a73e33fe7293f31f4a054f1459784f14b5dcce1a8ef5fb1f9afdcb5cbab0fdb4868e1f85a08d724a1f52c0dcce133ac414d3c253df83
7
+ data.tar.gz: 9fe83d648ab87829346874ac39dd37a2d1b9a795e152e5b35914af6e05e6eb31a49a0a3c223461ed22a38f4b2b3fff9bcc2bb64f93b5289a8447c21447874de7
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
13
+
14
+ # local c extensions
15
+ lib/rodsec/*.so
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rvmrc ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
7
+ # Only full ruby name is supported here, for short names use:
8
+ # echo "rvm use 2.5.1@rodsec" > .rvmrc
9
+ environment_id="ruby-2.3.6@rodsec"
10
+
11
+ # Uncomment the following lines if you want to verify rvm version per project
12
+ # rvmrc_rvm_version="1.29.4 (latest)" # 1.10.1 seems like a safe start
13
+ # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | __rvm_awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
14
+ # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
15
+ # return 1
16
+ # }
17
+
18
+ # First we attempt to load the desired environment directly from the environment
19
+ # file. This is very fast and efficient compared to running through the entire
20
+ # CLI and selector. If you want feedback on which environment was used then
21
+ # insert the word 'use' after --create as this triggers verbose mode.
22
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
23
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
24
+ then
25
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
26
+ for __hook in "${rvm_path:-$HOME/.rvm}/hooks/after_use"*
27
+ do
28
+ if [[ -f "${__hook}" && -x "${__hook}" && -s "${__hook}" ]]
29
+ then \. "${__hook}" || true
30
+ fi
31
+ done
32
+ unset __hook
33
+ if (( ${rvm_use_flag:=1} >= 2 )) # display only when forced
34
+ then
35
+ if [[ $- == *i* ]] # check for interactive shells
36
+ then printf "%b" "Using: $(tput setaf 2 2>/dev/null)$GEM_HOME$(tput sgr0 2>/dev/null)\n" # show the user the ruby and gemset they are using in green
37
+ else printf "%b" "Using: $GEM_HOME\n" # don't use colors in non-interactive shells
38
+ fi
39
+ fi
40
+ else
41
+ # If the environment file has not yet been created, use the RVM CLI to select.
42
+ rvm --create "$environment_id" || {
43
+ echo "Failed to create RVM environment '${environment_id}'."
44
+ return 1
45
+ }
46
+ fi
47
+
48
+ # If you use bundler, this might be useful to you:
49
+ # if [[ -s Gemfile ]] && {
50
+ # ! builtin command -v bundle >/dev/null ||
51
+ # builtin command -v bundle | GREP_OPTIONS="" \command \grep $rvm_path/bin/bundle >/dev/null
52
+ # }
53
+ # then
54
+ # printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
55
+ # gem install bundler
56
+ # fi
57
+ # if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
58
+ # then
59
+ # bundle install | GREP_OPTIONS="" \command \grep -vE '^Using|Your bundle is complete'
60
+ # fi
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.15.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rodsec.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 John Anderson
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # Rodsec
2
+
3
+ An ffi wrapper for [ModSecurity](https://www.modsecurity.org/) Web Application
4
+ Firewall. It will need a ruleset, most likely you'll want to use
5
+ [OWASP ModSecurity Core Rule Set (CRS)](https://coreruleset.org/).
6
+
7
+ This gem also provides a Rack middleware which can return a 403 Forbidden
8
+ response to bad requests, in many cases before your application code runs.
9
+
10
+ ## Installation
11
+
12
+ Install [ModSecurity >= 3.0.0](https://www.modsecurity.org/download.html). This
13
+ gem's native extensions will not compile without it. As of 23-Sep-2018, you may
14
+ have to compile ModSecurity yourself, seems that distro packages of 3.0.0
15
+ versions are not available.
16
+
17
+ And now back to your scheduled gem installation dance. Add this line to your
18
+ application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'rodsec'
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install rodsec
31
+
32
+
33
+ ## Usage
34
+
35
+ ### ModSecurity config
36
+
37
+ Copy ```spec/config/modsecurity.conf``` and ```spec/config/crs-setup.conf``` into a config directory
38
+ in your app somewhere. These are pre-configured to signal an intervention on dodgy requests or
39
+ responses - the rack middleware in this gem returns a 403 "Forbidden" in those cases.
40
+
41
+ You should be able to use the config files as-is. Possibly decrease the paranoia
42
+ level in ```crs-setup.conf``` from 3 to 1 or 2.
43
+
44
+ Then you'll need a ruleset - start with the
45
+ [OWASP CRS](https://github.com/SpiderLabs/owasp-modsecurity-crs/).
46
+
47
+ Easiest is a directory structure like this:
48
+
49
+ ```
50
+ config/
51
+ modsecurity.conf
52
+ crs-setup.conf
53
+ rules/
54
+ # copy files from OWASP CRS rules/*
55
+ REQUEST-920-PROTOCOL-ENFORCEMENT.conf
56
+ ...
57
+ RESPONSE-980-CORRELATION.conf
58
+ ...
59
+ scanners-headers.data
60
+ ...
61
+ ```
62
+
63
+ The location of your ```rules``` directory is configurable if you
64
+ really need to - see comments in ```Rodsec::Rack``` source.
65
+
66
+ Take a look at the ```*.example``` files in ```rules/```.
67
+
68
+ Copying the rules files is a manual step because you really want to have at
69
+ least some idea of what rules you've activated, and how to handle false
70
+ positives. Search for ModSecurity and apache or nginx and you'll get lots to
71
+ read.
72
+
73
+ ### Rack/Rails
74
+
75
+ Now you can add a ```use``` line to your rack config. In plain rack this would
76
+ be something like
77
+
78
+ ``` ruby
79
+ use Rodsec::Rack, config: config_dir, log_blk: -> tag, str { p tag: tag, str: str }
80
+ ```
81
+
82
+ See
83
+ [official Rails docs](https://guides.rubyonrails.org/rails_on_rack.html#configuring-middleware-stack)
84
+ on adding rack middleware to rails.
85
+
86
+ You'll know it worked when you see "loading rules file" log messages showing up
87
+ in your ```log_blk:``` lambda on application startup.
88
+
89
+ ### Standalone
90
+
91
+ You can also use this gem without rack.
92
+
93
+ ``` ruby
94
+ msc = Rodsec::Modsec.new do |tag, str|
95
+ # this block will be called with log strings from ModSecurity
96
+ puts tag, str
97
+ end
98
+
99
+ # load config files
100
+ rule_set = Rodsec::ReadConfig.read_config config_dir, rules_dir do |tag, str|
101
+ p tag => str
102
+ end
103
+
104
+ # Now check one, or several, request/response cycles.
105
+ # You'll need a new Transaction instance for each cycle.
106
+ txn = Rodsec::Transaction.new msc, rule_set, txn_log_tag: 'my_first_transaction'
107
+ begin
108
+ # method calls MUST be in this order
109
+ txn.connection! ...
110
+ txn.uri! ...
111
+ txn.request_headers! ...
112
+ txn.request_body! ...
113
+ txn.response_headers! ...
114
+ txn.response_body! ...
115
+
116
+ txn.logging
117
+ rescue Rodsec::Intervention => iex
118
+ # a good place to do some logging...
119
+ puts iex.msi.to_h # so you can see what fields are available
120
+ puts "http_status: #{iex.msi.status}"
121
+ end
122
+ ```
123
+
124
+ ## Acknowledgements
125
+
126
+ Thanks to [NETSTOCK](https://www.netstock.co/) for funding development.
127
+
128
+ ## Development
129
+
130
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
131
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
132
+ prompt that will allow you to experiment.
133
+
134
+ To install this gem onto your local machine, run `bundle exec rake install`. To
135
+ release a new version, update the version number in `version.rb`, and then run
136
+ `bundle exec rake release`, which will create a git tag for the version, push
137
+ git commits and tags, and push the `.gem` file to
138
+ [rubygems.org](https://rubygems.org).
139
+
140
+ ## Contributing
141
+
142
+ Bug reports and pull requests are welcome on GitHub at
143
+ https://github.com/djellemah/rodsec.
144
+
145
+ ## License
146
+
147
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ # from rake-compiler gem https://github.com/rake-compiler/rake-compiler
7
+ require 'rake/extensiontask'
8
+ gs = Gem::Specification.load 'rodsec.gemspec'
9
+ Rake::ExtensionTask.new 'msc_intervention', gs do |ext|
10
+ ext.lib_dir = 'lib/rodsec'
11
+ end
12
+
13
+ task :spec => :compile
14
+ task :default => :spec
15
+ task :build => :compile
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # require "bundler/setup"
4
+ $: << "#{__dir__}/../lib"
5
+ require "rodsec"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting easier
8
+
9
+ require 'pry'
10
+ Pry.start Object.new.extend(Rodsec)
data/bin/setup ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
9
+ rake compile
data/examples/body.yml ADDED
@@ -0,0 +1,5 @@
1
+ # this will trigger the rules
2
+ - java.sql.SQLException
3
+ # this won't
4
+ - wp-config.temp
5
+ - He walked away from the confrontation feeling angry and roiled, but at least somewhat vindicated.
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rackup
2
+
3
+ require 'pathname'
4
+ require 'yaml'
5
+ require 'rack/session/cookie.rb'
6
+
7
+ $: << (Pathname(__dir__).parent + 'lib').realpath.to_s
8
+
9
+ require 'rack'
10
+ require 'rodsec/rack'
11
+
12
+ rules_dir = (Pathname __dir__).parent.parent + 'owasp-modsecurity-crs/rules'
13
+ config_dir = (Pathname __dir__).parent + 'spec/config'
14
+
15
+ log_blk = lambda do |tag, str |
16
+ p tag: tag, str: str
17
+ end
18
+
19
+ use Rodsec::Rack, config: config_dir, rules: rules_dir, log_blk: log_blk
20
+ use Rack::Session::Cookie, secret: 'fairy sikrit'
21
+
22
+ fn = Proc.new do |env|
23
+ if (session = env['rack.session'])&.any?
24
+ log_blk.call __FILE__, session.to_h
25
+ end
26
+
27
+ case env['REQUEST_METHOD']
28
+ when 'POST'
29
+ body = env['rack.input'].read
30
+ ['200', {'Content-Type' => 'text/plain'}, [body]]
31
+
32
+ when 'GET'
33
+ body = YAML.load_file Pathname(__dir__) + 'body.yml'
34
+ ['200', {'Content-Type' => 'text/plain'}, body]
35
+ else
36
+ ['200', {}, []]
37
+ end
38
+ end
39
+
40
+ run fn
@@ -0,0 +1,8 @@
1
+ require 'mkmf'
2
+ pkg_config 'modsecurity'
3
+
4
+ # don't need piles of debug info. Or maybe we do. Dunno.
5
+ CONFIG['debugflags'] = ''
6
+
7
+ # argument is the directory in which the .so file will be put
8
+ create_makefile 'rodsec/msc_intervention'
@@ -0,0 +1,48 @@
1
+ #include <stdlib.h>
2
+ #include <new>
3
+
4
+ #include "modsecurity/intervention.h"
5
+
6
+ namespace modsecurity {
7
+
8
+ /**
9
+ * @name msc_new_intervention
10
+ * @brief allocate a new ModSecurityIntervention
11
+ *
12
+
13
+ * For languages that need to access interventions via ffi. So no need to put it
14
+ * in a header file.
15
+
16
+ *
17
+ * @returns a pointer to a clean ModSecurityIntervention,
18
+ * or NULL if the allocation failed.
19
+ *
20
+ */
21
+ extern "C" ModSecurityIntervention * msc_new_intervention() {
22
+ try {
23
+ ModSecurityIntervention * rv = new ModSecurityIntervention();
24
+ // It's actually just a struct, so initialize it to zero values,
25
+ // although status is set to 200 ¯\_(ツ)_/¯
26
+ modsecurity::intervention::clean(rv);
27
+ return rv;
28
+ }
29
+ catch (const std::bad_alloc&) {
30
+ return NULL;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * @name msc_free_intervention
36
+ * @brief free a ModSecurityIntervention pointer
37
+ *
38
+ * For languages that need to access interventions via ffi. So no need to put it
39
+ * in a header file.
40
+ *
41
+ */
42
+ extern "C" void msc_free_intervention(ModSecurityIntervention *it) {
43
+ // free the url and log pointers if they're assigned
44
+ modsecurity::intervention::free(it);
45
+ delete it;
46
+ }
47
+
48
+ } // namespace modsecurity
data/lib/rodsec.rb ADDED
@@ -0,0 +1,18 @@
1
+ require "rodsec/version"
2
+ require 'rodsec/modsec'
3
+ require 'rodsec/rule_set'
4
+ require 'rodsec/transaction'
5
+ require 'rodsec/read_config'
6
+
7
+ module Rodsec
8
+ class Error < StandardError; end
9
+
10
+ class Intervention < StandardError
11
+ def initialize msi
12
+ @msi = msi
13
+ super()
14
+ end
15
+
16
+ attr_reader :msi
17
+ end
18
+ end
@@ -0,0 +1,60 @@
1
+ require_relative 'wrapper'
2
+ require_relative 'version'
3
+ require_relative 'string_pointers'
4
+
5
+ module Rodsec
6
+ class Modsec
7
+ def initialize &log_blk
8
+ @msc_ptr = Wrapper.msc_init
9
+ @msc_ptr.free = Wrapper['msc_cleanup']
10
+
11
+ Wrapper.msc_set_connector_info @msc_ptr, (strptr info_string)
12
+
13
+ logger_fn &log_blk
14
+ end
15
+
16
+ include StringPointers
17
+
18
+ attr_reader :msc_ptr
19
+
20
+ # Given a block, this will set the logger callback.
21
+ # With no block, it will return the current logger callback, which may be nil.
22
+ # 'ModSecLogCb', 'void (*) (void *, const void *)'
23
+ # msc_set_log_cb(ModSecurity *msc, ModSecLogCb cb)
24
+ def logger_fn &log_blk
25
+ if block_given?
26
+ # set the logger callback
27
+ #
28
+ # NOTENOTE logger_fn and logger_closure must NOT be garbage-collected,
29
+ # otherwise callbacks from C to logger_fn will segfault. Also,
30
+ # Fiddle::Function seems to not keep a reference its closure argument,
31
+ # so hang on to that too.
32
+
33
+ return_type = Fiddle::TYPE_VOID
34
+ arg_types = Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP
35
+
36
+ # txn_log_tag is set in the contructor of Transaction.
37
+ @logger_closure = Fiddle::Closure::BlockCaller.new return_type, arg_types do |txn_log_tag, log_str_ptr|
38
+ log_blk.call txn_log_tag.to_s, log_str_ptr.to_s
39
+ end
40
+
41
+ @logger_fn = Fiddle::Function.new @logger_closure, arg_types, return_type
42
+ Wrapper.msc_set_log_cb @msc_ptr, @logger_fn
43
+ else
44
+ # return the logger callback, might be nil
45
+ @logger_fn
46
+ end
47
+ end
48
+
49
+ # given to ModSecurity.
50
+ # should be in the form ConnectorName vX.Y.Z-tag (something else)
51
+ def info_string
52
+ "Rodsec v#{VERSION}"
53
+ end
54
+
55
+ # information about the version of libmodsecurity
56
+ def version_info
57
+ (Wrapper.msc_who_am_i @msc_ptr).to_s
58
+ end
59
+ end
60
+ end