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.
- data/MIT-LICENSE +20 -0
- data/README.mkd +101 -0
- data/Rakefile +26 -0
- data/lib/generators/install_generator.rb +25 -0
- data/lib/generators/templates/create_google_safe_browsing_tables.rb +37 -0
- data/lib/google_safe_browsing.rb +58 -0
- data/lib/google_safe_browsing/add_shavar.rb +5 -0
- data/lib/google_safe_browsing/api_v2.rb +60 -0
- data/lib/google_safe_browsing/binary_helper.rb +40 -0
- data/lib/google_safe_browsing/canonicalize.rb +181 -0
- data/lib/google_safe_browsing/chunk_helper.rb +77 -0
- data/lib/google_safe_browsing/effective_tld_names.dat.txt +5197 -0
- data/lib/google_safe_browsing/full_hash.rb +5 -0
- data/lib/google_safe_browsing/google_safe_browsing_railtie.rb +19 -0
- data/lib/google_safe_browsing/hash_helper.rb +29 -0
- data/lib/google_safe_browsing/http_helper.rb +42 -0
- data/lib/google_safe_browsing/rescheduler.rb +14 -0
- data/lib/google_safe_browsing/response_helper.rb +181 -0
- data/lib/google_safe_browsing/sub_shavar.rb +6 -0
- data/lib/google_safe_browsing/top_level_domain.rb +50 -0
- data/lib/google_safe_browsing/version.rb +3 -0
- data/lib/tasks/google_safe_browsing_tasks.rake +11 -0
- metadata +173 -0
@@ -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,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,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
|
+
|