malware_api 0.1

Sign up to get free protection for your applications and to get access to all the features.
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