Compare commits

..

14 Commits

Author SHA1 Message Date
9533bf2c66 Replace should with expectations in specs 2015-05-27 11:27:00 +02:00
c4bde860e2 Add Rakefile to run tests 2015-05-27 11:26:43 +02:00
415f7c9d24 Add ProjectHoneypot::Rack class to DRY 2015-05-27 11:09:44 +02:00
707b115428 Update copyright information 2013-01-20 01:57:52 +01:00
15ea3a4f67 Bump version 0.3.1 2013-01-20 01:30:28 +01:00
a7e59ef5c8 Add github repo as homepage 2013-01-20 01:30:00 +01:00
6bb140e738 Bump version 0.3.0 2013-01-20 01:28:32 +01:00
2870baff6e Inverse api_key and ip_address arguments in lookup method 2013-01-20 01:27:37 +01:00
Guillaume DOTT
56166bbb3c Add version requirement for dependencies 2013-01-15 09:17:12 +01:00
Guillaume DOTT
5585e5d92e Update README 2013-01-14 17:13:07 +01:00
Guillaume DOTT
a1a7beb464 Improve tests rspec syntax 2013-01-14 14:30:07 +01:00
Guillaume DOTT
2f87b63c18 Test safe? method and search engine type 2013-01-14 10:07:55 +01:00
Guillaume DOTT
e0b488868a Add custom offenses list to determine if safe or not 2013-01-04 17:22:00 +01:00
Guillaume DOTT
1a3eeaf4cf Add search engines support 2013-01-04 17:20:05 +01:00
13 changed files with 152 additions and 81 deletions

View File

@ -1,4 +1,5 @@
Copyright (c) 2010 Charles Max Wood chuck@teachmetocode.com Copyright (c) 2010 Charles Max Wood chuck@teachmetocode.com
Copyright (c) 2013 Guillaume DOTT guillaume+github@dott.fr
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -19,4 +20,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE. OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,28 +1,40 @@
= Project Honeypot = Project Honeypot
Project Honeypot is a programmatic interface to the Project Honeypot HTTP:BL service for identifying suspicious ip addresses. Project Honeypot is a programmatic interface to the Project Honeypot HTTP:BL service for identifying suspicious ip addresses.
This Gem was built to filter out spammers on http://www.tweetburner.com.
It is a handy thing to be able to identify spammers, harvesters, and other suspicious IP addresses if you're worried about who might be abusing your service. It is a handy thing to be able to identify spammers, harvesters, and other suspicious IP addresses if you're worried about who might be abusing your service.
= Requirements == Requirements
This Gem requires that you have an Http:BL API key from Project Honeypot. You can get one at http://projecthhoneypot.org This Gem requires that you have an Http:BL API key from Project Honeypot. You can get one at http://projecthhoneypot.org
= Usage == Configuration
ProjectHoneypot.configure do
@api_key = 'api_key'
@score = 42
@last_activity = 10
@offenses = [:comment_spammer, :suspicious, :harvester]
end
== Usage
require 'project_honeypot'
HTTP:BL lookups through Project Honeypot result in a Url object that gives you the risk score, last activity, and types of offenses the ip address is listed for. HTTP:BL lookups through Project Honeypot result in a Url object that gives you the risk score, last activity, and types of offenses the ip address is listed for.
The score is worse the higher it is and the last_activity is in days. The score is worse the higher it is and the last_activity is in days.
== Example #1: Suspicious IP Address
Given an api key of "abcdefghijkl" === Example #1: Suspicious IP Address
@listing = ProjectHoneypot.lookup("abcdefghijkl", "192.168.1.1") @listing = ProjectHoneypot.lookup("<ip_address>", "<api_key>")
@listing.safe? @listing.safe?
# => false # => false
@listing.safe?(score: 64, last_activity: 10, offenses: [:comment_spammer])
# => true
@listing.ip_address @listing.ip_address
# => "192.168.1.1" # => "192.168.1.1"
@ -44,9 +56,12 @@ Given an api key of "abcdefghijkl"
@listing.harvester? @listing.harvester?
# => false # => false
== Example #2: Safe IP Address @listing.search_engine?
# => false
@listing = ProjectHoneypot.lookup("abcdefghijkl", "192.168.1.1") === Example #2: Safe IP Address
@listing = ProjectHoneypot.lookup("<ip_address>")
@listing.safe? @listing.safe?
# => true # => true
@ -70,9 +85,3 @@ Given an api key of "abcdefghijkl"
@listing.harvester? @listing.harvester?
# => false # => false
= To Do Items
- Cache Responses from Project Honeypot
- Allow 'safe?' to be configurable (algorithm based on recency and severity(score))
- A .yml config file

