malware_api 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.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,39 @@
1
+ Malware
2
+ =======
3
+
4
+ Malware is a rails plugin to take advantage of the Google Safe Browsing API
5
+
6
+ Installation
7
+ ============
8
+
9
+ Request an API key from http://code.google.com/apis/safebrowsing/key_signup.html
10
+
11
+ Create a config file with your API key. In `RAILS_ROOT`/config/initializers/malware_key.rb
12
+
13
+ # API key for Google Safe Browsing API
14
+ # http://code.google.com/apis/safebrowsing/
15
+ SAFEBROWSING_API_KEY = "ABQIAAAAN_MxjBsAgBKC8N1cqNloZhT3elDgi-bgogZuFD30ho7emQ1XEw"
16
+
17
+ Install the plugin
18
+
19
+ gem install malware
20
+
21
+ Generate and run a migration to create the tables
22
+
23
+ script/generate malware_migration
24
+ rake db:migrate
25
+
26
+ Example
27
+ =======
28
+
29
+ $ script/console
30
+ Loading development environment (Rails 2.3.5)
31
+ >> Malware.update
32
+ => "Updated malware info (in 73.865188s) +301657/-0 (1.47438 1.18185)\n"
33
+ >> Malware.check('http://malware.testing.google.test/testing/malware/')
34
+ => true
35
+ >> Malware.check('http://www.jorgebernal.info/')
36
+ => false
37
+
38
+
39
+ Copyright (c) 2009 Jorge Bernal <koke@amedias.org>, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+
6
+ desc 'Default: run unit tests.'
7
+ task :default => :test
8
+
9
+ desc 'Test the malware plugin.'
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << 'lib'
12
+ t.libs << 'test'
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = true
15
+ end
16
+
17
+ desc 'Generate documentation for the malware plugin.'
18
+ Rake::RDocTask.new(:rdoc) do |rdoc|
19
+ rdoc.rdoc_dir = 'rdoc'
20
+ rdoc.title = 'Malware'
21
+ rdoc.options << '--line-numbers' << '--inline-source'
22
+ rdoc.rdoc_files.include('README')
23
+ rdoc.rdoc_files.include('lib/**/*.rb')
24
+ end
25
+
26
+ namespace :db do
27
+ namespace :migrate do
28
+ description = "Migrate the database through scripts in vendor/plugins/malware/lib/db/migrate"
29
+ description << "and update db/schema.rb by invoking db:schema:dump."
30
+ description << "Target specific version with VERSION=x. Turn off output with VERBOSE=false."
31
+
32
+ desc description
33
+ task :malware => :environment do
34
+ ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
35
+ ActiveRecord::Migrator.migrate("vendor/plugins/malware/lib/db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
36
+ Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
37
+ end
38
+ end
39
+ end
40
+
41
+ desc %{Update ".manifest" with the latest list of project filenames. Respect\
42
+ .gitignore by excluding everything that git ignores. Update `files` and\
43
+ `test_files` arrays in "*.gemspec" file if it's present.}
44
+ task :manifest do
45
+ list = `git ls-files --full-name --exclude=*.gemspec --exclude=.*`.chomp.split("\n")
46
+
47
+ if spec_file = Dir['*.gemspec'].first
48
+ spec = File.read spec_file
49
+ spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
50
+ assignment = $1
51
+ bunch = $2 ? list.grep(/^test\//) : list
52
+ '%s%%w(%s)' % [assignment, bunch.join(' ')]
53
+ end
54
+
55
+ File.open(spec_file, 'w') { |f| f << spec }
56
+ end
57
+ File.open('.manifest', 'w') { |f| f << list.join("\n") }
58
+ end
File without changes
@@ -0,0 +1,9 @@
1
+ class MalwareMigrationGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.migration_template 'migration.rb', "db/migrate", {
5
+ :migration_file_name => "create_malware_tables"
6
+ }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ class CreateMalwareTables < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :malwares do |t|
4
+ t.integer :black_major
5
+ t.integer :black_minor
6
+ t.integer :malware_major
7
+ t.integer :malware_minor
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ create_table :malware_hashes do |t|
13
+ t.string :url, :limit => 32, :null => false
14
+ end
15
+ end
16
+
17
+ def self.down
18
+ drop_table :malware_hashes
19
+ drop_table :malwares
20
+ end
21
+ end
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,111 @@
1
+ require "open-uri"
2
+
3
+ class Malware < ActiveRecord::Base
4
+ before_create :delete_previous
5
+
6
+ # Inserting/deleting one entry at a time is painfully slow
7
+ # Inserting in batches of 100 entries reduces time to ~1 minute
8
+ @batch_size = 100
9
+
10
+ # Fetch changes from google servers and store them in the database
11
+ # Might be a slow operation, specially on the first run
12
+ def self.update
13
+ options = find(:first, :order => "created_at DESC")
14
+ unless options
15
+ options = self.new
16
+ options.black_major ||= 1
17
+ options.black_minor ||= -1
18
+ options.malware_major ||= 1
19
+ options.malware_minor ||= -1
20
+
21
+ options.save
22
+ end
23
+
24
+ update_uri = "http://sb.google.com/safebrowsing/update?client=api" +
25
+ "&apikey=#{SAFEBROWSING_API_KEY}" +
26
+ "&version=goog-black-hash:#{options.black_major}:#{options.black_minor}," +
27
+ "goog-malware-hash:#{options.malware_major}:#{options.malware_minor}"
28
+
29
+ logger.info("Updating malware info (#{options.black_major}.#{options.black_minor} #{options.malware_major}.#{options.malware_minor})")
30
+ added = 0
31
+ removed = 0
32
+ to_add = []
33
+ to_remove = []
34
+ started = Time.now
35
+
36
+ # FIXME: try batch inserts if performance becomes an issue
37
+ open(update_uri) do |f|
38
+ f.each_line do |line|
39
+ if (line =~ /\[goog-black-hash (\d+).(\d+)/)
40
+ options.black_major = $1
41
+ options.black_minor = $2
42
+ logger.info("Found new black version (#{options.black_major},#{options.black_minor})")
43
+ elsif (line =~ /\[goog-malware-hash (\d+).(\d+)/)
44
+ options.malware_major = $1
45
+ options.malware_minor = $2
46
+ logger.info("Found new malware version (#{options.malware_major}.#{options.malware_minor})")
47
+ elsif (line =~ /\+([0-9a-f]{32})/)
48
+ to_add << $1
49
+ # MalwareHash.create(:url => $1)
50
+ added += 1
51
+ elsif (line =~ /-([0-9a-f]{32})/)
52
+ to_remove << $1
53
+ # MalwareHash.delete_all(["url = ?", $1])
54
+ removed += 1
55
+ end
56
+
57
+ if to_add.size > @batch_size
58
+ MalwareHash.add_batch(to_add)
59
+ to_add = []
60
+ end
61
+
62
+ if to_remove.size > @batch_size
63
+ MalwareHash.delete_batch(to_remove)
64
+ to_remove = []
65
+ end
66
+
67
+ end
68
+ end
69
+
70
+ MalwareHash.add_batch(to_add)
71
+ MalwareHash.delete_batch(to_remove)
72
+
73
+ options.save
74
+ logger.info("Updated malware info (in #{Time.now - started}s) +#{added}/-#{removed} (#{options.black_major}.#{options.black_minor} #{options.malware_major}.#{options.malware_minor})")
75
+ end
76
+
77
+ # Check a url against the malware database
78
+ # URLs can be in any form:
79
+ # * http://example.com/foo/bar.html
80
+ # * example.com/foo/
81
+ # * example.com
82
+ def self.check(url)
83
+ # Make sure url is according to http://code.google.com/apis/safebrowsing/developers_guide.html#ListFormat
84
+ url.gsub!(/^http:\/\//, '')
85
+ url.downcase!
86
+ alt_url = nil
87
+
88
+ if url =~ /\//
89
+ # Contains a /
90
+ url =~ /^([^\/]+\/)/
91
+ alt_url = $1
92
+ else
93
+ # Doesn't contain a slash -> hostname
94
+ url += "/"
95
+ end
96
+
97
+ if MalwareHash.check(url)
98
+ return true
99
+ elsif alt_url
100
+ return MalwareHash.check(alt_url)
101
+ else
102
+ return false
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ def delete_previous
109
+ Malware.delete_all
110
+ end
111
+ end
@@ -0,0 +1,22 @@
1
+ class MalwareHash < ActiveRecord::Base
2
+ validates_uniqueness_of :url
3
+ validates_length_of :url, :is => 32
4
+
5
+ def self.check(url)
6
+ not find(:first, :conditions => ["url = MD5( ? )", url]).nil?
7
+ end
8
+
9
+ def self.add_batch(urls)
10
+ return if urls.empty?
11
+
12
+ values = urls.map {|url| sanitize_sql(["(?)", url])}.join(',')
13
+ MalwareHash.connection.execute("INSERT INTO #{MalwareHash.table_name} (url) VALUES #{values}")
14
+ end
15
+
16
+ def self.delete_batch(urls)
17
+ return if urls.empty?
18
+
19
+ values = urls.map {|url| sanitize_sql(["?", url])}.join(',')
20
+ MalwareHash.connection.execute("DELETE FROM #{MalwareHash.table_name} WHERE url IN (#{values})")
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ class CreateMalwares < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :malwares do |t|
4
+ t.integer :black_major
5
+ t.integer :black_minor
6
+ t.integer :malware_major
7
+ t.integer :malware_minor
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+
13
+ def self.down
14
+ drop_table :malwares
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ class CreateMalwareHashes < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :malware_hashes do |t|
4
+ t.string :url, :limit => 32, :null => false
5
+ end
6
+ end
7
+
8
+ def self.down
9
+ drop_table :malware_hashes
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ # Malware
2
+
3
+ %w{ models }.each do |dir|
4
+ path = File.join(File.dirname(__FILE__), 'app', dir)
5
+ $LOAD_PATH << path
6
+ ActiveSupport::Dependencies.load_paths << path
7
+ ActiveSupport::Dependencies.load_once_paths.delete(path)
8
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "malware_api"
@@ -0,0 +1,14 @@
1
+ # namespace :db do
2
+ # namespace :migrate do
3
+ # description = "Migrate the database through scripts in vendor/plugins/malware/lib/db/migrate"
4
+ # description << "and update db/schema.rb by invoking db:schema:dump."
5
+ # description << "Target specific version with VERSION=x. Turn off output with VERBOSE=false."
6
+ #
7
+ # desc description
8
+ # task :malware => :environment do
9
+ # ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
10
+ # ActiveRecord::Migrator.migrate("vendor/plugins/malware/lib/db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
11
+ # Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
12
+ # end
13
+ # end
14
+ # end
data/test/database.yml ADDED
@@ -0,0 +1,6 @@
1
+ mysql:
2
+ :adapter: mysql
3
+ :host: localhost
4
+ :username: root
5
+ :password:
6
+ :database: malware_plugin_test
@@ -0,0 +1,10 @@
1
+ require 'test_helper'
2
+
3
+ class MalwareTest < ActiveSupport::TestCase
4
+ load_schema
5
+
6
+ test "loaded ok" do
7
+ assert_kind_of(Malware, Malware.new)
8
+ assert_kind_of(MalwareHash, MalwareHash.new)
9
+ end
10
+ end
data/test/schema.rb ADDED
@@ -0,0 +1,14 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table "malware_hashes", :force => true do |t|
3
+ t.string "url", :limit => 32, :null => false
4
+ end
5
+
6
+ create_table "malwares", :force => true do |t|
7
+ t.integer "black_major"
8
+ t.integer "black_minor"
9
+ t.integer "malware_major"
10
+ t.integer "malware_minor"
11
+
12
+ t.timestamps
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'active_support/test_case'
4
+
5
+ ENV['RAILS_ENV'] = 'test'
6
+ ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'
7
+
8
+ require 'test/unit'
9
+ require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb'))
10
+
11
+ def load_schema
12
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
13
+
14
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
15
+ db_adapter = ENV['DB'] || "mysql"
16
+
17
+ ActiveRecord::Base.establish_connection(config[db_adapter])
18
+ load(File.dirname(__FILE__) + "/schema.rb")
19
+ require File.dirname(__FILE__) + '/../rails/init.rb'
20
+ end
data/uninstall.rb ADDED
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: malware_api
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Jorge Bernal
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-26 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: koke@amedias.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.markdown
24
+ files:
25
+ - MIT-LICENSE
26
+ - README.markdown
27
+ - Rakefile
28
+ - generators/malware_migration/USAGE
29
+ - generators/malware_migration/malware_migration_generator.rb
30
+ - generators/malware_migration/templates/migration.rb
31
+ - install.rb
32
+ - lib/app/models/malware.rb
33
+ - lib/app/models/malware_hash.rb
34
+ - lib/db/migrate/20091225014508_create_malwares.rb
35
+ - lib/db/migrate/20091225014608_create_malware_hashes.rb
36
+ - lib/malware_api.rb
37
+ - rails/init.rb
38
+ - tasks/malware_tasks.rake
39
+ - test/database.yml
40
+ - test/malware_test.rb
41
+ - test/schema.rb
42
+ - test/test_helper.rb
43
+ - uninstall.rb
44
+ has_rdoc: true
45
+ homepage: http://github.com/koke/malware_api
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options: []
50
+
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.3.5
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Google Safe Browsing API for Rails
72
+ test_files:
73
+ - test/database.yml
74
+ - test/malware_test.rb
75
+ - test/schema.rb
76
+ - test/test_helper.rb