Compare commits
No commits in common. "master" and "v0.2.0" have entirely different histories.
@ -1,5 +1,4 @@
|
|||||||
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
|
||||||
|
37
README.rdoc
37
README.rdoc
@ -1,40 +1,28 @@
|
|||||||
= 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
|
||||||
|
|
||||||
== Configuration
|
= Usage
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
=== 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?
|
@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"
|
||||||
|
|
||||||
@ -56,12 +44,9 @@ The score is worse the higher it is and the last_activity is in days.
|
|||||||
@listing.harvester?
|
@listing.harvester?
|
||||||
# => false
|
# => false
|
||||||
|
|
||||||
@listing.search_engine?
|
== Example #2: Safe IP Address
|
||||||
# => false
|
|
||||||
|
|
||||||
=== Example #2: Safe IP Address
|
@listing = ProjectHoneypot.lookup("abcdefghijkl", "192.168.1.1")
|
||||||
|
|
||||||
@listing = ProjectHoneypot.lookup("<ip_address>")
|
|
||||||
@listing.safe?
|
@listing.safe?
|
||||||
# => true
|
# => true
|
||||||
|
|
||||||
@ -85,3 +70,9 @@ The score is worse the higher it is and the last_activity is in days.
|
|||||||
|
|
||||||
@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
5
Rakefile
@ -1,5 +0,0 @@
|
|||||||
require 'rspec/core/rake_task'
|
|
||||||
|
|
||||||
RSpec::Core::RakeTask.new(:spec)
|
|
||||||
|
|
||||||
task :default => :spec
|
|
1
lib/project-honeypot.rb
Normal file
1
lib/project-honeypot.rb
Normal file
@ -0,0 +1 @@
|
|||||||
|
require "project_honeypot"
|
@ -1,13 +1,12 @@
|
|||||||
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, :offenses
|
attr_accessor :api_key, :score, :last_activity
|
||||||
|
|
||||||
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
|
||||||
@ -19,12 +18,12 @@ module ProjectHoneypot
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.lookup(url, api_key=nil)
|
def self.lookup(api_key_or_url, url=nil)
|
||||||
api_key ||= ProjectHoneypot.api_key
|
if url.nil?
|
||||||
|
url = api_key_or_url
|
||||||
raise ArgumentError, 'Must specify an API key' unless api_key
|
api_key_or_url = ProjectHoneypot.api_key
|
||||||
|
end
|
||||||
searcher = Base.new(api_key)
|
searcher = Base.new(api_key_or_url)
|
||||||
searcher.lookup(url)
|
searcher.lookup(url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -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
|
|
@ -1,6 +1,12 @@
|
|||||||
module ProjectHoneypot
|
module ProjectHoneypot::Rack
|
||||||
class Rack
|
class Forbidden
|
||||||
class Forbidden < 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
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
request = ::Rack::Request.new(env)
|
request = ::Rack::Request.new(env)
|
||||||
url = ProjectHoneypot.lookup(request.ip)
|
url = ProjectHoneypot.lookup(request.ip)
|
||||||
@ -12,5 +18,4 @@ module ProjectHoneypot
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
module ProjectHoneypot
|
module ProjectHoneypot::Rack
|
||||||
class Rack
|
class Header
|
||||||
class Header < 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
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
request = ::Rack::Request.new(env)
|
request = ::Rack::Request.new(env)
|
||||||
url = ProjectHoneypot.lookup(request.ip)
|
url = ProjectHoneypot.lookup(request.ip)
|
||||||
@ -10,5 +16,4 @@ module ProjectHoneypot
|
|||||||
@app.call(request.env)
|
@app.call(request.env)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -1,22 +1,6 @@
|
|||||||
module ProjectHoneypot
|
module ProjectHoneypot
|
||||||
class Url
|
class Url
|
||||||
SEARCH_ENGINES = %w(
|
attr_reader :ip_address, :last_activity, :score, :offenses
|
||||||
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?
|
||||||
@ -27,25 +11,14 @@ 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
|
||||||
@ -67,22 +40,11 @@ module ProjectHoneypot
|
|||||||
@offenses = []
|
@offenses = []
|
||||||
else
|
else
|
||||||
hp_array = honeypot_response.split(".")
|
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
|
@last_activity = hp_array[1].to_i
|
||||||
@score = hp_array[2].to_i
|
@score = hp_array[2].to_i
|
||||||
@offenses = set_offenses(hp_array[3])
|
@offenses = set_offenses(hp_array[3])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def set_offenses(offense_code)
|
def set_offenses(offense_code)
|
||||||
offense_code = offense_code.to_i
|
offense_code = offense_code.to_i
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
module ProjectHoneypot
|
module ProjectHoneypot
|
||||||
VERSION = "0.3.1"
|
VERSION = "0.2.0"
|
||||||
end
|
end
|
||||||
|
@ -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 = "https://github.com/gdott9/project_honeypot"
|
s.homepage = ""
|
||||||
|
|
||||||
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', '~> 0.7.1'
|
s.add_runtime_dependency 'net-dns'
|
||||||
end
|
end
|
||||||
|
@ -2,21 +2,20 @@ 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
|
||||||
expect(url).to be_a(ProjectHoneypot::Url)
|
url.last_activity.should == 1
|
||||||
expect(url.last_activity).to eq(1)
|
url.score.should == 63
|
||||||
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
|
||||||
|
@ -2,51 +2,34 @@ require "spec_helper"
|
|||||||
|
|
||||||
describe ProjectHoneypot::Url do
|
describe ProjectHoneypot::Url do
|
||||||
describe "with honeypot response" 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
|
it "is safe" do
|
||||||
expect(url.safe?).to be false
|
@url.should_not be_safe
|
||||||
|
|
||||||
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
|
||||||
expect(url.last_activity).to eq(1)
|
@url.last_activity.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
it "has the correct score" do
|
it "has the correct score" do
|
||||||
expect(url.score).to eq(63)
|
@url.score.should == 63
|
||||||
end
|
end
|
||||||
|
|
||||||
it "has the correct offenses" do
|
it "has the correct offenses" do
|
||||||
expect(url.offenses).to include(:suspicious)
|
@url.offenses.should include(:suspicious)
|
||||||
expect(url.offenses).to include(:harvester)
|
@url.offenses.should include(:harvester)
|
||||||
expect(url.offenses).to_not include(:comment_spammer)
|
@url.offenses.should_not include(:comment_spammer)
|
||||||
expect(url).to be_suspicious
|
@url.should be_suspicious
|
||||||
expect(url).to be_harvester
|
@url.should be_harvester
|
||||||
expect(url).to_not be_comment_spammer
|
@url.should_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 { ProjectHoneypot::Url.new("127.0.0.1", nil) }
|
subject { @url = ProjectHoneypot::Url.new("teachmetocode.com", nil) }
|
||||||
it { should be_safe }
|
it { should be_safe }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user