5
Rakefile Normal file
View File

@ -0,0 +1,5 @@
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
task :default => :spec

View File

@ -1 +0,0 @@
require "project_honeypot"

View File

@ -1,12 +1,13 @@
require 'net/dns' require 'net/dns'
require "project_honeypot/url" require "project_honeypot/url"
require "project_honeypot/base" require "project_honeypot/base"
require "project_honeypot/rack"
require "project_honeypot/rack/header" require "project_honeypot/rack/header"
require "project_honeypot/rack/forbidden" require "project_honeypot/rack/forbidden"
module ProjectHoneypot module ProjectHoneypot
class << self class << self
attr_accessor :api_key, :score, :last_activity attr_accessor :api_key, :score, :last_activity, :offenses
def api_key def api_key
raise "ProjectHoneypot really needs its api_key set to work" unless @api_key raise "ProjectHoneypot really needs its api_key set to work" unless @api_key
@ -18,12 +19,12 @@ module ProjectHoneypot
end end
end end
def self.lookup(api_key_or_url, url=nil) def self.lookup(url, api_key=nil)
if url.nil? api_key ||= ProjectHoneypot.api_key
url = api_key_or_url
api_key_or_url = ProjectHoneypot.api_key raise ArgumentError, 'Must specify an API key' unless api_key
end
searcher = Base.new(api_key_or_url) searcher = Base.new(api_key)
searcher.lookup(url) searcher.lookup(url)
end end
end end

View File

@ -0,0 +1,10 @@
module ProjectHoneypot
class Rack
def initialize(app, options={})
@app = app
raise ArgumentError, 'Must specify an API key' unless options[:api_key]
ProjectHoneypot.api_key = options[:api_key]
end
end
end

View File

@ -1,20 +1,15 @@
module ProjectHoneypot::Rack module ProjectHoneypot
class Forbidden class Rack
def initialize(app, options={}) class Forbidden < Rack
@app = app def call(env)
request = ::Rack::Request.new(env)
url = ProjectHoneypot.lookup(request.ip)
raise ArgumentError, 'Must specify an API key' unless options[:api_key] if url.safe?
ProjectHoneypot.api_key = options[:api_key] @app.call(request.env)
end else
[403, {"Content-Type" => "text/html"}, ["Forbidden"]]
def call(env) end
request = ::Rack::Request.new(env)
url = ProjectHoneypot.lookup(request.ip)
if url.safe?
@app.call(request.env)
else
[403, {"Content-Type" => "text/html"}, ["Forbidden"]]
end end
end end
end end

View File

@ -1,19 +1,14 @@
module ProjectHoneypot::Rack module ProjectHoneypot
class Header class Rack
def initialize(app, options={}) class Header < Rack
@app = app def call(env)
request = ::Rack::Request.new(env)
url = ProjectHoneypot.lookup(request.ip)
raise ArgumentError, 'Must specify an API key' unless options[:api_key] env['PROJECT_HONEYPOT_SAFE'] = url.safe?
ProjectHoneypot.api_key = options[:api_key]
end
def call(env) @app.call(request.env)
request = ::Rack::Request.new(env) end
url = ProjectHoneypot.lookup(request.ip)
env['PROJECT_HONEYPOT_SAFE'] = url.safe?
@app.call(request.env)
end end
end end
end end

