google_safe_browsing 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ module GoogleSafeBrowsing
2
+ class FullHash < ActiveRecord::Base
3
+ set_table_name 'gsb_full_hashes'
4
+ end
5
+ end
@@ -0,0 +1,19 @@
1
+ module GoogleSafeBrowsing
2
+ class GoogleSafeBrowsingRailtie < Rails::Railtie
3
+ config.google_safe_browsing = ActiveSupport::OrderedOptions.new
4
+
5
+ rake_tasks do
6
+ load File.expand_path('../../tasks/google_safe_browsing_tasks.rake', __FILE__)
7
+ end
8
+
9
+ generators do
10
+ require File.expand_path('../../generators/install_generator', __FILE__)
11
+ end
12
+
13
+ initializer 'google_safe_browsing.set_api_key' do |app|
14
+ GoogleSafeBrowsing.configure do |config|
15
+ config.api_key = app.config.google_safe_browsing[:api_key]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ module GoogleSafeBrowsing
2
+ class HashHelper
3
+
4
+ class GsbHash
5
+ def initialize(hash)
6
+ @hash = hash
7
+ end
8
+
9
+ def prefix
10
+ @hash[0..7]
11
+ end
12
+
13
+ def to_s
14
+ @hash
15
+ end
16
+
17
+ end
18
+
19
+ def self.urls_to_hashes(urls)
20
+ hashes = []
21
+ urls.each do |u|
22
+ hash = ( Digest::SHA256.new << u ).to_s
23
+ hashes << GsbHash.new(hash)
24
+ #puts "#{u} -- #{hash}"
25
+ end
26
+ hashes
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,42 @@
1
+ module GoogleSafeBrowsing
2
+ class HttpHelper
3
+ def self.uri_builder(action)
4
+ uri = URI("#{GoogleSafeBrowsing.config.host}/#{action}#{encoded_params}")
5
+ uri
6
+ end
7
+
8
+ def self.encoded_params
9
+ "?client=#{GoogleSafeBrowsing.config.client}" +
10
+ "&apikey=#{GoogleSafeBrowsing.config.api_key}" +
11
+ "&appver=#{GoogleSafeBrowsing.config.app_ver}" +
12
+ "&pver=#{GoogleSafeBrowsing.config.p_ver}"
13
+ end
14
+
15
+ def self.request_full_hashes(hash_array)
16
+ uri = uri_builder('gethash')
17
+ request = Net::HTTP::Post.new(uri.request_uri)
18
+ request.body = "4:#{hash_array.length * 4}\n"
19
+ hash_array.each do |h|
20
+ request.body << BinaryHelper.hex_to_bin(h[0..7])
21
+ end
22
+
23
+ response = Net::HTTP.start(uri.host) { |http| http.request request }
24
+
25
+ ResponseHelper.parse_full_hash_response(response.body)
26
+ end
27
+
28
+ def self.get_data(list=nil)
29
+ # Get (via Post) List Data
30
+ uri = uri_builder('downloads')
31
+ request = Net::HTTP::Post.new(uri.request_uri)
32
+ request.body = ChunkHelper.build_chunk_list(list)
33
+
34
+ Net::HTTP.start(uri.host) { |http| http.request request }
35
+ end
36
+
37
+ def get_lists
38
+ uri = uri_builder('list')
39
+ lists = Net::HTTP.get(uri).split("\n")
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,14 @@
1
+ module GoogleSafeBrowsing
2
+ class Rescheduler
3
+ @queue = :google_safe_browsing
4
+
5
+ def self.perform
6
+ puts "Running Update"
7
+ delay = APIv2.update
8
+ puts "Scheduling new update in #{delay} seconds"
9
+ Resque.enqueue_in(delay.seconds, Rescheduler)
10
+ puts "Update scheduled"
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,181 @@
1
+ module GoogleSafeBrowsing
2
+ class ResponseHelper
3
+ def self.parse_full_hash_response(response)
4
+ f = StringIO.new(response)
5
+
6
+ full_hashes = []
7
+ while(! f.eof? )
8
+ hash = {}
9
+
10
+ meta = f.gets.chomp.split(':')
11
+ hash[:list] = meta[0]
12
+ hash[:add_chunk_num] = meta[1]
13
+
14
+ hash[:full_hash] = f.read(meta[2].to_i).unpack('H*')[0]
15
+ full_hashes << hash
16
+ end
17
+ full_hashes
18
+ end
19
+
20
+ def self.parse_data_response(response)
21
+ #print "\n\n#{response}\n\n"
22
+ data_urls = []
23
+ ret = {}
24
+
25
+ ret[:lists] = []
26
+ ret[:data_urls] = {}
27
+
28
+ current_list = ''
29
+ response.split("\n").each do |line|
30
+ vals = line.split(':')
31
+ case vals[0]
32
+ when 'n'
33
+ ret[:delay_seconds] = vals[1].to_i
34
+ @delay_seconds = ret[:delay_seconds].to_i
35
+ when 'i'
36
+ ret[:lists] << vals[1]
37
+ current_list = vals[1]
38
+ ret[:data_urls][current_list] = []
39
+ when 'u'
40
+ ret[:data_urls][current_list] << vals[1]
41
+ when 'r'
42
+ # reset (delete all data and try again)
43
+ when 'ad'
44
+ # vals[1] is a CHUNKLIST number or range representing add chunks to delete
45
+ # we can also delete the associated Shavar Hashes
46
+ # we no longer have to report hat we received these chunks
47
+ chunk_number_clause = ChunkHelper.chunklist_to_sql(vals[1])
48
+ AddShavar.delete_all([ "list = ? and (#{chunk_number_clause})", current_list ])
49
+ when 'sd'
50
+ # vals[1] is a CHUNKLIST number or range representing sub chunks to delete
51
+ # we no longer have to report hat we received these chunks
52
+ chunk_number_clause = ChunkHelper.chunklist_to_sql(vals[1])
53
+ SubShavar.delete_all([ "list = ? and (#{chunk_number_clause})", current_list ])
54
+ end
55
+ end
56
+
57
+ #ret[:data_urls] = data_urls
58
+
59
+ ret
60
+ end
61
+
62
+ def self.receive_data(url, list)
63
+
64
+ open(url) do |f|
65
+ while(line = f.gets)
66
+ line_actions = parse_data_line(line)
67
+
68
+ chunk = f.read(line_actions[:chunk_length])
69
+ # f iterator is now set for next chunk
70
+
71
+ add_attrs = { :chunk_number => line_actions[:chunk_number],
72
+ :list => list, :prefix => nil, :host_key => nil
73
+ }
74
+
75
+ case line_actions[:action]
76
+ when 'a'
77
+ if line_actions[:chunk_length] == 0
78
+ record_add_shavar_to_insert(add_attrs)
79
+ else
80
+ chunk_iterator = chunk.bytes
81
+ counter = 0
82
+ begin
83
+ while true
84
+ add_attrs[:host_key] = BinaryHelper.read_bytes_as_hex(chunk_iterator, 4)
85
+ count = chunk_iterator.next
86
+ if count > 0
87
+ count.times do |i|
88
+ add_attrs[:prefix] = BinaryHelper.read_bytes_as_hex(chunk_iterator, line_actions[:hash_length])
89
+ record_add_shavar_to_insert(add_attrs)
90
+ end
91
+ else
92
+ add_attrs[:prefix] = add_attrs[:host_key]
93
+ record_add_shavar_to_insert(add_attrs)
94
+ end
95
+ counter += 1
96
+ end
97
+ rescue StopIteration
98
+ puts "Added #{counter} host_keys for add chunk number #{line_actions[:chunk_number]}"
99
+ end
100
+ end
101
+ when 's'
102
+ sub_attrs = add_attrs.merge({ :add_chunk_number => nil })
103
+ if line_actions[:chunk_length] == 0
104
+ record_sub_shavar_to_insert(sub_attrs)
105
+ else
106
+ chunk_iterator = chunk.bytes
107
+ counter = 0
108
+ begin
109
+ while true
110
+ sub_attrs[:host_key] = BinaryHelper.read_bytes_as_hex(chunk_iterator, 4)
111
+ count = chunk_iterator.next
112
+ if count > 0
113
+ count.times do |i|
114
+ sub_attrs[:add_chunk_number] = BinaryHelper.unpack_add_chunk_num(BinaryHelper.read_bytes_from(chunk_iterator, 4))
115
+ sub_attrs[:prefix] = BinaryHelper.read_bytes_as_hex(chunk_iterator, line_actions[:hash_length])
116
+ record_sub_shavar_to_insert(sub_attrs)
117
+ end
118
+ else
119
+ sub_attrs[:add_chunk_number] = BinaryHelper.unpack_add_chunk_num(BinaryHelper.read_bytes_from(chunk_iterator, 4))
120
+ sub_attrs[:prefix] = sub_attrs[:host_key]
121
+ record_sub_shavar_to_insert(sub_attrs)
122
+ end
123
+ counter += 1
124
+ end
125
+ rescue StopIteration
126
+ puts "Added #{counter} host_keys for sub chunk number #{line_actions[:chunk_number]}"
127
+ end
128
+ end
129
+ else
130
+ puts "neither a nor s ======================================================="
131
+ end
132
+
133
+ end
134
+ end
135
+
136
+ # actually perform inserts
137
+ while @add_shavar_values && @add_shavar_values.any?
138
+ AddShavar.connection.execute( "insert into gsb_add_shavars (prefix, host_key, chunk_number, list) " +
139
+ " values #{@add_shavar_values.pop(10000).join(', ')}")
140
+ end
141
+ while @sub_shavar_values && @sub_shavar_values.any?
142
+ SubShavar.connection.execute( "insert into gsb_sub_shavars (prefix, host_key, add_chunk_number, chunk_number, list) " +
143
+ " values #{@sub_shavar_values.pop(10000).join(', ')}")
144
+ end
145
+ # remove invalid full_hases
146
+ FullHash.connection.execute("delete from gsb_full_hashes using gsb_full_hashes " +
147
+ "inner join gsb_sub_shavars on " +
148
+ "gsb_sub_shavars.add_chunk_number = gsb_full_hashes.add_chunk_number " +
149
+ "and gsb_sub_shavars.list = gsb_full_hashes.list;")
150
+ @add_shavar_values = []
151
+ @sub_shavar_values = []
152
+ end
153
+
154
+ def self.record_add_shavar_to_insert(h)
155
+ @add_shavar_values ||= []
156
+ @add_shavar_values << "('#{h[:prefix]}', '#{h[:host_key]}', '#{h[:chunk_number]}', '#{h[:list]}')"
157
+ end
158
+ def self.record_sub_shavar_to_insert(h)
159
+ @sub_shavar_values ||= []
160
+ @sub_shavar_values << "('#{h[:prefix]}', '#{h[:host_key]}', '#{h[:add_chunk_number]}', '#{h[:chunk_number]}', '#{h[:list]}')"
161
+ end
162
+
163
+ def self.parse_data_line(line)
164
+ split_line = line.split(':')
165
+
166
+ ret = {}
167
+ ret[ :action ] = split_line[0]
168
+ ret[ :chunk_number ] = split_line[1].to_i
169
+ ret[ :hash_length ] = split_line[2].to_i
170
+ ret[ :chunk_length ] = split_line[3].to_i
171
+
172
+ #puts "Chunk ##{s_chunk_count + a_chunk_count}"
173
+ #puts "Action: #{action}"
174
+ #puts "Chunk Number: #{split_line[1]}"
175
+ #puts "Hash Length: #{hash_length}"
176
+ #puts "Chunk Length: #{chunk_length}"
177
+ ##puts "Chuch Data:\n#{chunk}\nend"
178
+ ret
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,6 @@
1
+ module GoogleSafeBrowsing
2
+ class SubShavar < ActiveRecord::Base
3
+ set_table_name 'gsb_sub_shavars'
4
+ end
5
+ end
6
+
@@ -0,0 +1,50 @@
1
+ module GoogleSafeBrowsing
2
+ class TopLevelDomain
3
+
4
+ def self.from_host(host)
5
+ components = host.split('.')
6
+
7
+ tlds = parse_tld_to_hash
8
+
9
+ tld = components.pop
10
+ components.reverse.each do |comp|
11
+ next_tld = "#{comp}.#{tld}"
12
+
13
+ if tlds[next_tld]
14
+ tld = next_tld
15
+ else
16
+ break
17
+ end
18
+ end
19
+
20
+ tld
21
+ end
22
+
23
+ def self.split_from_host(host)
24
+ components = host.split('.')
25
+
26
+ tlds = parse_tld_to_hash
27
+
28
+ next_tld = components[-2..-1].join('.')
29
+ while tlds[next_tld]
30
+ tmp = components.pop
31
+ components[-1] = components.last + '.' + tmp
32
+ next_tld = components[-2..-1].join('.')
33
+ end
34
+
35
+ components
36
+ end
37
+
38
+
39
+ private
40
+
41
+ def self.parse_tld_to_hash
42
+ hash = Hash.new(nil)
43
+ f = File.open(File.dirname(__FILE__) + '/effective_tld_names.dat.txt', 'r')
44
+ while(line = f.gets)
45
+ hash[line.chomp] = true unless line[0..1] == '//'
46
+ end
47
+ hash
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ module GoogleSafeBrowsing
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,11 @@
1
+ namespace :google_safe_browsing do
2
+ desc "Performs an Update"
3
+ task :update => :environment do
4
+ GoogleSafeBrowsing::APIv2.update
5
+ end
6
+
7
+ desc "Enqueues an Update via Rescheduler, which will reschedule another Update the appropriate number of seconds in the future"
8
+ task :update_and_reschedule => :environment do
9
+ GoogleSafeBrowsing.kick_off
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: google_safe_browsing
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Chris Marshall
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-02-28 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ prerelease: false
22
+ version_requirements: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ hash: 3
28
+ segments:
29
+ - 3
30
+ - 1
31
+ - 0
32
+ version: 3.1.0
33
+ requirement: *id001
34
+ name: rails
35
+ type: :runtime
36
+ - !ruby/object:Gem::Dependency
37
+ prerelease: false
38
+ version_requirements: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 57
44
+ segments:
45
+ - 0
46
+ - 9
47
+ - 1
48
+ version: 0.9.1
49
+ requirement: *id002
50
+ name: ruby-ip
51
+ type: :runtime
52
+ - !ruby/object:Gem::Dependency
53
+ prerelease: false
54
+ version_requirements: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirement: *id003
64
+ name: activerecord
65
+ type: :runtime
66
+ - !ruby/object:Gem::Dependency
67
+ prerelease: false
68
+ version_requirements: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirement: *id004
78
+ name: sqlite3
79
+ type: :development
80
+ - !ruby/object:Gem::Dependency
81
+ prerelease: false
82
+ version_requirements: &id005 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ requirement: *id005
92
+ name: rspec-rails
93
+ type: :development
94
+ - !ruby/object:Gem::Dependency
95
+ prerelease: false
96
+ version_requirements: &id006 !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ hash: 3
102
+ segments:
103
+ - 0
104
+ version: "0"
105
+ requirement: *id006
106
+ name: generator_spec
107
+ type: :development
108
+ description: Rails 3 plugin using Google's Safe Browsing API for url lookup against Malware and Phishing blacklists. Implementation includes storing and updating locally stored shavar lists and url lookup methods.
109
+ email: chris@chrismar035.com
110
+ executables: []
111
+
112
+ extensions: []
113
+
114
+ extra_rdoc_files: []
115
+
116
+ files:
117
+ - lib/generators/install_generator.rb
118
+ - lib/generators/templates/create_google_safe_browsing_tables.rb
119
+ - lib/google_safe_browsing/add_shavar.rb
120
+ - lib/google_safe_browsing/api_v2.rb
121
+ - lib/google_safe_browsing/binary_helper.rb
122
+ - lib/google_safe_browsing/canonicalize.rb
123
+ - lib/google_safe_browsing/chunk_helper.rb
124
+ - lib/google_safe_browsing/effective_tld_names.dat.txt
125
+ - lib/google_safe_browsing/full_hash.rb
126
+ - lib/google_safe_browsing/google_safe_browsing_railtie.rb
127
+ - lib/google_safe_browsing/hash_helper.rb
128
+ - lib/google_safe_browsing/http_helper.rb
129
+ - lib/google_safe_browsing/rescheduler.rb
130
+ - lib/google_safe_browsing/response_helper.rb
131
+ - lib/google_safe_browsing/sub_shavar.rb
132
+ - lib/google_safe_browsing/top_level_domain.rb
133
+ - lib/google_safe_browsing/version.rb
134
+ - lib/google_safe_browsing.rb
135
+ - lib/tasks/google_safe_browsing_tasks.rake
136
+ - MIT-LICENSE
137
+ - Rakefile
138
+ - README.mkd
139
+ homepage: https://github.com/chrismar035/google_safe_browsing
140
+ licenses: []
141
+
142
+ post_install_message:
143
+ rdoc_options: []
144
+
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ none: false
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ hash: 3
153
+ segments:
154
+ - 0
155
+ version: "0"
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ none: false
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ hash: 3
162
+ segments:
163
+ - 0
164
+ version: "0"
165
+ requirements: []
166
+
167
+ rubyforge_project:
168
+ rubygems_version: 1.8.16
169
+ signing_key:
170
+ specification_version: 3
171
+ summary: Rails 3 plugin for Google's Safe Browsing API v2
172
+ test_files: []
173
+