Compare commits

..

No commits in common. "master" and "v0.2.0" have entirely different histories.

13 changed files with 81 additions and 152 deletions

View File

@ -1,5 +1,4 @@
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
obtaining a copy of this software and associated documentation

View File

@ -1,40 +1,28 @@
= Project Honeypot
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.
== Requirements
= Requirements
This Gem requires that you have an Http:BL API key from Project Honeypot. You can get one at http://projecthhoneypot.org
== Configuration
ProjectHoneypot.configure do
@api_key = 'api_key'
@score = 42
@last_activity = 10
@offenses = [:comment_spammer, :suspicious, :harvester]
end
== Usage
require 'project_honeypot'
= Usage
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.
== Example #1: Suspicious IP Address
=== Example #1: Suspicious IP Address
Given an api key of "abcdefghijkl"
@listing = ProjectHoneypot.lookup("<ip_address>", "<api_key>")
@listing = ProjectHoneypot.lookup("abcdefghijkl", "192.168.1.1")
@listing.safe?
# => false
@listing.safe?(score: 64, last_activity: 10, offenses: [:comment_spammer])
# => true
@listing.ip_address
# => "192.168.1.1"
@ -56,12 +44,9 @@ The score is worse the higher it is and the last_activity is in days.
@listing.harvester?
# => false
@listing.search_engine?
# => false
== Example #2: Safe IP Address
=== Example #2: Safe IP Address
@listing = ProjectHoneypot.lookup("<ip_address>")
@listing = ProjectHoneypot.lookup("abcdefghijkl", "192.168.1.1")
@listing.safe?
# => true
@ -85,3 +70,9 @@ The score is worse the higher it is and the last_activity is in days.
@listing.harvester?
# => 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

View File

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

1
lib/project-honeypot.rb Normal file
View File

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

View File

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

View File

@ -1,10 +0,0 @@
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,6 +1,12 @@
module ProjectHoneypot
class Rack
class Forbidden < Rack
module ProjectHoneypot::Rack
class Forbidden
def initialize(app, options={})
@app = app
raise ArgumentError, 'Must specify an API key' unless options[:api_key]
ProjectHoneypot.api_key = options[:api_key]
end
def call(env)
request = ::Rack::Request.new(env)
url = ProjectHoneypot.lookup(request.ip)
@ -13,4 +19,3 @@ module ProjectHoneypot
end
end
end
end

View File

@ -1,6 +1,12 @@
module ProjectHoneypot
class Rack
class Header < Rack
module ProjectHoneypot::Rack
class Header
def initialize(app, options={})
@app = app
raise ArgumentError, 'Must specify an API key' unless options[:api_key]
ProjectHoneypot.api_key = options[:api_key]
end
def call(env)
request = ::Rack::Request.new(env)
url = ProjectHoneypot.lookup(request.ip)
@ -11,4 +17,3 @@ module ProjectHoneypot
end
end
end
end

View File

@ -1,22 +1,6 @@
module ProjectHoneypot
class Url
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
attr_reader :ip_address, :last_activity, :score, :offenses
def initialize(ip_address, honeypot_response)
@ip_address = ip_address
@safe = honeypot_response.nil?
@ -27,25 +11,14 @@ module ProjectHoneypot
score = hash[:score] || ProjectHoneypot.score
last_activity = hash[:last_activity] || ProjectHoneypot.last_activity
forbidden_offenses = hash[:offenses] ||
ProjectHoneypot.offenses ||
[:comment_spammer, :harvester, :suspicious]
detected_offenses = forbidden_offenses & @offenses
@safe ||
detected_offenses.length == 0 ||
!(
last_activity.nil? && score.nil? ||
!score.nil? && self.score >= score ||
!last_activity.nil? && self.last_activity >= last_activity
!score.nil? && self.score > score ||
!last_activity.nil? && self.last_activity > last_activity
)
end
def search_engine?
@offenses.include?(:search_engine)
end
def comment_spammer?
@offenses.include?(:comment_spammer)
end
@ -67,22 +40,11 @@ module ProjectHoneypot
@offenses = []
else
hp_array = honeypot_response.split(".")
if hp_array[3].to_i == 0
# 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
def set_offenses(offense_code)
offense_code = offense_code.to_i

View File

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

View File

@ -10,7 +10,7 @@ Gem::Specification.new do |s|
s.email = ["chuck@teachmetocode.com", "guillaume+github@dott.fr"]
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.homepage = "https://github.com/gdott9/project_honeypot"
s.homepage = ""
s.files = `git ls-files`.split($/)
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 'flexmock'
s.add_runtime_dependency 'net-dns', '~> 0.7.1'
s.add_runtime_dependency 'net-dns'
end

View File

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

View File

@ -2,51 +2,34 @@ require "spec_helper"
describe ProjectHoneypot::Url do
describe "with honeypot response" do
let(:url) { ProjectHoneypot::Url.new("127.0.0.1", "127.1.63.3") }
before(:each) do
@url = ProjectHoneypot::Url.new("teachmetocode.com", "127.1.63.3")
end
it "is safe" do
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
@url.should_not be_safe
end
it "has the correct latest activity" do
expect(url.last_activity).to eq(1)
@url.last_activity.should == 1
end
it "has the correct score" do
expect(url.score).to eq(63)
@url.score.should == 63
end
it "has the correct offenses" do
expect(url.offenses).to include(:suspicious)
expect(url.offenses).to include(:harvester)
expect(url.offenses).to_not include(:comment_spammer)
expect(url).to be_suspicious
expect(url).to be_harvester
expect(url).to_not be_comment_spammer
@url.offenses.should include(:suspicious)
@url.offenses.should include(:harvester)
@url.offenses.should_not include(:comment_spammer)
@url.should be_suspicious
@url.should be_harvester
@url.should_not be_comment_spammer
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
subject { ProjectHoneypot::Url.new("127.0.0.1", nil) }
subject { @url = ProjectHoneypot::Url.new("teachmetocode.com", nil) }
it { should be_safe }
end
end