icaprb-filter 0.0.1
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 +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/README.md +68 -0
- data/README.rdoc +22 -0
- data/Rakefile +12 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/start_server.rb +40 -0
- data/icaprb-filter.gemspec +33 -0
- data/lib/icaprb/filter.rb +189 -0
- data/lib/icaprb/filter/service.rb +107 -0
- data/lib/icaprb/filter/solution.rb +954 -0
- data/lib/icaprb/filter/steganography.rb +91 -0
- data/lib/icaprb/filter/version.rb +8 -0
- data/samples/block_samples/check_hashes_test.rb +7 -0
- data/samples/block_samples/check_hashes_test.txt +2 -0
- data/samples/block_samples/content_has_key_test.html +10 -0
- data/samples/block_samples/content_has_key_test.rb +6 -0
- data/samples/block_samples/content_includes_test.html +10 -0
- data/samples/block_samples/content_includes_test.rb +6 -0
- data/samples/block_samples/nothing_to_filter_test.html +10 -0
- data/samples/block_samples/url_contains_test.rb +6 -0
- data/samples/custom_samples/virustotal_config.rb +6 -0
- metadata +205 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0837e275231b622dabdb5b873dc7e241bad263ee
|
4
|
+
data.tar.gz: 29a4e6006735b354efdd595ba16957323248451b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3623f5d18b8c7d5699cb0f45e9cadd76b0fde8e4a5fbd08bfc151766151a5f91078136108fa465eb9eb3bb63d82ed70d1440911e93dc6d91852634f1367780b4
|
7
|
+
data.tar.gz: 5877b36f5a61248bc9a9aa4486d5b4df86a57b00cee9fabb0f5092cc44e8e81e1f793b4875325d58c40cc86fb3168751d1e70b19e34f57b2e0cd404b3eb317f2
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Copyright (c) 2016, Fabian Franz
|
2
|
+
Copyright (c) 2016, Markus Petz
|
3
|
+
Copyright (c) 2016, Fabian Mayer
|
4
|
+
All rights reserved.
|
5
|
+
|
6
|
+
Redistribution and use in source and binary forms, with or without modification,
|
7
|
+
are permitted provided that the following conditions are met:
|
8
|
+
|
9
|
+
1. Redistributions of source code must retain the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer.
|
11
|
+
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
13
|
+
this list of conditions and the following disclaimer in the documentation and/or
|
14
|
+
other materials provided with the distribution.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
17
|
+
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
18
|
+
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
19
|
+
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
20
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
21
|
+
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
22
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
23
|
+
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
24
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
25
|
+
|
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# ICAPrb::Filter
|
2
|
+
|
3
|
+
ICAPrb::Filter is a content filter based on ICAPrb::Server. The goal is
|
4
|
+
to develop a framework which allows to create any custom filtering of
|
5
|
+
any content you would not like to have in your network. This could be
|
6
|
+
ads, trackers, malware, adult content and so on.
|
7
|
+
|
8
|
+
The way to defile rules should feel like configuring a firewall ruleset.
|
9
|
+
|
10
|
+
Plug ins are automatically detected when they reside in the correct
|
11
|
+
namespace and are available to use.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'icaprb-filter'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install icaprb-filter
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
First of all, you will need a server which is serving your content
|
32
|
+
filter.
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
require "icaprb/server"
|
36
|
+
require "icaprb/filter"
|
37
|
+
|
38
|
+
s = ICAPServer.new
|
39
|
+
# url: echo ist der Service - rechts ist der zu verwendende Service
|
40
|
+
s.services['filter'] = ICAPrb::Server::FilterService.new('/path/to/filterconfig')
|
41
|
+
s.run
|
42
|
+
```
|
43
|
+
You will need to replace the path by the path you use on your local
|
44
|
+
machine.
|
45
|
+
|
46
|
+
Next you will have to write your ruleset:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
request_filter do
|
50
|
+
end
|
51
|
+
|
52
|
+
response_filter do
|
53
|
+
block 'Block pages may contain samples',content_includes: ['sample']
|
54
|
+
block 'Block invalid domains', url_contains: ['.invalid']
|
55
|
+
block 'Block hashes of bad file', :check_hashes => ['~/hashes']
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
## Development
|
60
|
+
|
61
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
62
|
+
|
63
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
64
|
+
|
65
|
+
## Contributing
|
66
|
+
|
67
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/fabianfrz/icaprb-filter.
|
68
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
+ICAPrb::Filter+ is the filter component of +ICAPrb+.
|
2
|
+
It aims to be able to filter content based on some matches as well as being able to use plug ins to modify content.
|
3
|
+
|
4
|
+
Modify Content
|
5
|
+
|
6
|
+
This feature is useful to remove some specific parts of your traffic.
|
7
|
+
So you can remove some scripts (for example trackers and ads) based on theire content instead of the url as an URL can simply be changed.
|
8
|
+
So the blocket content can be removed cleanly instead of having a whitespace somewhere on websites.
|
9
|
+
The filter framework also supports actions like passing or blocking content.
|
10
|
+
|
11
|
+
The supported methods are
|
12
|
+
* pass:: passes the current state to the client
|
13
|
+
(in case the client supports 204 responses and the traffic has not been modified - it will be generated, otherwise the full data
|
14
|
+
is sent to the client).
|
15
|
+
* block:: sends a message that the content has been blocked
|
16
|
+
* modify:: calls a plug in to modify the content depending on the return value the modified flag may be set which makes it impossible to
|
17
|
+
create a 204 response. For large files it is a good idea to have a pass rule generating 204 responses before any rule of this
|
18
|
+
type to reduce network traffic (and increase performance).
|
19
|
+
* custom_action:: reserved for plug ins which should interact like a rule type (for example writing files out of the service for
|
20
|
+
analysis. For example put it into a queue to test it inside a sandbox to detect malware)
|
21
|
+
|
22
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
require 'rdoc/task'
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
RDoc::Task.new do |rdoc|
|
10
|
+
rdoc.main = 'README.rdoc'
|
11
|
+
rdoc.rdoc_files.include('README.rdoc', 'lib/**/*.rb')
|
12
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "icaprb/filter"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/bin/start_server.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#
|
2
|
+
require "bundler/setup"
|
3
|
+
require "icaprb/server"
|
4
|
+
require "icaprb/filter"
|
5
|
+
require 'logger'
|
6
|
+
include ICAPrb::Server
|
7
|
+
|
8
|
+
trap('SIGINT') { exit! 0 }
|
9
|
+
########################################################################
|
10
|
+
# DIFFERENT WAYS TO RUN THE SERVER #
|
11
|
+
########################################################################
|
12
|
+
|
13
|
+
# normal socket
|
14
|
+
s = ICAPServer.new
|
15
|
+
# puts 'Server is running on port 1344. Press CTRL+C to exit...'
|
16
|
+
|
17
|
+
# squid v4 variant
|
18
|
+
#options = {secure: true,
|
19
|
+
# certificate: '../cert.pem',
|
20
|
+
# key: '../key.pem',
|
21
|
+
# tls_socket: true}
|
22
|
+
#s = ICAPServer.new('localhost',11344,options)
|
23
|
+
# puts 'Server is running on port 11344. Press CTRL+C to exit...'
|
24
|
+
|
25
|
+
# rfc 3507 variant
|
26
|
+
#options = {secure: true,
|
27
|
+
# certificate: '../cert.pem',
|
28
|
+
# key: '../key.pem',
|
29
|
+
# tls_socket: false}
|
30
|
+
#s = ICAPServer.new('localhost',1344,options)
|
31
|
+
puts 'Server is running on port 1344. Press CTRL+C to exit...'
|
32
|
+
|
33
|
+
########################################################################
|
34
|
+
s.logger.level = Logger::DEBUG
|
35
|
+
# TIMEOUT:
|
36
|
+
# timeout_in_seconds = 10
|
37
|
+
# s.services['filter'] = ICAPrb::Server::FilterService.new('~/conf/conf.conf',timeout_in_seconds)
|
38
|
+
s.services['filter'] = ICAPrb::Server::FilterService.new('~/conf/conf.conf')
|
39
|
+
|
40
|
+
s.run
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'icaprb/filter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'icaprb-filter'
|
8
|
+
spec.version = ICAPrb::Filter::VERSION
|
9
|
+
spec.authors = ['Fabian Franz']
|
10
|
+
spec.email = ['fabian.franz@students.fh-hagenberg.at']
|
11
|
+
|
12
|
+
spec.summary = %q{Filter framework for ICAPrb-server. Does not work standalone. This gem includes
|
13
|
+
a framework to create a content filter which allows to filter content instead of only the
|
14
|
+
urls. It is extensible by plug ins. For example it can be used to enforce your companies
|
15
|
+
network usage policies at the proxy.}
|
16
|
+
spec.homepage = 'https://github.com/fabianfrz/ICAPrb-Filter'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
spec.bindir = 'exe'
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_dependency 'nokogiri', '~> 1.6', '>= 1.6.7'
|
24
|
+
spec.add_dependency 'uirusu' # Virustotal public api
|
25
|
+
spec.add_dependency 'pdf-reader', '~> 1.4'
|
26
|
+
spec.add_dependency 'chunky_png'
|
27
|
+
spec.add_dependency 'icaprb-server'
|
28
|
+
spec.add_dependency 'rest-client'
|
29
|
+
# spec.add_dependency 'ruby-filemagic', '~> 0.7.1'
|
30
|
+
spec.add_development_dependency 'bundler', '~> 1.11'
|
31
|
+
spec.add_development_dependency 'rake'
|
32
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
33
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require "icaprb/filter/version"
|
2
|
+
# All filter types
|
3
|
+
require "icaprb/filter/solution"
|
4
|
+
require "icaprb/filter/service"
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
# ICAPrb
|
8
|
+
module ICAPrb
|
9
|
+
# Filter module
|
10
|
+
module Filter
|
11
|
+
# Exception which is thrown when the configuration file is invalid
|
12
|
+
class ConfigurationLoadError < StandardError
|
13
|
+
end
|
14
|
+
# Response mod filters+plugins
|
15
|
+
@resp_mod = nil
|
16
|
+
# Temporary during reading
|
17
|
+
@temp_reps_mod = nil
|
18
|
+
# Request mod filters+plugins
|
19
|
+
@req_mod = nil
|
20
|
+
# Temporary during reading
|
21
|
+
@temp_req_mod = nil
|
22
|
+
|
23
|
+
# set the configuration mode to request filter so the rules will know that they are request filter rules.
|
24
|
+
# It takes a block which is executed while the filter_type is set up.
|
25
|
+
def self.request_filter
|
26
|
+
@filter_type = :request_mod
|
27
|
+
yield
|
28
|
+
@filter_type = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
# set the configuration mode to response filter so the rules will know that they are response filter rules.
|
32
|
+
# It takes a block which is executed while the filter_type is set up.
|
33
|
+
def self.response_filter
|
34
|
+
@filter_type = :response_mod
|
35
|
+
yield
|
36
|
+
@filter_type = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# returns the filters
|
40
|
+
def self.get_filters
|
41
|
+
return @filters
|
42
|
+
end
|
43
|
+
|
44
|
+
# creates a new block rule with the parameters
|
45
|
+
# +description+:: description of the rule, so it can be logged and the admin can troubleshoot the ruleset.
|
46
|
+
# +action+:: do something
|
47
|
+
#
|
48
|
+
# raises a ConfigurationLoadError if it is called outside of request_filter or response_filter
|
49
|
+
def self.block(description, action)
|
50
|
+
puts "============================="
|
51
|
+
puts "BLOCK: #{description}, ACTION NAME: #{action.keys.first}"
|
52
|
+
if @filter_type == :request_mod
|
53
|
+
puts "TYPE: REQUEST"
|
54
|
+
elsif @filter_type == :response_mod
|
55
|
+
puts "TYPE: RESPONSE"
|
56
|
+
else
|
57
|
+
raise ConfigurationLoadError, "used param 'block' without a filter type"
|
58
|
+
end
|
59
|
+
# Check if action name exists and create it
|
60
|
+
found_name = false
|
61
|
+
@valid_filters.each { |filter|
|
62
|
+
if filter[:name].to_s == action.keys.first.to_s
|
63
|
+
found_name = true
|
64
|
+
new_filter = {:name => action.keys.first,
|
65
|
+
:object => filter[:class].new(@filter_type, action[action.keys.first]),
|
66
|
+
:description => description}
|
67
|
+
if @filter_type == :request_mod
|
68
|
+
@temp_req_mod << new_filter
|
69
|
+
elsif @filter_type == :response_mod
|
70
|
+
@temp_resp_mod << new_filter
|
71
|
+
end
|
72
|
+
break
|
73
|
+
end
|
74
|
+
}
|
75
|
+
unless found_name
|
76
|
+
raise ConfigurationLoadError, "Unknown filter type '" + action.keys.first.to_s + "' "
|
77
|
+
end
|
78
|
+
end
|
79
|
+
# Load a modifiying rule
|
80
|
+
# +description+:: Description of the rule
|
81
|
+
# +action+:: Name of the plugin to use
|
82
|
+
# +match+:: Parameters for the plugin
|
83
|
+
def self.modify(description, action, match={})
|
84
|
+
puts "============================="
|
85
|
+
puts "MODIFY/CUSTOM: #{description}, ACTION NAME: #{action.to_s}"
|
86
|
+
if @filter_type == :request_mod
|
87
|
+
puts "TYPE: REQUEST"
|
88
|
+
elsif @filter_type == :response_mod
|
89
|
+
puts "TYPE: RESPONSE"
|
90
|
+
else
|
91
|
+
raise ConfigurationLoadError, "used param 'block' without a filter type"
|
92
|
+
end
|
93
|
+
# Check if action name exists and create it
|
94
|
+
found_name = false
|
95
|
+
@valid_plugins.each { |plugin|
|
96
|
+
if plugin[:name].to_s == action.to_s
|
97
|
+
found_name = true
|
98
|
+
# Check if given plugin supports filter type
|
99
|
+
unless plugin[:class]::MODES.include? @filter_type
|
100
|
+
raise ConfigurationLoadError, 'Plugin ' + action.to_s + ' does not support filter type'
|
101
|
+
end
|
102
|
+
new_plugin = {:name => action,
|
103
|
+
:object => plugin[:class].new(@filter_type, match[match.keys.first]),
|
104
|
+
:description => description}
|
105
|
+
if @filter_type == :request_mod
|
106
|
+
@temp_req_mod << new_plugin
|
107
|
+
elsif @filter_type == :response_mod
|
108
|
+
@temp_resp_mod << new_plugin
|
109
|
+
end
|
110
|
+
end
|
111
|
+
}
|
112
|
+
unless found_name
|
113
|
+
raise ConfigurationLoadError, 'Unknown plugin type'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
# Custom action, alias for modify
|
117
|
+
# The reason for it being a simple alias is that it may not modify
|
118
|
+
# its content, for example a virustotal scan takes too long
|
119
|
+
# so we pass it through anyway, but the scan results is saved in a log file
|
120
|
+
# May modify the content, but default behaviour should be that it doesn't
|
121
|
+
# +description+:: Description of the function
|
122
|
+
# +action+:: Name of the action in the configuration file
|
123
|
+
# +match+:: Attributes
|
124
|
+
def self.custom_action(description, action, match={})
|
125
|
+
self.modify(description, action, match)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Placeholder if user wants to write pass, doesn't do anything
|
129
|
+
# For future expansion: Add conditionals to the rules ==> rewrite pass
|
130
|
+
def self.pass()
|
131
|
+
end
|
132
|
+
# Delete current filters
|
133
|
+
# Simple reset for tests of configuration files
|
134
|
+
def self.delete_filters()
|
135
|
+
@req_mod = nil
|
136
|
+
@resp_mod = nil
|
137
|
+
@temp_req_mod = nil
|
138
|
+
@temp_resp_mod = nil
|
139
|
+
@filter_type = nil
|
140
|
+
end
|
141
|
+
|
142
|
+
# Try to load the configureation file from path.
|
143
|
+
# If everything works as expected, the new filter rules are set,
|
144
|
+
# otherwise an error is raised if the configuration file is invalid and no previous configuration is available
|
145
|
+
# which would lead into an empty config or the loading will fail with an error on the log.
|
146
|
+
# +path+:: Path to the configuration file
|
147
|
+
def self.load_filters(path)
|
148
|
+
# puts are required because no logger is given when the service is created
|
149
|
+
# Setup objects to be written into
|
150
|
+
puts 'Trying to read new configuration'
|
151
|
+
# Set valid plugins and filters, has to done for each reload
|
152
|
+
@valid_filters = ICAPrb::Filters.get_filters
|
153
|
+
@valid_plugins = ICAPrb::Filters.get_plugins
|
154
|
+
# Execute file (we trust the user blindly, assuming that the configuration file is protected sufficiently)
|
155
|
+
@temp_resp_mod = []
|
156
|
+
@temp_req_mod = []
|
157
|
+
begin
|
158
|
+
eval(File.read File.expand_path(path))
|
159
|
+
rescue StandardError => error
|
160
|
+
error_info = "#{error.message} #{error.backtrace}"
|
161
|
+
# Initial info couldn't be loaded, shutting down filter framework
|
162
|
+
if @resp_mod == nil or @req_mod == nil
|
163
|
+
puts "Initial configuration couldn't be loaded: #{error_info}"
|
164
|
+
# Non recoverable
|
165
|
+
raise ConfigurationLoadError, "Initial configuration couldn't be loaded: #{error_info}"
|
166
|
+
else
|
167
|
+
puts "Error when reloading the configuration, configuration wasn't changed: #{error_info}"
|
168
|
+
# Don't throw an error, we still retain the old configuration
|
169
|
+
@temp_resp_mod = nil
|
170
|
+
@temp_req_mod = nil
|
171
|
+
@filter_type = nil
|
172
|
+
return
|
173
|
+
end
|
174
|
+
end
|
175
|
+
# Write data into variables (writing it beforehand is dangerous because the incomplete object may be used
|
176
|
+
# for new requests, this allows to reload the configuration during runtime)
|
177
|
+
@req_mod = @temp_req_mod
|
178
|
+
@resp_mod = @temp_resp_mod
|
179
|
+
puts 'Finished reading filter configuration'
|
180
|
+
return @req_mod, @resp_mod
|
181
|
+
end
|
182
|
+
|
183
|
+
# Set logger for filter
|
184
|
+
# +logger+:: Logger object
|
185
|
+
def self.set_logger(logger)
|
186
|
+
@logger = logger
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|