├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── test
└── honeypot.js
└── honeypot.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Piotr Rochala
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | honeypot
2 | =========
3 |
4 | Node.js implementation of the Project Honeypot (Http:BL) API. Because we all hate spam.
5 |
6 | - Utilizes Http:BL from known and loved https://www.projecthoneypot.org/
7 | - Uses built-in node dns.resolve4 to get response from the DNS API
8 | - No Unicorns were harmed during development
9 |
10 | Installation
11 | --------------
12 |
13 | ```sh
14 | npm install honeypot
15 | ```
16 |
17 | Usage
18 | ----
19 | ```javascript
20 | var honeypot = require('honeypot');
21 |
22 | var pot = new honeypot('your_api_key');
23 |
24 | pot.query('127.0.0.1', function(err, response){
25 | if (!response) {
26 | console.log("IP not found in honeypot, we're all good!");
27 | } else {
28 | console.log("Oh no, it's a spammer mate! Kil it with fire!");
29 | console.log(response.getFormattedResponse());
30 | // Suspicious, Comment Spammer
31 | // Threat Rating: 58 / 255
32 | // Recency: 1 / 255
33 | }
34 | });
35 | ```
36 |
37 | Example within Express
38 | ----
39 |
40 | ```javascript
41 | var honeypot = require('honeypot');
42 |
43 | var pot = new honeypot('your_api_key');
44 |
45 | // example route for POST /comment/
46 | create: function(req, res) {
47 |
48 | pot.query(req.ip, function(err, response){
49 | if (!response) {
50 | console.log("IP not found in honeypot, we're all good!");
51 | // do some commentary magic
52 | res.send({ msg: 'we hate spam!' });
53 | } else {
54 | console.log("Die!");
55 | res.send(null);
56 | }
57 | });
58 | }
59 | ```
60 |
61 | Kudos
62 | ----
63 |
64 | Based on this sweet PHP gist https://gist.github.com/smithweb/7773373.
65 |
66 |
67 | License
68 | ----
69 |
70 | MIT
71 |
72 | **Free Software, Hell Yeah!**
73 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "honeypot",
3 | "version": "0.0.2",
4 | "description": "Node.js implementation of the Project Honeypot (Http:BL) API. Because we all hate spam.",
5 | "keywords": [
6 | "honeypot",
7 | "spam",
8 | "honey",
9 | "Http:BL",
10 | "ip",
11 | "spammer"
12 | ],
13 | "author": {
14 | "name": "Piotr Rochala",
15 | "email": "piotr.rochala@gmail.com",
16 | "url": "http://rocha.la/"
17 | },
18 | "dependencies": {},
19 | "devDependencies": {
20 | "mocha": "*",
21 | "should": "*"
22 | },
23 | "engines": {
24 | "node": "*"
25 | },
26 | "repository": {
27 | "type": "git",
28 | "url": "https://github.com/rochal/honeypot.git"
29 | },
30 | "bugs": {
31 | "url": "https://github.com/rochal/honeypot/issues"
32 | },
33 | "licenses": [
34 | {
35 | "type": "MIT"
36 | }
37 | ],
38 | "main": "honeypot",
39 | "readme": "honeypot\n=========\n\nNode.js implementation of the Project Honeypot (Http:BL) API. Because we all hate spam.\n\n - Utilizes Http:BL from known and loved https://www.projecthoneypot.org/\n - Uses built-in node dns.resolve4 to get response from the DNS API\n - No Unicorns were harmed during development\n\nInstallation\n--------------\n\n```sh\nnpm install honeypot\n```\n\nUsage\n----\n```javascript\nvar honeypot = require('honeypot');\n\nvar pot = new honeypot('your_api_key');\n\npot.query('127.0.0.1', function(err, response){\n if (!response) {\n console.log(\"IP not found in honeypot, we're all good!\");\n } else {\n console.log(\"Oh no, it's a spammer mate! Kil it with fire!\");\n console.log(response.getFormattedResponse());\n // Suspicious, Comment Spammer\n // Threat Rating: 58 / 255\n // Recency: 1 / 255\n }\n});\n```\n\nExample within Express\n----\n\n```javascript\nvar honeypot = require('honeypot');\n\nvar pot = new honeypot('your_api_key');\n\n// example route for POST /comment/\ncreate: function(req, res) {\n \n pot.query(req.ip, function(err, response){\n if (!response) {\n console.log(\"IP not found in honeypot, we're all good!\");\n // do some commentary magic\n res.send({ msg: 'we hate spam!' });\n } else {\n console.log(\"Die!\");\n res.send(null);\n }\n });\n}\n```\n\nKudos\n----\n\nBased on this sweet PHP gist https://gist.github.com/smithweb/7773373.\n\n\nLicense\n----\n\nMIT\n\n**Free Software, Hell Yeah!**\n\n",
40 | "readmeFilename": "Readme.md",
41 | "_id": "honeypot@0.0.2",
42 | "_from": "honeypot@0.0.2",
43 | "scripts": {}
44 | }
45 |
--------------------------------------------------------------------------------
/test/honeypot.js:
--------------------------------------------------------------------------------
1 |
2 | var should = require('should'),
3 | honeypot = require('../honeypot');
4 |
5 | var pot = new honeypot('api_key');
6 |
7 | describe('honeypot', function(){
8 |
9 | it('honeypot object should be initialized', function(){
10 | should(pot).be.ok;
11 | should(pot.getRawResponse()).be.ok;
12 | should(pot.getRawResponse().length).equal(0);
13 | });
14 |
15 | it('honeypot should parse response for bunch of invalid IPs', function(){
16 |
17 | var ip_response = {
18 | '194.90.36.155' : { // harvester
19 | response: [127, 1, 36, 3],
20 | visitor: 3,
21 | formattedVisitor: 'Suspicious, Harvester',
22 | threat: 36,
23 | recency: 1,
24 | isSearch: false
25 | },
26 | '91.207.7.165' : { // spam server
27 | response: [127, 1, 58, 5],
28 | visitor: 5,
29 | formattedVisitor: 'Suspicious, Comment Spammer',
30 | threat: 58,
31 | recency: 1,
32 | isSearch: false
33 | },
34 | '72.22.73.25' : { // bad web host
35 | response: [127, 1, 38, 1],
36 | visitor: 1,
37 | formattedVisitor: 'Suspicious',
38 | threat: 38,
39 | recency: 1,
40 | isSearch: false
41 | },
42 | '62.210.123.137': { // comment spammer
43 | response: [127, 1, 45, 5],
44 | visitor: 5,
45 | formattedVisitor: 'Suspicious, Comment Spammer',
46 | threat: 45,
47 | recency: 1,
48 | isSearch: false
49 | },
50 | '121.78.126.228': { // dictionary attack
51 | response: [127, 1, 56, 1],
52 | visitor: 1,
53 | formattedVisitor: 'Suspicious',
54 | threat: 56,
55 | recency: 1,
56 | isSearch: false
57 | },
58 | '123.164.66.39' : { // rule breaker
59 | response: [127, 1, 47, 5],
60 | visitor: 5,
61 | formattedVisitor: 'Suspicious, Comment Spammer',
62 | threat: 47,
63 | recency: 1,
64 | isSearch: false
65 | },
66 | '157.56.93.85' : { // crawler
67 | response: [127, 0, 8, 0],
68 | visitor: 0,
69 | formattedVisitor: 'Search Engine Bot (MSN)',
70 | threat: 8,
71 | recency: 0,
72 | isSearch: true
73 | }
74 | }
75 |
76 | // simulate response
77 | for (var i in ip_response) {
78 | var value = ip_response[i];
79 | pot.setRawResponse(value.response);
80 |
81 | should(pot.isListed()).equal(true);
82 | should(pot.getVisitorType()).equal(value.visitor);
83 | should(pot.getFormattedVisitorType()).equal(value.formattedVisitor);
84 | should(pot.getThreatRating()).equal(value.threat);
85 | should(pot.getRecency()).equal(value.recency);
86 | should(pot.isSearchEngine()).equal(value.isSearch);
87 | }
88 |
89 | });
90 |
91 | /**
92 | * NOTE: Tests below are performing actual dns call
93 | * - verify that IP is still classified as expected in test
94 | * https://www.projecthoneypot.org/ip_194.90.36.155
95 | * https://www.projecthoneypot.org/ip_91.207.7.165
96 | * - ensure api_key is updated
97 | */
98 |
99 | // it('should do actual DNS call', function(done) {
100 | // pot.query('194.90.36.155', function(err, data) {
101 | // should(pot.isListed()).equal(true);
102 | // should(pot.getFormattedVisitorType()).equal('Suspicious, Harvester');
103 | // done();
104 | // });
105 | // });
106 |
107 | it('should do actual DNS call', function(done) {
108 | pot.query('91.207.7.165', function(err, data) {
109 | should(pot.isListed()).equal(true);
110 | should(pot.getFormattedVisitorType()).equal('Suspicious, Comment Spammer');
111 | console.log(pot.getFormattedResponse());
112 | done();
113 | });
114 | });
115 |
116 | })
--------------------------------------------------------------------------------
/honeypot.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Node Honeypot 2.3.0
4 | * Copyright 2014 Piotr Rochala
5 | * Based on original PHP class by smithweb
6 | * Available under MIT license
7 | */
8 | var dns = require('dns');
9 |
10 | module.exports = function(key) {
11 |
12 | var api_key = key;
13 |
14 | var visitor_type = {
15 | 0: 'Search Engine Bot',
16 | 1: 'Suspicious',
17 | 2: 'Harvester',
18 | 3: 'Suspicious, Harvester',
19 | 4: 'Comment Spammer',
20 | 5: 'Suspicious, Comment Spammer',
21 | 6: 'Harvester, Comment Spammer',
22 | 7: 'Suspicious, Harvester, Comment Spammer'
23 | };
24 |
25 | var search_engine = {
26 | 0: 'Undocumented',
27 | 1: 'AltaVista',
28 | 2: 'Ask',
29 | 3: 'Baidu',
30 | 4: 'Excite',
31 | 5: 'Google',
32 | 6: 'Looksmart',
33 | 7: 'Lycos',
34 | 8: 'MSN',
35 | 9: 'Yahoo',
36 | 10: 'Cuil',
37 | 11: 'InfoSeek',
38 | 12: 'Miscellaneous'
39 | };
40 |
41 | // Raw Response from http:BL query
42 | var _response = [];
43 |
44 | /**
45 | * Performs query of the httpBL service, using a DNS Query.
46 | *
47 | * See http://www.projecthoneypot.org/httpbl_api.php for
48 | * information on proper format and possible responses.
49 | *
50 | */
51 | this.query = function(ip, callback) {
52 |
53 | var reversed_ip = ip.split('.').reverse().join('.')
54 |
55 | dns.resolve4([api_key, reversed_ip, 'dnsbl.httpbl.org'].join('.'), function(err, data) {
56 | if (data) {
57 | _response = data.toString().split('.').map(Number);
58 | callback(null, data);
59 | } else {
60 | callback(err, null);
61 | }
62 | })
63 | }
64 |
65 | /**
66 | * Checks if the ip address was listed in the httpBL
67 | *
68 | * @return bool True if listed, False if not listed
69 | */
70 | this.isListed = function() {
71 | if (_response[0] === 127) {
72 | return true;
73 | }
74 | return false;
75 | }
76 |
77 | /**
78 | * Returns vistor type as integer
79 | *
80 | * @return int|bool Vistor type or false if not in httBL
81 | */
82 | this.getVisitorType = function() {
83 | if (this.isListed()) {
84 | return _response[3];
85 | }
86 | return false;
87 | }
88 |
89 | /**
90 | * Returns string containing a text description of the visitor type
91 | *
92 | * @return string|bool Visitor type if listed in httpBL, false if not
93 | */
94 | this.getFormattedVisitorType = function() {
95 | if (this.isListed()) {
96 | if (_response[3] === 0) {
97 | return visitor_type[_response[3]] + ' (' + search_engine[_response[2]] + ')';
98 | } else {
99 | return visitor_type[_response[3]];
100 | }
101 | } else {
102 | return false;
103 | }
104 | }
105 |
106 | /**
107 | * Gets the threat rating for an ip address if it is listed in the httpBL.
108 | *
109 | * @return int Threat score (out of a possible 255)
110 | */
111 | this.getThreatRating = function() {
112 | if (this.isListed()) {
113 | return _response[2];
114 | }
115 | return 0;
116 | }
117 |
118 | /**
119 | * Gets the number of days since an event was tracked for an ip address
120 | * if it is listed in the httpBL.
121 | *
122 | * @return int Number of days since most recent event (up to max of 255)
123 | */
124 | this.getRecency = function() {
125 | if (this.isListed()) {
126 | return _response[1];
127 | }
128 | return 0;
129 | }
130 |
131 | /**
132 | * Checks whether the ip address belongs to a search engine bot or company
133 | *
134 | * @return boolean True of ip belongs to search engine, false if not
135 | */
136 | this.isSearchEngine = function() {
137 | if (this.isListed() && _response[3] === 0) {
138 | return true;
139 | }
140 | return false;
141 | }
142 |
143 | /**
144 | * @return Array containing response details
145 | */
146 | this.getRawResponse = function() {
147 | return _response;
148 | }
149 |
150 | /**
151 | * Sets raw response, useful for testing
152 | */
153 | this.setRawResponse = function(value) {
154 | _response = value;
155 | }
156 |
157 | /*
158 | * Returns a formatted message with details about the IP address
159 | *
160 | * @param string format type of output for the response, text or html
161 | * @return string Formatted string of response info
162 | */
163 | this.getFormattedResponse = function(format) {
164 |
165 | if (!format) {
166 | format = 'text';
167 | }
168 |
169 | var line_end = "\n";
170 | var output = '';
171 |
172 | if (format == 'html') {
173 | line_end = "
\n";
174 | }
175 |
176 | if (this.isListed()) {
177 | output += this.getFormattedVisitorType() + line_end;
178 | if (!this.isSearchEngine()) {
179 | output += "Threat Rating: " + this.getThreatRating() + " / 255" + line_end;
180 | output += "Recency: " + this.getRecency() + " / 255" + line_end;
181 | }
182 | }
183 |
184 | return output;
185 | }
186 | };
187 |
--------------------------------------------------------------------------------