google_safe_browsing 0.1.0

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.
@@ -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
+