View File

@ -1,6 +1,22 @@
module ProjectHoneypot module ProjectHoneypot
class Url class Url
attr_reader :ip_address, :last_activity, :score, :offenses SEARCH_ENGINES = %w(
Undocumented
AltaVista
Ask
Baidu
Excite
Google
Looksmart
Lycos
MSN
Yahoo
Cuil
InfoSeek
Miscellaneous
)
attr_reader :ip_address, :last_activity, :score, :offenses, :search_engine
def initialize(ip_address, honeypot_response) def initialize(ip_address, honeypot_response)
@ip_address = ip_address @ip_address = ip_address
@safe = honeypot_response.nil? @safe = honeypot_response.nil?
@ -11,14 +27,25 @@ module ProjectHoneypot
score = hash[:score] || ProjectHoneypot.score score = hash[:score] || ProjectHoneypot.score
last_activity = hash[:last_activity] || ProjectHoneypot.last_activity last_activity = hash[:last_activity] || ProjectHoneypot.last_activity
forbidden_offenses = hash[:offenses] ||
ProjectHoneypot.offenses ||
[:comment_spammer, :harvester, :suspicious]
detected_offenses = forbidden_offenses & @offenses
@safe || @safe ||
detected_offenses.length == 0 ||
!( !(
last_activity.nil? && score.nil? || last_activity.nil? && score.nil? ||
!score.nil? && self.score > score || !score.nil? && self.score >= score ||
!last_activity.nil? && self.last_activity > last_activity !last_activity.nil? && self.last_activity >= last_activity
) )
end end
def search_engine?
@offenses.include?(:search_engine)
end
def comment_spammer? def comment_spammer?
@offenses.include?(:comment_spammer) @offenses.include?(:comment_spammer)
end end
@ -40,9 +67,20 @@ module ProjectHoneypot
@offenses = [] @offenses = []
else else
hp_array = honeypot_response.split(".") hp_array = honeypot_response.split(".")
@last_activity = hp_array[1].to_i
@score = hp_array[2].to_i if hp_array[3].to_i == 0
@offenses = set_offenses(hp_array[3]) # search engine
@last_activity = nil
@score = 0
@offenses = [:search_engine]
@search_engine = (hp_array[2].to_i < SEARCH_ENGINES.length ?
SEARCH_ENGINES[hp_array[2].to_i] :
SEARCH_ENGINES[0])
else
@last_activity = hp_array[1].to_i
@score = hp_array[2].to_i
@offenses = set_offenses(hp_array[3])
end
end end
end end

View File

@ -1,3 +1,3 @@
module ProjectHoneypot module ProjectHoneypot
VERSION = "0.2.0" VERSION = "0.3.1"
end end

View File

@ -10,7 +10,7 @@ Gem::Specification.new do |s|
s.email = ["chuck@teachmetocode.com", "guillaume+github@dott.fr"] s.email = ["chuck@teachmetocode.com", "guillaume+github@dott.fr"]
s.summary = %q{Project-Honeypot provides a programatic interface to the Project Honeypot services.} s.summary = %q{Project-Honeypot provides a programatic interface to the Project Honeypot services.}
s.description = %q{Project-Honeypot provides a programatic interface to the Project Honeypot services. It can be used to identify spammers, bogus commenters, and harvesters. You will need a FREE api key from http://projecthoneypot.org} s.description = %q{Project-Honeypot provides a programatic interface to the Project Honeypot services. It can be used to identify spammers, bogus commenters, and harvesters. You will need a FREE api key from http://projecthoneypot.org}
s.homepage = "" s.homepage = "https://github.com/gdott9/project_honeypot"
s.files = `git ls-files`.split($/) s.files = `git ls-files`.split($/)
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) } s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
@ -19,5 +19,5 @@ Gem::Specification.new do |s|
s.add_development_dependency 'rspec' s.add_development_dependency 'rspec'
s.add_development_dependency 'flexmock' s.add_development_dependency 'flexmock'
s.add_runtime_dependency 'net-dns' s.add_runtime_dependency 'net-dns', '~> 0.7.1'
end end

