diff --git a/README.md b/README.md index 94269d8..0259204 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,26 @@ # virustotal-prometheus -Monitor VirusTotal reports and expose Prometheus metrics +App to monitor (mainly) VirusTotal reports and to expose Prometheus metrics. + +The application is K8s ready, and it provides also a ServiceMonitor and a PrometheusRule configurations that can be used by your Prometheus/Alertmanager. + +## Intelligence sources + +- VirusTotal +- IBM Xforce (optional) + +## Domain List + +It can be configured via a [config file](/config/main.sample.yaml). + +### Api sources for the domain list +- Cloudflare (optional): +the domain list can be dynamically enriched via Clodflare. Please set up this connection with a read-only apiKey. + +## How to Run + +``` +yarn +yarn start -c path_to_config_file +``` \ No newline at end of file diff --git a/charts/virustotal-prometheus/Chart.yaml b/charts/virustotal-prometheus/Chart.yaml index fba1654..89b7feb 100644 --- a/charts/virustotal-prometheus/Chart.yaml +++ b/charts/virustotal-prometheus/Chart.yaml @@ -1,5 +1,5 @@ description: Virustotal Prometheus name: virustotal-prometheus -version: v0.2.0 -appVersion: v0.2.0 +version: v0.3.0 +appVersion: v0.3.0 apiVersion: v2 diff --git a/charts/virustotal-prometheus/templates/alertrules.yaml b/charts/virustotal-prometheus/templates/alertrules.yaml index 1730d9e..346a5ac 100644 --- a/charts/virustotal-prometheus/templates/alertrules.yaml +++ b/charts/virustotal-prometheus/templates/alertrules.yaml @@ -23,5 +23,15 @@ spec: for: 2m labels: severity: warning - origin: {{ .Values.prometheusRules.origin }} + origin: {{ .Values.prometheusRules.origin }} + {{ if eq .Values.config.ibmXforce.enabled true }} + - alert: IbmXforceBadScore + annotations: + message: 'Domain {{`{{ $labels.domain }}`}} might have been flagged as problematic, please visit https://www.exchange.xforce.ibmcloud.com/url/{{`{{ $labels.domain }}`}} to doublecheck.' + expr: max without(instance,pod) (last_over_time(ibm_xforce_score[10m])) > 1 + for: 2m + labels: + severity: warning + origin: {{ .Values.prometheusRules.origin }} + {{ end }} {{ end }} diff --git a/charts/virustotal-prometheus/values.yaml b/charts/virustotal-prometheus/values.yaml index 730564e..ec9f5f0 100644 --- a/charts/virustotal-prometheus/values.yaml +++ b/charts/virustotal-prometheus/values.yaml @@ -16,6 +16,10 @@ config: - example.com virusTotal: apiKey: secret + ibmXforce: + enabled: false + apiKey: xxx + apiPassword: xxx serviceMonitor: enabled: true diff --git a/config/main.sample.yaml b/config/main.sample.yaml index 80e43fb..5f8682d 100644 --- a/config/main.sample.yaml +++ b/config/main.sample.yaml @@ -8,4 +8,8 @@ targetDomains: manualList: - example.com virusTotal: - apiKey: secret \ No newline at end of file + apiKey: secret +ibmXforce: + enabled: false + apiKey: xxx + apiPassword: xxx \ No newline at end of file diff --git a/package.json b/package.json index 417d84f..c66c0e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "virustotal-prometheus", - "version": "0.2.0", + "version": "0.3.0", "description": "Monitor VirusTotal reports and expose Prometheus metrics", "repository": "git@github.com:w3f/report-scanner.git", "author": "W3F Infrastructure Team ", @@ -25,6 +25,7 @@ "cloudflare": "^2.9.1", "commander": "^4.0.0", "express": "^4.18.1", + "got": "^11", "node-virustotal": "^3.35.0", "prom-client": "^14.0.1" }, diff --git a/src/actions/start.ts b/src/actions/start.ts index 29b9183..5ddf3a1 100644 --- a/src/actions/start.ts +++ b/src/actions/start.ts @@ -6,16 +6,24 @@ import { register } from 'prom-client'; import { InputConfig } from '../types'; import { evalIntervalMinutes } from '../constants'; import vt from 'node-virustotal' -import Cloudflare = require('cloudflare') +import cloudflare from 'cloudflare' +import got from 'got' -const logger = LoggerSingleton.getInstance() +interface LookupConfig { + vtApi: any; + ibmApiKey?: string; + ibmPassword?: string; + ibmEnabled: boolean; + promClient: Prometheus; +} +let logger = LoggerSingleton.getInstance() -function lookup(vtApi: any, promClient: Prometheus, domain: string){ - logger.debug(`triggering a lookup for the domain ${domain}`) +function vtLookup(vtApi: any, promClient: Prometheus, domain: string): void { + logger.debug(`triggering a Virustotal lookup for the domain ${domain}`) vtApi.domainLookup(domain,function(err, res){ if (err) { - logger.error(`Error on processing ${domain}`); + logger.error(`Error on VT processing ${domain}`); logger.error(err); process.exit(-1) } @@ -23,42 +31,48 @@ function lookup(vtApi: any, promClient: Prometheus, domain: string){ const parsed = JSON.parse(res) const reports = parsed.data.attributes.last_analysis_stats.malicious + parsed.data.attributes.last_analysis_stats.suspicious logger.info(`${domain} reports: ${reports}`); - promClient.setVTReports(domain,reports) - return; + promClient.setVTReport(domain,reports) + logger.debug(`VT lookup for the domain ${domain} DONE`) }) - logger.debug(`lookup for the domain ${domain} DONE`) } -export async function startAction(cmd): Promise { +async function ibmLookup(apiKey: string, apiPassword: string ,promClient: Prometheus, domain: string): Promise { + logger.debug(`triggering an IBM lookup for the domain ${domain}`) + const url = `https://api.xforce.ibmcloud.com/api/url/${domain}`; - const cfg = new Config().parse(cmd.config); - LoggerSingleton.setInstance(cfg.logLevel) - - const domainsSet = new Set() - if(cfg.targetDomains.cloudflare.enabled){ - const cf = new Cloudflare({ - token: cfg.targetDomains.cloudflare.apiKey - }); + const options = { + headers: { + 'accept': 'application/json', + 'Authorization': `Basic ${Buffer.from(`${apiKey}:${apiPassword}`).toString("base64")}` + }, + }; - try { - const zones = await cf.zones.browse() - zones.result.forEach(element => { - domainsSet.add(element.name) - }); - } catch (error) { - logger.error(`Error on processing the clouflare api`); + try { + const response: any = await got(url,options).json(); + logger.debug(JSON.stringify(response)) + const score: number = response.result.score? response.result.score : 1 //unkown is treated as OK + logger.info(`${domain} ibm score, 1 is OK: ${score}`); + promClient.setIbmScore(domain,score) + logger.debug(`IBM lookup for the domain ${domain} DONE`) + } catch (error) { + const errorMessage: string = error.toString() + if(errorMessage.includes("404")){ + logger.warn(`IBM api is not capable of processing ${domain}`) + logger.debug(errorMessage); + } else{ + logger.error(`Error on IBM processing ${domain}`); logger.error(error); process.exit(-1) } } +} - const manualList = cfg.targetDomains.manualList ? cfg.targetDomains.manualList : [] - manualList.forEach(domain=>domainsSet.add(domain)) - const targetDomains = Array.from( domainsSet.values() ) +async function lookup(config: LookupConfig, domain: string): Promise { + vtLookup(config.vtApi,config.promClient,domain) + if(config.ibmEnabled) await ibmLookup(config.ibmApiKey,config.ibmPassword,config.promClient,domain) +} - logger.debug(targetDomains.toString()) - - const server = express(); +function configureServerEndpoints(server: express.Express): void { server.get('/healthcheck', async (req: express.Request, res: express.Response): Promise => { res.status(200).send('OK!') @@ -67,17 +81,59 @@ export async function startAction(cmd): Promise { res.set('Content-Type', register.contentType) res.end(await register.metrics()) }) - server.listen(cfg.port); +} + +async function cfAddDomains(apiKey: string, domains: Set): Promise { + const cf = cloudflare({ + token: apiKey + }); + + try { + const zones = await cf.zones.browse() + zones.result.forEach(element => { + domains.add(element.name) + }); + } catch (error) { + logger.error(`Error on processing the clouflare api`); + logger.error(error); + process.exit(-1) + } +} + +export async function startAction(cmd): Promise { + + const cfg = new Config().parse(cmd.config); + logger = LoggerSingleton.getNewInstance(cfg.logLevel) + + const domainsSet = new Set() + if(cfg.targetDomains.cloudflare.enabled){ + await cfAddDomains(cfg.targetDomains.cloudflare.apiKey,domainsSet) + } + const manualList = cfg.targetDomains.manualList ? cfg.targetDomains.manualList : [] + manualList.forEach(domain=>domainsSet.add(domain)) + const targetDomains = Array.from( domainsSet.values() ) + logger.info(`Target List: ${targetDomains.toString()}`) - const api = vt.makeAPI(); - api.setKey(cfg.virusTotal.apiKey) + const server = express(); + configureServerEndpoints(server) + server.listen(cfg.port); + const promClient = new Prometheus(); const evalInterval = cfg.evalIntervalMinutes? cfg.evalIntervalMinutes*1000*60 : evalIntervalMinutes - targetDomains.forEach(domain=>lookup(api,promClient,domain)) + + const vtApi = vt.makeAPI(); + vtApi.setKey(cfg.virusTotal.apiKey) + + const lookupConfig = { + vtApi: vtApi, + ibmApiKey: cfg.ibmXforce.apiKey, + ibmPassword: cfg.ibmXforce.apiPassword, + ibmEnabled: cfg.ibmXforce.enabled, + promClient: promClient + } + targetDomains.forEach(domain => lookup(lookupConfig,domain)) setInterval( - () => targetDomains.forEach(domain => { - lookup(api,promClient,domain) - }), + () => targetDomains.forEach(domain => lookup(lookupConfig,domain)), evalInterval ) } diff --git a/src/logger.ts b/src/logger.ts index 2f5969d..528b2c5 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -5,15 +5,16 @@ export class LoggerSingleton { private constructor() { //do nothing } - public static setInstance(level: string): void { - LoggerSingleton.instance = createLogger(level) - } public static getInstance(level?: string): Logger { if (!LoggerSingleton.instance) { LoggerSingleton.instance = createLogger(level) } return LoggerSingleton.instance } + public static getNewInstance(level?: string): Logger { + LoggerSingleton.instance = createLogger(level) + return LoggerSingleton.instance + } } export type Logger = LoggerW3f diff --git a/src/prometheus.ts b/src/prometheus.ts index 357c82a..7b9cb92 100644 --- a/src/prometheus.ts +++ b/src/prometheus.ts @@ -5,6 +5,7 @@ import { PromClient } from './types'; export class Prometheus implements PromClient { private virustotalReports: promClient.Gauge<"domain">; + private ibmScore: promClient.Gauge<"domain">; private readonly logger: Logger = LoggerSingleton.getInstance() @@ -21,9 +22,12 @@ export class Prometheus implements PromClient { promClient.collectDefaultMetrics(); } - setVTReports(domain: string, reports: number): void{ + setVTReport(domain: string, reports: number): void{ this.virustotalReports.set({domain}, reports); } + setIbmScore(domain: string, score: number): void{ + this.ibmScore.set({domain}, score); + } _initMetrics(): void { this.virustotalReports = new promClient.Gauge({ @@ -31,5 +35,10 @@ export class Prometheus implements PromClient { help: 'Virustotal bad status report for a specific domain', labelNames: ['domain'] }); + this.ibmScore = new promClient.Gauge({ + name: 'ibm_xforce_score', + help: 'Ibm xforce score for a specific domain, 1 is good', + labelNames: ['domain'] + }); } } diff --git a/src/types.ts b/src/types.ts index 30aa921..4f98e82 100644 --- a/src/types.ts +++ b/src/types.ts @@ -13,8 +13,14 @@ export interface InputConfig { virusTotal: { apiKey: string; }; + ibmXforce: { + enabled: boolean; + apiKey: string; + apiPassword: string; + }; } export interface PromClient { - setVTReports(domain: string, reports: number): void; + setVTReport(domain: string, reports: number): void; + setIbmScore(domain: string, score: number): void; } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 941fc6d..70c7463 100644 --- a/yarn.lock +++ b/yarn.lock @@ -91,6 +91,18 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@sindresorhus/is@^4.0.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -119,6 +131,16 @@ "@types/connect" "*" "@types/node" "*" +"@types/cacheable-request@^6.0.1": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" + integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + "@types/connect@*": version "3.4.35" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" @@ -150,11 +172,23 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/http-cache-semantics@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" + integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== + "@types/json-schema@^7.0.3": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/keyv@*": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + "@types/mime@*": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" @@ -180,6 +214,13 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== +"@types/responselike@*", "@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/serve-static@*": version "1.15.0" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" @@ -454,6 +495,24 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-request@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" + integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + call-bind@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -499,6 +558,13 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + cloudflare@^2.9.1: version "2.9.1" resolved "https://registry.yarnpkg.com/cloudflare/-/cloudflare-2.9.1.tgz#9911b2a9ee3bfc2fc4f239c1f991892667e26309" @@ -671,11 +737,23 @@ debug@4, debug@^4.0.1, debug@^4.1.1: dependencies: ms "2.1.2" +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -765,6 +843,13 @@ encoding@>=0.1.4, encoding@~0.1.12: dependencies: iconv-lite "^0.6.2" +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + enquirer@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -1090,6 +1175,13 @@ get-stream@^3.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" integrity sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ== +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -1123,6 +1215,23 @@ globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" +got@^11: + version "11.8.5" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" + integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + got@^6.3.0: version "6.7.1" resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -1185,6 +1294,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -1205,6 +1319,14 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -1363,6 +1485,11 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -1405,6 +1532,13 @@ jsprim@^1.2.2: json-schema "0.4.0" verror "1.10.0" +keyv@^4.0.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.0.tgz#dbce9ade79610b6e641a9a65f2f6499ba06b9bc6" + integrity sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA== + dependencies: + json-buffer "3.0.1" + kuler@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" @@ -1449,6 +1583,11 @@ lowercase-keys@^1.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -1525,6 +1664,16 @@ mimelib@>=0.2.17: addressparser "~1.0.1" encoding "~0.1.12" +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -1597,6 +1746,11 @@ node-virustotal@^3.35.0: speedconcat "^1.0.2" tar ">=4.4.2" +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" @@ -1619,7 +1773,7 @@ on-finished@2.4.1: dependencies: ee-first "1.1.1" -once@^1.3.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -1645,6 +1799,11 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -1717,6 +1876,14 @@ psl@^1.1.28: resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -1734,6 +1901,11 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + ramda@^0.26.1: version "0.26.1" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" @@ -1816,6 +1988,11 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -1830,6 +2007,13 @@ resolve@^1.1.6: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"