├── .gitignore ├── index.js ├── package.json ├── README.md ├── test └── api-spec.js └── lib └── safe_browse.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/* 3 | test/config.js 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | exports = module.exports = require('./lib/safe_browse'); 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Arnab Chakraborty (http://arnab.ch)", 3 | "name": "safe-browse", 4 | "description": "A Node.js module to verify whether a URL is a malware or phishing website, using Google Safe Browsing API", 5 | "version": "1.0.1", 6 | "keywords": ["safe browse", "browse safe", "safe", "phishing", "malware"], 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/arnabc/node-safe-browse.git" 10 | }, 11 | "main": "./lib/safe-browse.js", 12 | "engines": { 13 | "node": ">=v0.6.0" 14 | }, 15 | "dependencies": { 16 | "underscore": "latest", 17 | "request": ">=2.9.203" 18 | }, 19 | "devDependencies": { 20 | "vows": ">=0.6.3" 21 | }, 22 | 23 | "license": "MIT" 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Node Safe Browse -- Utility module to check URLs against Google's SafeBrowsing Lookup API 2 | ================ 3 | 4 | The [SafeBrowsing Lookup API v3](https://developers.google.com/safe-browsing/v3/) allows applications to check malicious URLs against Google's constantly updated list of malware and phishing websites/pages. 5 | 6 | Install 7 | --------- 8 | 9 | Using `npm` 10 | 11 | ``` 12 | npm install safe-browse 13 | ``` 14 | 15 | or from source 16 | 17 | ``` 18 | git clone git://github.com/arnabc/node-safe-browse.git 19 | cd node-safe-browse 20 | npm link 21 | ``` 22 | 23 | Usage 24 | ---------- 25 | 26 | In order to use the module you need to sign up for an `API_KEY` at the [Google Developers Console](https://console.developers.google.com). 27 | 28 | 29 | ```javascript 30 | // initialize 31 | SafeBrowse = require('safe-browse'); 32 | var api = new SafeBrowse.Api( API_KEY, options /* optional */ ); 33 | ``` 34 | 35 | By default the `lookup()` method returns an `EventEmitter` object which you can use to bind to `success` and `error` events respectively. 36 | 37 | ```javascript 38 | api.lookup('http://twitter.com') 39 | .on( 'success', function ( data ) { 40 | // handle success 41 | } ) 42 | .on( 'error', function ( error ) { 43 | // handle error 44 | } ); 45 | ``` 46 | 47 | or you can also use a `callback function` as the second argument like this: 48 | 49 | ```javascript 50 | api.lookup(['http://twitter.com', 'http://gumblar.cn'], function ( error, data ) { 51 | // handle data 52 | } ); 53 | ``` 54 | 55 | #### SafeBrowse Options 56 | 57 | * `appver` - Optional, the version number of the application, default is the version number of the `safe-browse` module. 58 | * `pver` - Google SafeBrowsing API protocol version, you can change this if Google updates their protocol version number. The current version is `3.1` (but there is also a completely re-designed [v4](https://developers.google.com/safe-browsing/v4/)). 59 | * `debug` - Debug flag (Boolean), enabling this will output some helppful logging messages in `Console`. 60 | * `api` - The URL of the Google SafeBrowsing API, in case it changes you can use the new API url to initialize the module without changing anything in the module code. 61 | 62 | 63 | Response Handling 64 | ----------------- 65 | 66 | In case of `success` the result data contains a map with the specified URL as the _key_ and corresponding result text as the value (as received from the API). 67 | 68 | ```javascript 69 | api.lookup('http://google.com') 70 | 71 | // will have the output like below: 72 | { 73 | statusCode: 204, 74 | data: { 75 | 'http://google.com': 'ok' // it could be anything like ok|malware|phishing|phishing,malware 76 | } 77 | } 78 | ``` 79 | 80 | For multiple requests 81 | 82 | To check multiple requests at once, provide an array of _valid_ URLs to the `lookup()` method. 83 | 84 | ```javascript 85 | api.lookup(['http://google.com', 'http://gumblar.cn']) 86 | 87 | // will have the output like below: 88 | { 89 | statusCode: 200, 90 | data: { 91 | 'http://google.com': 'ok', 92 | 'http://gumblar.cn': 'malware' 93 | } 94 | } 95 | ``` 96 | 97 | Response Status Codes 98 | ----------------- 99 | 100 | The following are the HTTP status codes that Google SafeBrowsing Lookup API returns for GET or POST request: 101 | 102 | #### GET Requests 103 | 104 | * `200` - The queried URL is either phishing, malware or both, see the response body for the specific type. 105 | * `204` - The requested URL is legitimate, no response body returned. 106 | * `400` - Bad Request — The HTTP request was not correctly formed. 107 | * `401` - Not Authorized — The apikey is not authorized. 108 | * `503` - Service Unavailable — The server cannot handle the request. Besides the normal server failures, it could also indicate that the client has been **throttled** by sending too many requests. 109 | 110 | Possible reasons for the Bad Request (HTTP code 400): 111 | 112 | * Not all the required CGI parameters are specified 113 | * Some of the CGI parameters are empty 114 | * The queried URL is not a valid URL or not properly encoded 115 | 116 | Be sure to check against `503`, if you get that back off for sometime (the documentation does not specify whether to exponentially back-off or not) and retry again. 117 | 118 | #### POST Requests 119 | 120 | If you provide multiple URLs to check against the SafeBrowsing API, `safe-browse` module automatically uses HTTP POST. The maximum number of URLs that you can check at once is `500`. 121 | 122 | * `200` - AT LEAST ONE of the queried URLs are matched in either the phishing or malware lists, the actual results are returned through the response body. 123 | * `204` - NONE of the queried URLs matched the phishing or malware lists, no response body returned. 124 | * `400` - Bad Request — The HTTP request was not correctly formed. 125 | * `401` - Not Authorized — The apikey is not authorized. 126 | * `503` - Service Unavailable — The server cannot handle the request. Besides the normal server failures, it could also indicate that the client has been **throttled** by sending too many requests. 127 | 128 | 129 | Possible reasons for the Bad Request (HTTP code 400): 130 | 131 | * Not all the required CGI parameters are specified. 132 | * Some of the CGI parameters are empty. 133 | * Fail to specify the number of URLs in the first line of request body. 134 | * The number of URLs specified in the first line does not match the actual number of URLs specified in the subsequent lines. 135 | * At least one of the queried URL is not a valid URL or not properly encoded. 136 | 137 | 138 | Error Handling 139 | --------------- 140 | 141 | If the response status of the request is one of `400`, `401` and `503` then the module fires the `error` event and the error object contains the `statusCode` property with the value of the received HTTP status code. Take a look at the following example: 142 | 143 | ```javascript 144 | api = new SafeBrowse.API( 'INVALID_API_KEY' ); 145 | api.lookup('htp://www.example.com') 146 | .on( 'error', function ( error ) { 147 | // the HTTP status text returned by the API 148 | console.log( error.message ); 149 | // the HTTP status code returned by the API 150 | console.log( error.statusCode ); // 401 - Not authorized 151 | } ); 152 | ``` 153 | 154 | About 155 | ----- 156 | If you have a question then please file an issue or find me on the Twitter [@arnabc](http://twitter.com/arnabc). 157 | 158 | License 159 | -------- 160 | 161 | MIT License. Copyright 2012 Arnab Chakraborty. http://arnab.ch 162 | -------------------------------------------------------------------------------- /test/api-spec.js: -------------------------------------------------------------------------------- 1 | 2 | var vows = require('vows'), 3 | assert = require('assert'), 4 | EventEmitter = require('events').EventEmitter, 5 | SafeBrowse = require('../lib/safe_browse'), 6 | Config = require('./config'); 7 | 8 | 9 | var apiKey = Config.apikey; 10 | 11 | // helper method to create the SafeBrowse object 12 | function createSafeBrowseObj( key ) { 13 | return new SafeBrowse.Api( key, { debug: false } ); 14 | } 15 | 16 | // Mixed URLs 17 | var mixedUrls = [ 18 | 'http://quadnode.com', 19 | 'http://google.com', 20 | // do not visit this link, it may harm your computer 21 | 'http://beatageyer.com/projects/pages/google%2520site%2520check%2520malware.html', 22 | 'http://benanshell.cz.cc', 23 | 'http://gumblar.cn', 24 | 'http://yahoo.com', 25 | 'http://www.msn.com' 26 | ]; 27 | 28 | // Good URLs 29 | var goodUrls = [ 30 | 'http://www.aol.com', 31 | 'http://www.facebook.com', 32 | 'http://mint.com', 33 | 'http://twitter.com' 34 | ]; 35 | 36 | vows.describe('Safe Browse API') 37 | .addBatch( { 38 | 'Should always insist on an API key to be specified': { 39 | topic: [null], 40 | 'should generate error if no API key is specified': function ( topic ) { 41 | var error = 'An API key is required to connect to the Google SafeBrowsing API'; 42 | assert.throws( function () { 43 | createSafeBrowseObj.apply( exports, topic ); 44 | }, 45 | new RegExp( error ) ); 46 | } 47 | }, 48 | 49 | 'Should throw error if invalid URI/URIs provided': { 50 | 51 | topic: createSafeBrowseObj( apiKey ), 52 | 53 | 'URI is undefined/null/empty': function ( topic ) { 54 | var error = 'Specified URL is not a valid one'; 55 | assert.throws( function () { 56 | topic.lookup(); 57 | }, 58 | new RegExp(error) ); 59 | }, 60 | 61 | 'URI is not as per RFC-3986': function ( topic ) { 62 | var error = 'Specified URL is not a valid one'; 63 | 64 | assert.throws( function () { 65 | topic.lookup( '/invalid/url/scheme' ); 66 | }, 67 | 68 | new RegExp(error) ); 69 | }, 70 | 71 | 'None of the URLs are valid for multiple URL verification': function ( topic ) { 72 | var urls = [ 73 | '/invalid/url/scheme', 74 | '/invalid/url/scheme1' 75 | ], 76 | error = 'No URL to look up, check the supplied list whether it contains valid URLs or not'; 77 | 78 | assert.throws( function () { 79 | topic.lookup( urls ); 80 | }, 81 | new RegExp(error) ); 82 | }, 83 | 84 | 'Number of URLs provided has exceeded the MAX allowed limit of 500 URLs': function ( topic ) { 85 | var error = 'Total number of URLs has exceeded the maximum allowed limit of 500'; 86 | assert.throws( function () { 87 | var urls = Array(600); 88 | topic.lookup( urls ); 89 | }, 90 | new RegExp(error) 91 | ); 92 | } 93 | } 94 | } ) 95 | .addBatch( { 96 | 97 | 'Test Multiple URLs with Mixed Content': { 98 | topic: function () { 99 | var sf = createSafeBrowseObj( apiKey ); 100 | sf.lookup( mixedUrls, this.callback ); 101 | }, 102 | 103 | 'should be null': function ( error, result ) { 104 | assert.isNull( error ); 105 | }, 106 | 107 | 'should be an object': function ( error, result ) { 108 | assert.isObject( result ); 109 | }, 110 | 111 | 'should have the key in the result object': function ( error, result ) { 112 | assert.include( result.data, 'http://quadnode.com' ); 113 | }, 114 | 115 | 'should be ok': function ( error, result ) { 116 | assert.equal( result.data['http://quadnode.com'], 'ok' ); 117 | }, 118 | 119 | 'should be malware': function ( error, result ) { 120 | assert.equal( result.data['http://gumblar.cn'], 'malware' ); 121 | }, 122 | 123 | 'should be 200': function ( error, result ) { 124 | assert.equal( result.statusCode, 200 ); 125 | } 126 | }, 127 | 128 | 'Test all good URLs': { 129 | 130 | topic: function () { 131 | var sf = createSafeBrowseObj( apiKey ); 132 | sf.lookup( goodUrls, this.callback ); 133 | }, 134 | 135 | 'should be null': function ( error, result ) { 136 | assert.isNull( error ); 137 | }, 138 | 139 | 'should be an object': function ( error, result ) { 140 | assert.isObject( result ); 141 | }, 142 | 143 | 'should be ok': function ( error, result ) { 144 | assert.equal( result.data['http://twitter.com'], 'ok' ); 145 | }, 146 | 147 | 'should be 204': function ( error, result ) { 148 | assert.equal( result.statusCode, 204 ); 149 | } 150 | }, 151 | 152 | 'Test GET request using Bad URL': { 153 | topic: function () { 154 | var sf = createSafeBrowseObj( apiKey ); 155 | sf.lookup( 'http://gumblar.cn', this.callback ); 156 | }, 157 | 158 | 'should be null': function ( error, result ) { 159 | assert.isNull( error ); 160 | }, 161 | 162 | 'should be an object': function ( error, result ) { 163 | assert.isObject( result ); 164 | }, 165 | 166 | 'should be ok': function ( error, result ) { 167 | assert.equal( result.data['http://gumblar.cn'], 'malware' ); 168 | }, 169 | 170 | 'should be 200': function ( error, result ) { 171 | assert.equal( result.statusCode, 200 ); 172 | } 173 | }, 174 | 175 | 'Test GET request using Good URL': { 176 | topic: function () { 177 | var sf = createSafeBrowseObj( apiKey ); 178 | sf.lookup( 'http://twitter.com', this.callback ); 179 | }, 180 | 181 | 'should be null': function ( error, result ) { 182 | assert.isNull( error ); 183 | }, 184 | 185 | 'should be an object': function ( error, result ) { 186 | assert.isObject( result ); 187 | }, 188 | 189 | 'should be ok': function ( error, result ) { 190 | assert.equal( 'ok', result.data['http://twitter.com'] ); 191 | }, 192 | 193 | 'should be 204': function ( error, result ) { 194 | assert.equal( 204, result.statusCode ); 195 | } 196 | } 197 | 198 | } ) 199 | .export(module); -------------------------------------------------------------------------------- /lib/safe_browse.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011 Arnab Chakraborty 3 | 4 | MIT License 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | 26 | var _ = require('underscore'), 27 | request = require('request'), 28 | http = require('http'), 29 | url = require('url'), 30 | util = require('util'), 31 | qs = require('querystring'), 32 | EventEmitter = require('events').EventEmitter; 33 | 34 | 35 | // Number of URLs which can be verified at once 36 | // sending all URLs to google 37 | const MAX_NUMBER_OF_URLS_ALLOWED = 500; 38 | const GOOGLE_SAFE_BROWSE_LOOKUP_URL = 'https://sb-ssl.google.com/safebrowsing/api/lookup'; 39 | 40 | const Errors = { 41 | API_KEY_REQUIRED: 'An API key is required to connect to the Google SafeBrowsing API', 42 | MAX_URLS_ALLOWED: 'Total number of URLs has exceeded the maximum allowed limit of ' + MAX_NUMBER_OF_URLS_ALLOWED, 43 | INVALID_URL: 'Specified URL is not a valid one. Refer to the documentation for valid URLs', 44 | NO_URL_TO_LOOKUP: 'No URL to look up, check the supplied list whether it contains valid URLs or not' 45 | }; 46 | 47 | // @credit http://af-design.com/blog/2008/03/14/rfc-3986-compliant-uri-encoding-in-javascript/ 48 | /** 49 | * Necessary to override the QueryString module's escape() method 50 | * to make it compliant with RFC-3986. JavaScript's encodeURIComponent() 51 | * does not percent encode these characters ("!", "*", "(", ")", "'" ), in 52 | * order to make the escaping compliant with RFC-3986 which has reserved the 53 | * above mentioned characters, we need to override this method. 54 | * 55 | * @param str 56 | */ 57 | qs.escape = function (str) { 58 | var s = encodeURIComponent(str); 59 | s = s.replace('!','%21'); 60 | s = s.replace('*','%2A'); 61 | s = s.replace('(','%28'); 62 | s = s.replace(')','%29'); 63 | s = s.replace("'",'%27'); 64 | return s; 65 | } 66 | 67 | 68 | /** 69 | * Custom API Response Error wrapper, which basically adds additional 70 | * information to the error object. 71 | * @constructor 72 | * 73 | * @param message - the status text received from the API server 74 | * @param statusCode - The HTTP status code 75 | */ 76 | function APIResponseError( message, statusCode ) { 77 | Error.captureStackTrace( this, this.constructor ); 78 | 79 | this.message = message; 80 | this.statusCode = statusCode; 81 | } 82 | util.inherits( APIResponseError, Error ); 83 | APIResponseError.prototype.name = 'API Response Error'; 84 | 85 | 86 | /* 87 | params = { 88 | // application version 89 | appvar: '1.0.0', 90 | 91 | // protocol version Google SafeBrowsing Lookup API 92 | pvar: '3.0', 93 | 94 | // turn on log messages 95 | debug: false|true, 96 | 97 | // in case google api url changes in future, this can be used to 98 | // fix that problem without modifying anything in the code 99 | api: 'google safebrowsing api url' 100 | } 101 | 102 | */ 103 | 104 | function SafeBrowseApi( apiKey, params ) { 105 | var GOOGLE_API_URL, DEBUG, log; 106 | 107 | // without API KEY do not proceed 108 | if( !apiKey ) { 109 | throw new Error( Errors.API_KEY_REQUIRED ); 110 | } 111 | 112 | // params should be an object 113 | params = params || {}; 114 | 115 | GOOGLE_API_URL = params.api || GOOGLE_SAFE_BROWSE_LOOKUP_URL; 116 | delete params.api; // remove the API if provided 117 | 118 | // one can optionally enable debugging 119 | DEBUG = !!(params.debug); 120 | delete params.debug; 121 | 122 | // merge specified params with the default one 123 | params = _.defaults(params, { key: apiKey }, SafeBrowseApi.defaults); 124 | 125 | // Utility log method 126 | log = DEBUG 127 | ? function () { console.log.apply(console, arguments ); } 128 | : function (){} 129 | 130 | /** 131 | * Utility class which encapsulates the implementation for 132 | * for API request 133 | * @constructor 134 | */ 135 | function SafeBrowse() { 136 | } 137 | 138 | /* 139 | * Utility method to lookup an URL or a set of URLs for malware/phishing safety 140 | * @param {Array|String} uri 141 | * @chainable 142 | */ 143 | SafeBrowse.prototype.lookup = function ( uri, callback ) { 144 | var type = 'get' 145 | , len 146 | , options = {} 147 | , qparams = _.clone( params ) 148 | , self = this 149 | , emitter = new EventEmitter(); 150 | 151 | // if nothing specified, then bark at the user :-) 152 | if( !uri ) { 153 | throw new Error( Errors.INVALID_URL ); 154 | } 155 | 156 | // uri is an array then the request type must be POST 157 | // in order to send multiple URLs to verify to Google 158 | if( Array.isArray( uri ) ) { 159 | type = 'post'; 160 | 161 | log( 'Request type: POST' ); 162 | 163 | // check max number of urls 164 | if( uri.length > MAX_NUMBER_OF_URLS_ALLOWED ) { 165 | throw new Error( Errors.MAX_URLS_ALLOWED ); 166 | } 167 | 168 | // sort the array 169 | uri.sort(); 170 | 171 | // discard invalid urls 172 | var parsedUrls = uri.filter( function ( u ) { 173 | return ( u && isValidURL( u ) ? u : undefined ); 174 | } ); 175 | 176 | if( !parsedUrls.length ) { 177 | throw new Error( Errors.NO_URL_TO_LOOKUP ); 178 | } 179 | 180 | // discard duplicate items 181 | parsedUrls = _.unique( parsedUrls, true /* isSorted */ ); 182 | 183 | // length needs to be sent to the request body 184 | // as per API requirement 185 | parsedUrls.unshift( parsedUrls.length ); 186 | 187 | options.uri = buildQueryStringURL( qparams ); 188 | options.body = parsedUrls.join( '\n' ); 189 | 190 | log( 'Request URI:\n %s', options.uri ); 191 | log( 'Request Body:\n %s', options.body ); 192 | 193 | log( 'Total URLs to look up after processing: %d', parsedUrls[0] ); 194 | } 195 | 196 | 197 | // GET requests 198 | if( type == 'get' ) { 199 | log( 'Request type: GET' ); 200 | 201 | // check URL validness 202 | if( !isValidURL( uri ) ) { 203 | throw new Error( Errors.INVALID_URL ); 204 | } 205 | 206 | qparams.url = uri; 207 | options.uri = buildQueryStringURL( qparams ); 208 | 209 | log( 'URL to be looked up: %s', options.uri ); 210 | } 211 | 212 | // Make the request 213 | log( 'Sending request to Google...' ); 214 | request[type]( options, responseCallback ); 215 | 216 | 217 | // ==== Utility inner functions ==== 218 | 219 | /** 220 | * Utility method to check for URL validity 221 | * @param u 222 | */ 223 | function isValidURL( u ) { 224 | var o = url.parse(u); 225 | return !!(o.protocol && o.hostname ); 226 | } 227 | 228 | /** 229 | * Utility method to generate the GET URL for lookup 230 | * @param u 231 | * @param params 232 | */ 233 | function buildQueryStringURL( params ) { 234 | return ( GOOGLE_API_URL + '?' + qs.stringify( params ) ); 235 | } 236 | 237 | /** 238 | * Internal callback method for the HTTP Request 239 | * @param error 240 | * @param response 241 | * @param body 242 | */ 243 | function responseCallback( error, response, body ) { 244 | var data; 245 | 246 | log('Response Status: %d', response.statusCode ); 247 | log('Raw Response Body: %s', body ); 248 | 249 | function callbackOrEvent( event, args ) { 250 | if( _.isFunction( callback ) ) { 251 | if( event == 'error' ) { 252 | callback( args ); 253 | } else { 254 | callback( null, args ); 255 | } 256 | } else { 257 | emitter.emit( event, args ); 258 | } 259 | } 260 | 261 | if( error ) { 262 | return callbackOrEvent( 'error', error ); 263 | } 264 | 265 | // the Google Safe Browsing API returns the following response codes 266 | // for invalid requests which can be considered as Errors 267 | // 400, 401, 503 268 | 269 | // indexOf uses Strict Matching(===), hence parseInt() 270 | if( [400, 401, 503 ].indexOf( parseInt( response.statusCode, 10 ) ) > -1 ) { 271 | return callbackOrEvent( 'error', new APIResponseError(response.statusText, response.statusCode ) ); 272 | } else { 273 | // assume it's 200 or 204 274 | data = prepareData( response, body ); 275 | callbackOrEvent( 'success', data ); 276 | } 277 | 278 | log( 'Finished.' ); 279 | } 280 | 281 | 282 | /** 283 | * Utility function to parse the response body. It returns an object literal 284 | * with the the URLs as key and api response as values, so that it's easier 285 | * to figure out which url is a bad one. 286 | * 287 | * Output: 288 | * 289 | * response = { statusCode: HTTP_RESPONSE_CODE, data: {...URLs} } 290 | * 291 | * @param response 292 | * @param body 293 | */ 294 | function prepareData( response, body ) { 295 | var statusCode = response.statusCode, 296 | retVal = { 297 | statusCode: statusCode, 298 | data: {} 299 | }, 300 | results; 301 | 302 | if( type == 'get' ) { 303 | results = 'ok'; 304 | 305 | // if 200 then see the response body for the exact type i.e malware|fishing|malware,fishing 306 | if( statusCode == 200 ) { 307 | results = body.replace(/\n|\r\n/, '' ); 308 | } 309 | 310 | retVal['data'][uri] = results; 311 | 312 | } else { 313 | // the first element in parsedUrls array is the 314 | // total number of URLs, we need to shift that out 315 | parsedUrls.shift(); 316 | 317 | // NONE of the specified URLs matched, all clean! 318 | if( statusCode == 204 ) { 319 | parsedUrls.forEach( function ( value, index ) { 320 | retVal['data'][value] = 'ok'; 321 | } ); 322 | } 323 | // AT LEAST ONE of the specified URLs matched 324 | else { 325 | results = body.split('\n'); 326 | 327 | results.forEach( function ( value, index ) { 328 | retVal['data'][ parsedUrls[index] ] = value; 329 | } ); 330 | } 331 | } 332 | return retVal; 333 | } 334 | 335 | return emitter; // for event binding 336 | }; 337 | 338 | return new SafeBrowse(); 339 | } 340 | 341 | 342 | SafeBrowseApi.defaults = { 343 | client: 'Node Safe-Browse', 344 | key: null, 345 | appver: '1.0.0', // format = major.minor.patch 346 | pver: '3.1' // format = major.minor 347 | }; 348 | 349 | 350 | // export 351 | module.exports.Api = SafeBrowseApi; 352 | --------------------------------------------------------------------------------