View File

@ -2,20 +2,21 @@ require "spec_helper"
describe ProjectHoneypot::Base do describe ProjectHoneypot::Base do
describe "with honeypot response" do describe "with honeypot response" do
let(:base) { ProjectHoneypot::Base.new("abcdefghijklmnop") }
before(:each) do before(:each) do
flexmock(Net::DNS::Resolver, :start => flexmock("answer", :answer => ["somedomain.httpbl.org A Name 127.1.63.5"])) flexmock(Net::DNS::Resolver, :start => flexmock("answer", :answer => ["somedomain.httpbl.org A Name 127.1.63.5"]))
@base = ProjectHoneypot::Base.new("abcdefghijklmnop")
end end
it "returns a Url object" do it "returns a Url object" do
url = @base.lookup("127.10.10.5") url = base.lookup("127.10.10.5")
url.should be_a ProjectHoneypot::Url
url.last_activity.should == 1 expect(url).to be_a(ProjectHoneypot::Url)
url.score.should == 63 expect(url.last_activity).to eq(1)
expect(url.score).to eq(63)
end end
it "looks up non-ip addresses" do it "looks up non-ip addresses" do
url = @base.lookup("iamspam.com") url = base.lookup("iamspam.com")
Net::DNS::Resolver.should_receive(:start).with("iamspam.com") Net::DNS::Resolver.should_receive(:start).with("iamspam.com")
end end
end end

View File

@ -2,34 +2,51 @@ require "spec_helper"
describe ProjectHoneypot::Url do describe ProjectHoneypot::Url do
describe "with honeypot response" do describe "with honeypot response" do
before(:each) do let(:url) { ProjectHoneypot::Url.new("127.0.0.1", "127.1.63.3") }
@url = ProjectHoneypot::Url.new("teachmetocode.com", "127.1.63.3")
end
it "is safe" do it "is safe" do
@url.should_not be_safe expect(url.safe?).to be false
expect(url.safe?(score: 63)).to be false
expect(url.safe?(score: 64)).to be true
expect(url.safe?(last_activity: 1)).to be false
expect(url.safe?(last_activity: 2)).to be true
expect(url.safe?(last_activity: 2, score: 64)).to be true
expect(url.safe?(last_activity: 1, score: 64)).to be false
expect(url.safe?(last_activity: 2, score: 63)).to be false
expect(url.safe?(offenses: [:comment_spammer])).to be true
expect(url.safe?(offenses: [:suspicious, :comment_spammer])).to be false
end end
it "has the correct latest activity" do it "has the correct latest activity" do
@url.last_activity.should == 1 expect(url.last_activity).to eq(1)
end end
it "has the correct score" do it "has the correct score" do
@url.score.should == 63 expect(url.score).to eq(63)
end end
it "has the correct offenses" do it "has the correct offenses" do
@url.offenses.should include(:suspicious) expect(url.offenses).to include(:suspicious)
@url.offenses.should include(:harvester) expect(url.offenses).to include(:harvester)
@url.offenses.should_not include(:comment_spammer) expect(url.offenses).to_not include(:comment_spammer)
@url.should be_suspicious expect(url).to be_suspicious
@url.should be_harvester expect(url).to be_harvester
@url.should_not be_comment_spammer expect(url).to_not be_comment_spammer
end end
end end
describe "with search engine honeypot response" do
subject { ProjectHoneypot::Url.new("127.0.0.1", "127.0.9.0") }
it { should be_safe }
it { should be_search_engine }
end
describe "with nil honeypot response" do describe "with nil honeypot response" do
subject { @url = ProjectHoneypot::Url.new("teachmetocode.com", nil) } subject { ProjectHoneypot::Url.new("127.0.0.1", nil) }
it { should be_safe } it { should be_safe }
end end
end end