diff --git a/README.md b/README.md index ed7a04b..b1051f0 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ Minimum threat score necessary to display a matching entry (does not apply to ma Minimum risk level necessary to display a matching entry (only applies to malware entities). +## Installation Instructions + +Installation instructions for integrations are provided on the [PolarityIO GitHub Page](https://polarityio.github.io/). ## Polarity diff --git a/config/config.js b/config/config.js index bab0ff4..6ef9ca5 100644 --- a/config/config.js +++ b/config/config.js @@ -60,23 +60,23 @@ module.exports = { }, request: { // Provide the path to your certFile. Leave an empty string to ignore this option. - // Relative paths are relative to the STAXX integration's root directory + // Relative paths are relative to the x-force-exchange integration's root directory cert: '', // Provide the path to your private key. Leave an empty string to ignore this option. - // Relative paths are relative to the STAXX integration's root directory + // Relative paths are relative to the x-force-exchange integration's root directory key: '', // Provide the key passphrase if required. Leave an empty string to ignore this option. - // Relative paths are relative to the STAXX integration's root directory + // Relative paths are relative to the x-force-exchange integration's root directory passphrase: '', // Provide the Certificate Authority. Leave an empty string to ignore this option. - // Relative paths are relative to the STAXX integration's root directory + // Relative paths are relative to the x-force-exchange integration's root directory ca: '', // An HTTP proxy to be used. Supports proxy Auth with Basic Auth, identical to support for // the url parameter (by embedding the auth info in the uri) proxy: '', /** * If set to false, the integeration will ignore SSL errors. This will allow the integration to connect - * to STAXX servers without valid SSL certificates. Please note that we do NOT recommending setting this + * to the x-force-exchange without valid SSL certificates. Please note that we do NOT recommending setting this * to false in a production environment. */ rejectUnauthorized: true @@ -90,7 +90,7 @@ module.exports = { // the directory you specify is writable by the `polarityd:polarityd` user and group. //directoryPath: '/var/log/polarity-integrations', - level: 'trace', //trace, debug, info, warn, error, fatal + level: 'info', //trace, debug, info, warn, error, fatal }, /** * Options that are displayed to the user/admin in the Polarity integration user-interface. Should be structured @@ -130,7 +130,7 @@ module.exports = { { key: "minimumScore", name: "Minimum Score", - description: "Minimum threat score necessary to display a matching entry (does not apply to malware entities).", + description: "Minimum risk score necessary to display a matching entry (does not apply to malware entities).", default: 0, type: "number", userCanEdit: false, @@ -139,7 +139,7 @@ module.exports = { { key: "minimumRisk", name: "Minimum Malware Risk", - description: "Minimum risk level necessary to display a matching entry (only applies to malware entities).", + description: 'Minimum risk level necessary to display a matching entry (only applies to malware entities). Valid values are "low", "medium", and "high"', default: "medium", type: "text", userCanEdit: false, diff --git a/integration.js b/integration.js index d65659c..13b4ec2 100644 --- a/integration.js +++ b/integration.js @@ -5,9 +5,8 @@ let Transformer = require('./transformer'); let transformer; let Logger; -let requestOptions = { - json: true -}; +let requestWithDefaults; + const risk = { 'high': 3, @@ -16,31 +15,33 @@ const risk = { '': 0 }; -const DATA_TYPE_ERROR = 'if this error occures it means you added data ' + +const DATA_TYPE_ERROR = 'if this error occurs it means you added data ' + 'types in config.js without updating the code in integration.js'; -function getRequestOptions(options) { - let opts = JSON.parse(JSON.stringify(requestOptions)); - opts.auth = { - user: options.apikey, - password: options.password - }; - - return opts; -} - function doLookup(entities, options, callback) { - Logger.trace({ entities: entities, options: options }, 'Entities received'); + Logger.trace({entities: entities, options: options}, 'Entities received'); let results = []; let minimumScore = options.minimumScore; let minimumRisk = options.minimumRisk; - Logger.trace({ minimumScore: minimumScore, minimumRisk: minimumRisk }); + Logger.trace({minimumScore: minimumScore, minimumRisk: minimumRisk}); async.each(entities, (entity, done) => { - Logger.trace({ entity: entity }, 'Looking up entity in x-force exchange'); - let requestOptions = getRequestOptions(options); + Logger.trace({entity: entity}, 'Looking up entity in x-force exchange'); + + // x-force only seems to like URLs that start with http or https + if(entity.isURL && !_isValidUrl(entity.value)){ + done(null); + return; + } + + let requestOptions = { + auth: { + user: options.apikey, + password: options.password + } + }; if (entity.isIP) { requestOptions.url = options.host + '/ipr/' + entity.value; @@ -49,38 +50,51 @@ function doLookup(entities, options, callback) { } else if (entity.isHash) { requestOptions.url = options.host + '/malware/' + entity.value; } else { - Logger.error({ entity: entity }, DATA_TYPE_ERROR); + Logger.error({entity: entity}, DATA_TYPE_ERROR); throw new Error(DATA_TYPE_ERROR); } - request(requestOptions, function (err, resp, body) { - Logger.trace({ error: err, body: body }, 'Results of lookup'); + requestWithDefaults(requestOptions, function (err, resp, body) { + Logger.trace({error: err, body: body}, 'Results of lookup'); if (err) { - done(err); + done({ + detail: 'Error executing HTTPS request', + debug: err + }); return; } if (resp.statusCode !== 200) { if (resp.statusCode === 404) { - Logger.trace({ id: entity.value }, 'Entity not in x-force exhange'); + Logger.trace({id: entity.value}, 'Entity not in x-force exhange'); results.push({ entity: entity, data: null }); done(); + } else if (resp.statusCode === 400) { + Logger.error({error: body}, 'Bad Request'); + done({ + detail: 'Unexpected HTTP Status Code Received', + debug: body, + entityValue: entity.value + }); } else { - Logger.error({ error: body }, 'Error looking up entity in x-force exchange'); - done(body); + Logger.error({error: body}, 'Error looking up entity in x-force exchange'); + done({ + detail: 'Unexpected HTTP Status Code Received', + debug: body, + response: resp + }); } - return; } if (entity.isHash) { let riskLevel = body.malware.risk; - Logger.trace({ risk: riskLevel, minimumRisk: minimumRisk }, 'Checking minimum score'); + Logger.trace({risk: riskLevel, minimumRisk: minimumRisk}, 'Checking minimum score'); if (risk[riskLevel] < risk[minimumRisk]) { done(); @@ -89,7 +103,7 @@ function doLookup(entities, options, callback) { } else { let score = body.score ? body.score : (body.result ? body.result.score : body.score); - Logger.trace({ score: score, minimumScore: minimumScore }, 'Checking minimum score'); + Logger.trace({score: score, minimumScore: minimumScore}, 'Checking minimum score'); if (score < minimumScore) { done(); @@ -105,45 +119,58 @@ function doLookup(entities, options, callback) { } }; - Logger.trace({ result: result }, 'Result added to list'); + Logger.trace({result: result}, 'Result added to list'); results.push(result); done(); }); }, (err) => { - Logger.trace({ results: results }, 'All entity lookups completed, returning results to client'); + Logger.trace({results: results}, 'All entity lookups completed, returning results to client'); callback(err, results); }); } +// x-force only seems to accept URLs that start with http or https +function _isValidUrl(url){ + if(url.startsWith('http') || url.startsWith('https')){ + return true; + } + return false; +} + function startup(logger) { Logger = logger; transformer = new Transformer(logger); + let defaults = {}; if (typeof config.request.cert === 'string' && config.request.cert.length > 0) { - requestOptions.cert = fs.readFileSync(config.request.cert); + defaults.cert = fs.readFileSync(config.request.cert); } if (typeof config.request.key === 'string' && config.request.key.length > 0) { - requestOptions.key = fs.readFileSync(config.request.key); + defaults.key = fs.readFileSync(config.request.key); } if (typeof config.request.passphrase === 'string' && config.request.passphrase.length > 0) { - requestOptions.passphrase = config.request.passphrase; + defaults.passphrase = config.request.passphrase; } if (typeof config.request.ca === 'string' && config.request.ca.length > 0) { - requestOptions.ca = fs.readFileSync(config.request.ca); + defaults.ca = fs.readFileSync(config.request.ca); } if (typeof config.request.proxy === 'string' && config.request.proxy.length > 0) { - requestOptions.proxy = config.request.proxy; + defaults.proxy = config.request.proxy; } if (typeof config.request.rejectUnauthorized === 'boolean') { - requestOptions.rejectUnauthorized = config.request.rejectUnauthorized; + defaults.rejectUnauthorized = config.request.rejectUnauthorized; } + + defaults.json = true; + + requestWithDefaults = request.defaults(defaults); } function validateStringOption(errors, options, optionName, errMessage) { diff --git a/package.json b/package.json index 8c084c8..a3e914f 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "license": "MIT", "author": "Polarity", "dependencies": { - "request": "^2.75", - "async": "2.1.4" + "request": "^2.85", + "async": "2.6.0" }, "devDependencies": { "chai": "~3.5", diff --git a/templates/x-force-exchange-block.hbs b/templates/x-force-exchange-block.hbs index 8effa7d..9c7c6c3 100644 --- a/templates/x-force-exchange-block.hbs +++ b/templates/x-force-exchange-block.hbs @@ -1,16 +1,14 @@
+

+ View in X-Force Exchange +

{{#each details.titledProperties as |prop|}}

{{prop.key}}: {{prop.value}}

{{/each}} - -

- View in X-Force Exchange -

- {{#each details.headerLists as |list|}}
{{list.header}} @@ -30,7 +28,6 @@ {{/each}}
-
\ No newline at end of file diff --git a/transformer.js b/transformer.js index 3e5312d..c04923a 100644 --- a/transformer.js +++ b/transformer.js @@ -11,8 +11,8 @@ module.exports = class Transformer { this.logger.trace({ ip: entity.value, body: body }, 'Transforming body to result'); if (entity.isIP) { - details.addTitledProperty('Score', body.score); - details.addSummary(body.score) + details.addTitledProperty('Risk Score', body.score); + details.addSummary(body.score); details.addTitledProperty('Reason', body.reason); if (body.geo) {