diff --git a/.github/workflows/release-current-version.yml b/.github/workflows/release-current-version.yml index c3dea28..6d6faae 100644 --- a/.github/workflows/release-current-version.yml +++ b/.github/workflows/release-current-version.yml @@ -1,58 +1,11 @@ -# This workflow will run on merge of a PR or push to master -# It will run the integration developement checklist and if that passes -# creates a new release with the Release and Tag name both being the -# package.json version and will with a created tgz file and the SHA256 has in the release body - - name: Release Current Version on: push: - branches: [ master ] + branches: [ master, main ] jobs: - release-current-version: - runs-on: ubuntu-latest - container: 'centos:7' - - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 - - name: Get NPM Version - id: package-version - uses: martinbeentjes/npm-get-version-action@95bc31c6dd3145896c110e382f840bb1e750d09c - - name: Create Build - id: create_build - run: | - npm install && - cd .. && - tar --exclude="./${{ github.event.repository.name }}/.git" --exclude="./${{ github.event.repository.name }}/.gitignore" --exclude="./${{ github.event.repository.name }}/package-lock.json" --exclude="./${{ github.event.repository.name }}/.github" -czvf "${{ github.event.repository.name }}-${{ steps.package-version.outputs.current-version }}.tgz" "./${{ github.event.repository.name }}" && - echo "::set-output name=build_hash::$(sha256sum '${{ github.event.repository.name }}-${{ steps.package-version.outputs.current-version }}.tgz' | grep -oE '^[^ ]*' )" && - cd ${{ github.event.repository.name }} - - name: Polarity Integration Development Checklist - id: int-dev-checklist - uses: polarityio/polarity-integration-development-checklist@v1.0.0 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create Versioned Release - id: create_versioned_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ steps.package-version.outputs.current-version}} - release_name: ${{ steps.package-version.outputs.current-version}} - body: | - SHA256: ${{ steps.create_build.outputs.build_hash }} - draft: false - prerelease: false - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_versioned_release.outputs.upload_url }} - asset_path: ../${{ github.event.repository.name }}-${{ steps.package-version.outputs.current-version}}.tgz - asset_name: ${{ github.event.repository.name }}-${{ steps.package-version.outputs.current-version}}.tgz - asset_content_type: application/gzip + Run: + uses: polarityio/polarity-github-actions/.github/workflows/release-server-versions-for-int-store.yml@master + # with: + # use-integration-development-checklist: false diff --git a/.github/workflows/run-int-dev-checklist.yml b/.github/workflows/run-int-dev-checklist.yml index 546fdfe..ffa86d7 100644 --- a/.github/workflows/run-int-dev-checklist.yml +++ b/.github/workflows/run-int-dev-checklist.yml @@ -1,12 +1,8 @@ -# This workflow will run on a Pull Request is created on both master develop -# It run as series of checks from the Integration Developement Checklist - - name: Run Integration Development Checklist on: pull_request: - branches: [ master, develop ] + branches: [ master, main, develop ] jobs: run-integration-development-checklist: @@ -19,9 +15,9 @@ jobs: - name: Test NPM Install id: test-npm-install run: | - npm install + npm ci - name: Polarity Integration Development Checklist id: int-dev-checklist - uses: polarityio/polarity-integration-development-checklist@v1.0.0 + uses: polarityio/polarity-integration-development-checklist@main with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/LICENSE b/LICENSE index 94c1542..ab1a2a6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2018 Breach Intelligence, Inc. +Copyright (c) 2020 Polarity.IO, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 10e3d6f..603fda4 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,26 @@ # Polarity Palo Alto AutoFocus Integration -Polarity's AutoFocus integration gives users information on hashes that have been detected in Palo Alto's AutoFocus. +Polarity's AutoFocus integration retrieves the indicator summary contained in an AutoFocus Threat Intelligence Card. ![AutoFocus](https://user-images.githubusercontent.com/22529325/41662872-0e4ba0dc-7470-11e8-9cfa-2cfd5dc81e62.png) -Please see [AutoFocus](https://www.paloaltonetworks.com/products/secure-the-network/subscriptions/autofocus) for more information. +Please see [AutoFocus](https://www.paloaltonetworks.com/cortex/autofocus) for more information. ## AutoFocus Integration Options -### AutoFocus API Key - -Your AutoFocus API Key. To obtain a AutoFocus API key, you need to login to your Palo Alto account and navigate to site licenses to view your AutoFocus account. There you can find the API Key needed for AutoFocus. - ### AutoFocus URL +Base AutoFocus API URL. Defaults to https://autofocus.paloaltonetworks.com -Your AutoFocus URL. The URL you use to access Palo Alto's AutoFocus. +### AutoFocus API Key +Your AutoFocus API Key. To obtain a AutoFocus API key, you need to login to your Palo Alto account and navigate to site licenses to view your AutoFocus account. There you can find the API Key needed for AutoFocus. +### View Malware Indicators Only +When checked, the integration will only return indicators classified by AutoFocus as "malware" to the Polarity overlay window. ## Installation Instructions - Installation instructions for integrations are provided on the [PolarityIO GitHub Page](https://polarityio.github.io/). ## Polarity - Polarity is a memory-augmentation platform that improves and accelerates analyst decision making. For more information about the Polarity platform please see: https://polarity.io/ diff --git a/components/auto-block.js b/components/auto-block.js deleted file mode 100644 index 8507376..0000000 --- a/components/auto-block.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict' -polarity.export = PolarityComponent.extend({ - maxRegions: 10, - maxTags: 20 - -}); diff --git a/components/auto-summary.js b/components/auto-summary.js deleted file mode 100644 index a625d91..0000000 --- a/components/auto-summary.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -polarity.export = PolarityComponent.extend({ - - details: Ember.computed.alias('block.data.details.body'), - - summaryTags: Ember.computed('details.tags', function(){ - let summaryTags = []; - - if(this.get('details.total')){ - summaryTags.push("Total number of hits: " + this.get('details.total')); - } - - return summaryTags; - }), - - allTags: Ember.computed('details', function(){ - let tags = Ember.A(); - this.get('details.hits').forEach(function(item){ - if(Array.isArray(item._source.tag)){ - item._source.tag.forEach(function(tag){ - tags.push(tag); - }) - } - }); - - return tags; - }), - - otherData: Ember.computed('details', function(){ - let data = Ember.A(); - this.get('details.hits').forEach(function(item){ - data.push("FileType: " + item._source.filetype); - }); - - return data; - }) -}); diff --git a/components/block.js b/components/block.js new file mode 100644 index 0000000..fbd3dfa --- /dev/null +++ b/components/block.js @@ -0,0 +1,3 @@ +polarity.export = PolarityComponent.extend({ + details: Ember.computed.alias('block.data.details') +}); diff --git a/components/summary.js b/components/summary.js new file mode 100644 index 0000000..fbd3dfa --- /dev/null +++ b/components/summary.js @@ -0,0 +1,3 @@ +polarity.export = PolarityComponent.extend({ + details: Ember.computed.alias('block.data.details') +}); diff --git a/config/config.js b/config/config.js index 33ef24b..bd36625 100644 --- a/config/config.js +++ b/config/config.js @@ -5,7 +5,7 @@ module.exports = { * @type String * @required */ - name: 'Palo Alto - AutoFocus', + name: 'Palo Alto AutoFocus', /** * The acronym that appears in the notification window when information from this integration * is displayed. Note that the acronym is included as part of each "tag" in the summary information @@ -15,23 +15,18 @@ module.exports = { * @type String * @required */ - acronym: 'PAAF', + acronym: 'AUTO', /** * Description for this integration which is displayed in the Polarity integrations user interface * * @type String * @optional */ - description: 'Hash Lookups on data that has been analyzed in AutoFocus ', - entityTypes: ['hash'], - /** - * An array of style files (css or less) that will be included for your integration. Any styles specified in - * the below files can be used in your custom template. - * - * @type Array - * @optional - */ - styles: ['./styles/auto.less', './styles/exfoliate.less'], + description: + 'AutoFocus provides instant access to the massive repository of Palo Alto Networks threat intelligence crowdsourced from the largest footprint of network, endpoint and cloud intel sources.', + entityTypes: ['ipv4', 'domain', 'hash'], + styles: ['./styles/style.less'], + defaultColor: 'light-pink', /** * Provide custom component logic and template for rendering the integration details block. If you do not * provide a custom template and/or component then the integration will display data as a table of key value @@ -42,36 +37,37 @@ module.exports = { */ block: { component: { - file: './components/auto-block.js' + file: './components/block.js' }, template: { - file: './templates/auto-block.hbs' + file: './templates/block.hbs' } }, summary: { component: { - file: './components/auto-summary.js' + file: './components/summary.js' }, template: { - file: './templates/auto-summary.hbs' + file: './templates/summary.hbs' } }, request: { // Provide the path to your certFile. Leave an empty string to ignore this option. - // Relative paths are relative to the PAAF integration's root directory + // Relative paths are relative to the 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 PAAF integration's root directory + // Relative paths are relative to the 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 PAAF integration's root directory + // Relative paths are relative to the integration's root directory passphrase: '', // Provide the Certificate Authority. Leave an empty string to ignore this option. - // Relative paths are relative to the PAAF integration's root directory + // Relative paths are relative to the 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: '', + rejectUnauthorized: true }, logging: { @@ -86,22 +82,31 @@ module.exports = { */ options: [ { - key: 'apiKey', - name: 'API Key', - description: 'AutoFocus API Key.', - default: '', + key: 'url', + name: 'AutoFocus URL', + description: 'The base URL for the AutoFocus API including the schema.', type: 'text', - userCanEdit: true, - adminOnly: false + default: 'https://autofocus.paloaltonetworks.com', + userCanEdit: false, + adminOnly: true }, { - key: 'url', - name: 'Palo Alto URL', - description: 'AutoFocus url', + key: 'apiKey', + name: 'Valid AutoFocus API Key', + description: 'Valid AutoFocus API Key', default: '', - type: 'text', - userCanEdit: true, - adminOnly: false + type: 'password', + userCanEdit: false, + adminOnly: true + }, + { + key: 'malwareOnly', + name: 'View Malware Indicators Only', + description: 'If checked, only indicators flagged as malware will be returned', + default: false, + type: 'boolean', + userCanEdit: false, + adminOnly: true } ] }; diff --git a/config/config.json b/config/config.json new file mode 100644 index 0000000..39809ae --- /dev/null +++ b/config/config.json @@ -0,0 +1,69 @@ +{ + "name": "Palo Alto AutoFocus", + "acronym": "AUTO", + "description": "AutoFocus provides instant access to the massive repository of Palo Alto Networks threat intelligence crowdsourced from the largest footprint of network, endpoint and cloud intel sources.", + "entityTypes": [ + "ipv4", + "domain", + "hash" + ], + "styles": [ + "./styles/style.less" + ], + "defaultColor": "light-pink", + "block": { + "component": { + "file": "./components/block.js" + }, + "template": { + "file": "./templates/block.hbs" + } + }, + "summary": { + "component": { + "file": "./components/summary.js" + }, + "template": { + "file": "./templates/summary.hbs" + } + }, + "request": { + "cert": "", + "key": "", + "passphrase": "", + "ca": "", + "proxy": "" + }, + "logging": { + "level": "info" + }, + "options": [ + { + "key": "url", + "name": "AutoFocus URL", + "description": "The base URL for the AutoFocus API including the schema.", + "type": "text", + "default": "https://autofocus.paloaltonetworks.com", + "userCanEdit": false, + "adminOnly": true + }, + { + "key": "apiKey", + "name": "Valid AutoFocus API Key", + "description": "Valid AutoFocus API Key", + "default": "", + "type": "password", + "userCanEdit": false, + "adminOnly": true + }, + { + "key": "malwareOnly", + "name": "View Malware Indicators Only", + "description": "If checked, only indicators flagged as malware will be returned", + "default": false, + "type": "boolean", + "userCanEdit": false, + "adminOnly": true + } + ] +} \ No newline at end of file diff --git a/integration.js b/integration.js index 8290acc..cf882e9 100644 --- a/integration.js +++ b/integration.js @@ -1,541 +1,210 @@ 'use strict'; -let request = require('request'); -let _ = require('lodash'); -let util = require('util'); -let net = require('net'); -let config = require('./config/config'); -let async = require('async'); -let fs = require('fs'); +const request = require('postman-request'); +const config = require('./config/config'); +const async = require('async'); +const fs = require('fs'); + let Logger; let requestWithDefaults; -let previousDomainRegexAsString = ''; - const MAX_PARALLEL_LOOKUPS = 10; -const THROTTLE_INTERVAL = 60000; -const THROTTLE_MAX_REQUESTS = 17; - - - - function startup(logger) { - Logger = logger; - let defaults = {}; - - if (typeof config.request.cert === 'string' && config.request.cert.length > 0) { - defaults.cert = fs.readFileSync(config.request.cert); - } - - if (typeof config.request.key === 'string' && config.request.key.length > 0) { - defaults.key = fs.readFileSync(config.request.key); - } - - if (typeof config.request.passphrase === 'string' && config.request.passphrase.length > 0) { - defaults.passphrase = config.request.passphrase; - } - - if (typeof config.request.ca === 'string' && config.request.ca.length > 0) { - defaults.ca = fs.readFileSync(config.request.ca); - } - - if (typeof config.request.proxy === 'string' && config.request.proxy.length > 0) { - defaults.proxy = config.request.proxy; - } - - requestWithDefaults = request.defaults(defaults); -} + let defaults = {}; + Logger = logger; -let numLookupsInThrottleWindow = 0; -let lastThrottleWindowStartTime = Date.now(); + const { cert, key, passphrase, ca, proxy, rejectUnauthorized } = config.request; -function throttle(execFunc, throttledCB){ - if(Date.now() - lastThrottleWindowStartTime > THROTTLE_INTERVAL){ - numLookupsInThrottleWindow = 0; - lastThrottleWindowStartTime = Date.now(); - } - - if(numLookupsInThrottleWindow < THROTTLE_MAX_REQUESTS){ - numLookupsInThrottleWindow++; - execFunc(); - }else{ - throttledCB(null); + if (typeof cert === 'string' && cert.length > 0) { + defaults.cert = fs.readFileSync(cert); } -} -/** - * - * @param entities - * @param options - * @param cb - */ - - -function doLookup(entities, options, cb) { - - //Logger.debug({options: options}, 'Options'); - - - let lookupResults = []; - let entityObj = entities; - - //Logger.debug({entity: entityObj}, "Entity Objects"); - - - if (typeof(options.apiKey) !== 'string' || options.apiKey.length === 0) { - cb("The API key is not set."); - return; - } - async.each(entities, function (entityObj, next) { - if (entityObj.isSHA256) { - throttle(function(){ - createSha256Cookie(entityObj, options, function(err, token) { - _lookupEntitySha256(entityObj, options, token, function (err, result) { - if (err) { - next(err); - } else { - Logger.debug({results: result}, "Logging Sha256 results"); - lookupResults.push(result); - next(null); - } - }); - }); - }, function(){ - next('Your lookup for [' + entityObj.value + '] was throttled.'); - }) - }else if (entityObj.isMD5) { - throttle(function(){ - createMd5Cookie(entityObj, options, function(err, token) { - _lookupEntityMd5(entityObj, options, token, function (err, result) { - if (err) { - next(err); - } else { - Logger.debug({results: result}, "Logging MD5 results"); - lookupResults.push(result); - next(null); - } - }); - }); - }, function(){ - next('Your lookup for [' + entityObj.value + '] was throttled.'); - }); - }else if (entityObj.isSHA1) { - throttle(function(){ - createSha1Cookie(entityObj, options, function(err, token) { - _lookupEntitySha1(entityObj, options, token, function (err, result) { - if (err) { - next(err); - } else { - Logger.debug({results: result}, "Logging SHA1 results"); - lookupResults.push(result); - next(null); - } - }); - }); - }, function(){ - next('Your lookup for [' + entityObj.value + '] was throttled.'); - }); - }else { - lookupResults.push({entity: entityObj, data: null}); //Cache the missed results - next(null); - } - }, function(err) { - Logger.debug({lookup: lookupResults}, "Checking to see if the results are making its way to lookupresults"); - cb(err, lookupResults); - }); -} - -var createSha256Cookie = function (entityObj, options, cb) { - - let requestOptions = { - uri: 'https://autofocus.paloaltonetworks.com/api/v1.0/samples/search/', - method: 'POST', - body: -{ - "apiKey": options.apiKey, - "query":{ - "operator":"all", - "children":[ - { - "field":"sample.sha256", - "operator":"is", - "value": entityObj.value - } - ] - }, - "size":50, - "from":0, - "sort":{ - "create_date":{ - "order":"desc" - } - }, - "scope":"public" -}, - json: true - }; - - requestWithDefaults(requestOptions, function (err, response, body) { - let errorObject = _isApiError(err, response, body); - if (errorObject) { - cb(errorObject); - return; - } - - let afCookie = body.af_cookie; - - cb(null, afCookie); - }); -}; - -var createMd5Cookie = function (entityObj, options, cb) { - - let requestOptions = { - uri: options.url + '/api/v1.0/samples/search/', - method: 'POST', - body: -{ - "apiKey": options.apiKey, - "query":{ - "operator":"all", - "children":[ - { - "field":"sample.md5", - "operator":"is", - "value": entityObj.value - } - ] - }, - "size":50, - "from":0, - "sort":{ - "create_date":{ - "order":"desc" - } - }, - "scope":"public" -}, - json: true - }; - - requestWithDefaults(requestOptions, function (err, response, body) { - let errorObject = _isApiError(err, response, body); - if (errorObject) { - cb(errorObject); - return; - } - - Logger.trace({body:body}, "Checking to see if the afCookie is getting passed in now"); - - let afCookie = body.af_cookie; - - cb(null, afCookie); - }); -}; - -var createSha1Cookie = function (entityObj, options, cb) { - - let requestOptions = { - uri: options.url + '/api/v1.0/samples/search/', - method: 'POST', - body: -{ - "apiKey": options.apiKey, - "query":{ - "operator":"all", - "children":[ - { - "field":"sample.sha1", - "operator":"is", - "value": entityObj.value - } - ] - }, - "size":50, - "from":0, - "sort":{ - "create_date":{ - "order":"desc" - } - }, - "scope":"public" -}, - json: true - }; - - requestWithDefaults(requestOptions, function (err, response, body) { - let errorObject = _isApiError(err, response, body); - if (errorObject) { - cb(errorObject); - return; - } - - - let afCookie = body.af_cookie; - - cb(null, afCookie); - }); -}; - - -function _lookupEntitySha256(entityObj, options, token, cb) { - - let requestOptions = { - uri: 'https://autofocus.paloaltonetworks.com/api/v1.0/samples/results/' + token, - method: 'POST', - body: { - "apiKey": options.apiKey - }, - json: true - }; - - Logger.trace({request: requestOptions}, "Checking the request options in the sha lookup"); - - requestWithDefaults(requestOptions, function (err, response, body) { - let errorObject = _isApiError(err, response, body, entityObj.value); - if (errorObject) { - cb(errorObject); - return; - } - - - Logger.trace({data: body}, "Logging Body Data of the sha256"); - - if (_.isNull(body)){ - cb(null, { - entity: entityObj, - data: null - }); - return; - } - if (_.isEmpty(body.hits)) { - cb(null, { - entity: entityObj, - data: null - }); - return; - } - - if (_isLookupMiss(response)) { - cb(null, { - entity: entityObj, - data: null - }); - return; - } - - - // The lookup results returned is an array of lookup objects with the following format - cb(null, { - // Required: This is the entity object passed into the integration doLookup method - entity: entityObj, - // Required: An object containing everything you want passed to the template - data: { - // We are constructing the tags using a custom summary block so no data needs to be passed here - summary: [], - // Data that you want to pass back to the notification window details block - details: { entity: entityObj.value, body: body} - } - }); - }); -} - -function _lookupEntityMd5(entityObj, options, token, cb) { - let requestOptions = { - uri: 'https://autofocus.paloaltonetworks.com/api/v1.0/samples/results/' + token, - method: 'POST', - body: { - "apiKey": options.apiKey - }, - json: true - }; - - Logger.trace({request: requestOptions}, "Checking the request options in the MD5 lookup"); - - requestWithDefaults(requestOptions, function (err, response, body) { - let errorObject = _isApiError(err, response, body, entityObj.value); - if (errorObject) { - cb(errorObject); - return; - } + if (typeof key === 'string' && key.length > 0) { + defaults.key = fs.readFileSync(key); + } - Logger.trace({data: body}, "Logging Body Data of the MD5 Hash"); + if (typeof passphrase === 'string' && passphrase.length > 0) { + defaults.passphrase = passphrase; + } - if (_.isNull(body)){ - cb(null, { - entity: entityObj, - data: null - }); - return; - } - if (_.isEmpty(body.hits)) { - cb(null, { - entity: entityObj, - data: null - }); - return; - } + if (typeof ca === 'string' && ca.length > 0) { + defaults.ca = fs.readFileSync(ca); + } - if (_isLookupMiss(response)) { - cb(null, { - entity: entityObj, - data: null - }); - return; - } + if (typeof proxy === 'string' && proxy.length > 0) { + defaults.proxy = proxy; + } + if (typeof rejectUnauthorized === 'boolean') { + defaults.rejectUnauthorized = rejectUnauthorized; + } - // The lookup results returned is an array of lookup objects with the following format - cb(null, { - // Required: This is the entity object passed into the integration doLookup method - entity: entityObj, - // Required: An object containing everything you want passed to the template - data: { - // We are constructing the tags using a custom summary block so no data needs to be passed here - summary: [], - // Data that you want to pass back to the notification window details block - details: { entity: entityObj.value, body: body} - } - }); - }); + requestWithDefaults = request.defaults(defaults); } -function _lookupEntitySha1(entityObj, options, token, cb) { +function doLookup(entities, options, cb) { + let lookupResults = []; + let tasks = []; + Logger.debug(entities); + entities.forEach((entity) => { let requestOptions = { - uri: 'https://autofocus.paloaltonetworks.com/api/v1.0/samples/results/' + token, - method: 'POST', - body: { - "apiKey": options.apiKey - }, - json: true + method: 'GET', + uri: `${options.url}/api/v1.0/tic`, + headers: { + apiKey: options.apiKey + }, + json: true }; - Logger.trace({request: requestOptions}, "Checking the request options in the sha lookup"); + if (entity.isIPv4) { + requestOptions.qs = { + indicatorType: 'ipv4_address', + indicatorValue: entity.value, + includeTags: true + }; + } else if (entity.isDomain) { + requestOptions.qs = { + indicatorType: 'domain', + indicatorValue: entity.value.toLowerCase(), + includeTags: true + }; + } else if (entity.isHash) { + requestOptions.qs = { + indicatorType: 'filehash', + indicatorValue: entity.value.toLowerCase(), + includeTags: true + }; + } else { + return; + } - requestWithDefaults(requestOptions, function (err, response, body) { - let errorObject = _isApiError(err, response, body, entityObj.value); - if (errorObject) { - cb(errorObject); - return; - } + Logger.trace({ requestOptions }, 'Request Options'); - Logger.trace({data: body}, "Logging Body Data of the sha1"); + tasks.push(function (done) { + requestWithDefaults(requestOptions, function (error, res, body) { + let processedResult = handleRestError(error, entity, res, body); - if (_.isNull(body)){ - cb(null, { - entity: entityObj, - data: null - }); + if (processedResult.error) { + done(processedResult); return; } - if (_.isEmpty(body.hits)) { - cb(null, { - entity: entityObj, - data: null - }); - return; - } - - if (_isLookupMiss(response)) { - cb(null, { - entity: entityObj, - data: null - }); - return; - } - - // The lookup results returned is an array of lookup objects with the following format - cb(null, { - // Required: This is the entity object passed into the integration doLookup method - entity: entityObj, - // Required: An object containing everything you want passed to the template - data: { - // We are constructing the tags using a custom summary block so no data needs to be passed here - summary: [], - // Data that you want to pass back to the notification window details block - details: { entity: entityObj.value, body: body} - } - }); + done(null, processedResult); + }); }); -} - -function _isLookupMiss(response) { - return response.statusCode === 404 || response.statusCode === 500; -} + }); -function _isApiError(err, response, body, entityValue) { + async.parallelLimit(tasks, MAX_PARALLEL_LOOKUPS, (err, results) => { if (err) { - return err; + Logger.error({ err: err }, 'Error'); + cb(err); + return; } - if (response.statusCode === 500) { - return _createJsonErrorPayload("Internal Error", null, '500', '1', 'Internal Error', { - err: err + results.forEach((result) => { + if (!result.body || result.body === null || result.body.length === 0 || !result.body.indicator) { + lookupResults.push({ + entity: result.entity, + data: null }); - } - - if (response.statusCode === 503) { - return _createJsonErrorPayload("API Minute Bucket Exceeded", null, '503', '1', 'API Minute Bucket Exceeded', { - err: err + } else { + if (options.malwareOnly === true && getIsMalicious(result) === false) return; + + lookupResults.push({ + entity: result.entity, + data: { + summary: [], + details: result.body + } }); - } + } + }); - // Any code that is not 200 and not 404 (missed response), we treat as an error - if (response.statusCode !== 200 && response.statusCode !== 404) { - return body; - } + Logger.debug({ lookupResults }, 'Results'); + cb(null, lookupResults); + }); +} - return null; +function getIsMalicious(result) { + if ( + result.body && + result.body.indicator.latestPanVerdicts && + (result.body.indicator.latestPanVerdicts.PAN_DB === 'MALWARE' || + result.body.indicator.latestPanVerdicts.WF_SAMPLE === 'MALWARE') + ) { + return true; + } else { + return false; + } } -// function that takes the ErrorObject and passes the error message to the notification window -var _createJsonErrorPayload = function (msg, pointer, httpCode, code, title, meta) { +function handleRestError(error, entity, res, body) { + let result; + + if (error) { return { - errors: [ - _createJsonErrorObject(msg, pointer, httpCode, code, title, meta) - ] - } -}; + error: error, + detail: 'HTTP Request Error' + }; + } -var _createJsonErrorObject = function (msg, pointer, httpCode, code, title, meta) { - let error = { - detail: msg, - status: httpCode.toString(), - title: title, - code: 'PAAF_' + code.toString() + if (res.statusCode === 200 && body) { + // we got data! + result = { + entity: entity, + body: body + }; + } else if (res.statusCode === 404) { + //Autofocus returns a 404 if indicator is not present in the DB. + result = { + entity, + body: null + }; + } else if (res.statusCode === 400) { + result = { + error: 'Bad Request', + detail: body + }; + } else if (res.statusCode === 429) { + result = { + error: 'Rate Limit Exceeded', + detail: body + }; + } else { + result = { + error: 'Unexpected Error', + statusCode: res ? res.statusCode : 'Unknown', + detail: 'An unexpected error occurred' }; + } - if (pointer) { - error.source = { - pointer: pointer - }; - } + return result; +} - if (meta) { - error.meta = meta; - } +function validateOption(errors, options, optionName, errMessage) { + if ( + typeof options[optionName].value !== 'string' || + (typeof options[optionName].value === 'string' && options[optionName].value.length === 0) + ) { + errors.push({ + key: optionName, + message: errMessage + }); + } +} - return error; -}; +function validateOptions(options, callback) { + let errors = []; -function validateOptions(userOptions, cb) { - let errors = []; - if (typeof userOptions.apiKey.value !== 'string' || - (typeof userOptions.apiKey.value === 'string' && userOptions.apiKey.value.length === 0)) { - errors.push({ - key: 'apiKey', - message: 'You must provide an AutoFocus API key' - }) - } + validateOption(errors, options, 'apiKey', 'You must provide a valid API Key.'); - cb(null, errors); + callback(null, errors); } module.exports = { - doLookup: doLookup, - startup: startup, - validateOptions: validateOptions + doLookup: doLookup, + validateOptions: validateOptions, + startup: startup }; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4fc3cd9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,403 @@ +{ + "name": "autofocus", + "version": "3.0.6", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@postman/form-data": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", + "integrity": "sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "@postman/tough-cookie": { + "version": "4.1.2-postman.1", + "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.2-postman.1.tgz", + "integrity": "sha512-keOKL3RQohnH5K4GNGSV7JE8+SU/ktWJ3h9ulqttOGUWCZWcbUOtMNXkC3PQ/R1hIa+2qEfh0/5NCcAhWqeMTQ==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + } + }, + "@postman/tunnel-agent": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.3.tgz", + "integrity": "sha512-k57fzmAZ2PJGxfOA4SGR05ejorHbVAa/84Hxh/2nAztjNXc4ZjOm9NUIk6/Z6LCrBvJZqjRZbN8e/nROVUPVdg==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" + }, + "aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==" + }, + "brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "requires": { + "base64-js": "^1.1.2" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "http-signature": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", + "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.14.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "postman-request": { + "version": "2.88.1-postman.32", + "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.32.tgz", + "integrity": "sha512-Zf5D0b2G/UmnmjRwQKhYy4TBkuahwD0AMNyWwFK3atxU1u5GS38gdd7aw3vyR6E7Ii+gD//hREpflj2dmpbE7w==", + "requires": { + "@postman/form-data": "~3.1.1", + "@postman/tough-cookie": "~4.1.2-postman.1", + "@postman/tunnel-agent": "^0.6.3", + "aws-sign2": "~0.7.0", + "aws4": "^1.12.0", + "brotli": "^1.3.3", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "har-validator": "~5.1.3", + "http-signature": "~1.3.1", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "^2.1.35", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.3", + "safe-buffer": "^5.1.2", + "stream-length": "^1.0.2", + "uuid": "^8.3.2" + } + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" + }, + "qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stream-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", + "integrity": "sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg==", + "requires": { + "bluebird": "^2.6.2" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + } + } +} diff --git a/package.json b/package.json index e61bca1..e88ba28 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { + "name": "autofocus", + "version": "3.0.6", "main": "./integration.js", - "name": "Opswat_Metadefender", - "version": "3.0.3-beta", "private": true, "dependencies": { - "request": "^2.83", - "lodash": "^4.17", - "async": "^2.5" + "postman-request": "^2.88.1-postman.32", + "lodash": "^4.17.21", + "async": "^3.2.4" } -} +} \ No newline at end of file diff --git a/styles/auto.less b/styles/auto.less deleted file mode 100644 index 06c368a..0000000 --- a/styles/auto.less +++ /dev/null @@ -1,17 +0,0 @@ -/* - All integrations are namespaced with the class `-integration`. When creating custom - CSS to style your integration you should ensure that all your classes are appropriately namespaced to prevent - your css from affecting other integrations. Note that the integration id is based off the name of the integration - directory. -*/ -.tags { - margin-top: 5px; - - .section-tag { - background-color: #dedede; - border-radius: 4px; - margin-bottom: 2px; - padding: 3px; - display: inline-block; - } - } diff --git a/styles/exfoliate.less b/styles/exfoliate.less deleted file mode 100644 index 8722e58..0000000 --- a/styles/exfoliate.less +++ /dev/null @@ -1,123 +0,0 @@ -.p-title { - font-size: 12px; - color: #9a9a9a; //$p-grey - font-weight: 700; - margin: 10px 0 5px 0; - padding: 0px; - display: flex; - align-items: center; - - i { - font-size: 8px; - margin-right: 3px; - } -} - -.p-link, .p-link:link, .p-link:visited { - color: #039BE5; - font-size: 12px; - cursor: pointer; - font-weight: 600; - text-decoration: none; - padding-bottom: 3px; -} - -.p-link:hover, .p-link:active { - color: darken(#039BE5, 10%); - text-decoration: none; -} - -.p-action { - color: #7BC942; - font-size: 12px; - cursor: pointer; - font-weight: 600; - text-decoration: none; -} - -.p-action:hover { - color: darken(#7BC942, 10%); - text-decoration: none; -} - -.p-key { - font-size: 12px; - color: #000; - font-weight: normal; - line-height: 1.1em; -} - -.p-value { - font-size: 12px; - color: #000; //$p-black - font-weight: 700; - line-height: 1.1em; -} - -.p-green { - color: #388E3C; //$p-green -} - -.p-red { - color: #FF4559; //$p-red -} - -.p-grey { - color: #9A9A9A //$p-grey -} - -.p-bg-green { - background-color: #7BC942; -} - -.p-bg-yellow { - background-color: #FCEE21; -} - -.p-bg-red { - background-color: #FF4559; -} - -.p-bg-orange { - background-color: #FF8F00; -} - -.p-bg-white{ - background-color: #ffffff; -} - -.p-footnote{ - color: #9a9a9a; - font-size: 11px; -} - -table { - padding: 5px; - font-weight: 400; - font-size: 12px; - text-align: left; - width: 100%; - word-break: break-all; - - thead { - th { - color: #000; //$p-black - font-weight: 600; - padding: 5px; - } - } - - tbody { - tr:nth-child(odd) { - background-color: #f7f7f7; //$p-dark-white - } - } - - td { - padding: 5px; - } -} - -.p-non-exfoliate-outer-padding{ - padding: 10px; -} diff --git a/styles/style.less b/styles/style.less new file mode 100644 index 0000000..97271c0 --- /dev/null +++ b/styles/style.less @@ -0,0 +1,8 @@ +.tag { + border-radius: 4px; + padding: 2px 5px; + margin-right: 3px; + line-height: 2em; + white-space: nowrap; + background-color: #eee; +} diff --git a/templates/auto-block.hbs b/templates/auto-block.hbs deleted file mode 100644 index 015853c..0000000 --- a/templates/auto-block.hbs +++ /dev/null @@ -1,114 +0,0 @@ -
- - {{#if block.data.details.entity}} - View in AutoFocus - {{/if}} - - {{#if block.data.details.total}} -
- Total Number of Hits: - {{block.data.details.body.total}} -
- {{/if}} - - -{{#if block.data.details.body.hits}} - {{#each block.data.details.body.hits as |data|}} - - {{#if data._source.malware}} -
- Malware: - {{data._source.malware}} -
- {{/if}} - - {{#if data._source.filetype}} -
- Filetype: - {{data._source.filetype}} -
- {{/if}} - - {{#if data._source.size}} -
- File Size: - {{data._source.size}} -
- {{/if}} - - {{#if data._source.sha1}} -
- SHA1: - {{data._source.sha1}} -
- {{/if}} - - {{#if data._source.md5}} -
- MD5: - {{data._source.md5}} -
- {{/if}} - - {{#if data._source.app_packagename}} -
- Applicaton Package Name: - {{data._source.app_packagename}} -
- {{/if}} - - {{#if data._source.app_name}} -
- Application Name: - {{data._source.app_name}} -
- {{/if}} - - {{#if data._source.create_date}} -
- Date File Created: - {{data._source.create_date}} -
- {{/if}} - - {{#if data._source.finish_date}} -
- Date File Finished Processing: - {{data._source.finish_date}} -
- {{/if}} - - {{#if data._source.update_date}} -
- Date File Last Updated: - {{data._source.update_date}} -
- {{/if}} - - - {{#if data._source.region}} -
-

- Regions Affected -

- {{#each (limit data._source.region maxRegions) as |data|}} - - {{/each}} -
- {{/if}} - - {{#if data._source.tag}} -
-

- Tags -

- {{#each (limit data._source.tag maxTags) as |data|}} - - {{/each}} -
- {{/if}} - -{{/each}} -{{/if}} -
diff --git a/templates/auto-summary.hbs b/templates/auto-summary.hbs deleted file mode 100644 index 69b2feb..0000000 --- a/templates/auto-summary.hbs +++ /dev/null @@ -1,21 +0,0 @@ - -{{#each summaryTags as |tag|}} - - {{block.acronym}} - {{tag}} - -{{/each}} - -{{#each (limit allTags maxTagsInSummary) as |tag|}} - - {{block.acronym}} - {{tag}} - -{{/each}} - -{{#each (limit otherData maxTagsInSummary) as |tag|}} - - {{block.acronym}} - {{tag}} - -{{/each}} diff --git a/templates/block.hbs b/templates/block.hbs new file mode 100644 index 0000000..f8fc160 --- /dev/null +++ b/templates/block.hbs @@ -0,0 +1,72 @@ +{{#if details.indicator}} +

+ {{fa-icon "info" fixedWidth=true}} AutoFocus Threat Intelligence Card Summary +

+ {{#if details.indicator.indicatorType}} +
+ Indicator Type: + {{details.indicator.indicatorType}} +
+ {{/if}} + {{#if details.indicator.firstSeenTsGlobal}} +
+ First Seen: + {{moment-time details.indicator.firstSeenTsGlobal}} +
+ {{/if}} + {{#if details.indicator.lastSeenTsGlobal}} +
+ Last Seen: + {{moment-time details.indicator.lastSeenTsGlobal}} +
+ {{/if}} + {{#if details.indicator.wildfireRelatedSampleVerdictCounts.MALWARE}} +
+ Wildfire Verdict Count: + {{details.indicator.wildfireRelatedSampleVerdictCounts.MALWARE}} +
+ {{/if}} + {{#if details.indicator.seenByDataSourceIds}} +

+ {{fa-icon "eye" fixedWidth=true}} Sources Seen By +

+
+ {{#each details.indicator.seenByDataSourceIds as |seenBy| }} + {{seenBy}} + {{/each}} +
+ {{/if}} + {{#if details.tags}} +

+ {{fa-icon "tag" fixedWidth=true}} AutoFocus Tags +

+
+ {{#each details.tags as |tag index| }} + {{#if (gt index 0)}} +
+ {{/if}} +

+ Tag #{{inc index}}: +

+ {{#if tag.tag_name}} +
+ Name: + {{tag.tag_name}} +
+ {{/if}} + {{#if tag.description}} +
+ Description: + {{tag.description}} +
+ {{/if}} + {{#if tag.source}} +
+ Source: + {{tag.source}} +
+ {{/if}} + {{/each}} +
+ {{/if}} +{{/if}} diff --git a/templates/summary.hbs b/templates/summary.hbs new file mode 100644 index 0000000..8133996 --- /dev/null +++ b/templates/summary.hbs @@ -0,0 +1,26 @@ +{{#if details.indicator.latestPanVerdicts.WF_SAMPLE}} + {{#if (eq details.indicator.latestPanVerdicts.WF_SAMPLE "MALWARE")}} + + {{block.acronym}} + MALWARE + + {{else}} + + {{block.acronym}} + BENIGN + + {{/if}} +{{/if}} +{{#if details.indicator.latestPanVerdicts.PAN_DB}} + {{#if (eq details.indicator.latestPanVerdicts.PAN_DB "MALWARE")}} + + {{block.acronym}} + MALWARE + + {{else}} + + {{block.acronym}} + BENIGN + + {{/if}} +{{/if}}