From ec3ac82fcfea51110f503f08cd79974351246b29 Mon Sep 17 00:00:00 2001 From: Camden Moors <66680985+camdenmoors@users.noreply.github.com> Date: Tue, 25 Jan 2022 01:49:58 -0500 Subject: [PATCH 1/3] [WIP] Create `saf scan` --- package-lock.json | 515 ++++++++++++++++++++++++++++++++++++++ package.json | 3 + src/commands/scan/scan.ts | 98 +++++++- 3 files changed, 615 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index e047a601b..be5d85237 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "@types/node": "^14", "@types/prompt-sync": "^4.1.1", "@types/tmp": "^0.2.2", + "@types/ws": "^8.2.2", "chai": "^4", "csv-parse": "^4.16.0", "express": "^4.17.1", @@ -34,6 +35,7 @@ "flat": "^5.0.2", "genversion": "^3.0.1", "get-installed-path": "^4.0.8", + "got-cjs": "^12.0.1", "htmlparser2": "^6.1.0", "inspecjs": "^2.6.5", "lodash": "^4.17.21", @@ -44,6 +46,7 @@ "tslib": "^1", "typescript": "^3.3", "winston": "^3.4.0", + "ws": "^8.4.2", "xlsx-populate": "^1.21.0", "xml2js": "^0.4.23", "yaml": "^1.10.2" @@ -1753,6 +1756,17 @@ "node": ">=8.0.0" } }, + "node_modules/@sindresorhus/is": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.3.0.tgz", + "integrity": "sha512-wwOvh0eO3PiTEivGJWiZ+b946SlMSb4pe+y+Ur/4S87cwo09pYi+FWHHnbrM3W9W7cBYKDqQXcrFYjYUCOJUEQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, "node_modules/@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -1778,6 +1792,17 @@ "@types/node": "*" } }, + "node_modules/@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, "node_modules/@types/chai": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", @@ -1853,6 +1878,11 @@ "@types/node": "*" } }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, "node_modules/@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", @@ -1866,6 +1896,14 @@ "dev": true, "optional": true }, + "node_modules/@types/keyv": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/lodash": { "version": "4.14.178", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz", @@ -1915,6 +1953,14 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/serve-static": { "version": "1.13.10", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", @@ -1937,6 +1983,14 @@ "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.3.tgz", "integrity": "sha512-dDZH/tXzwjutnuk4UacGgFRwV+JSLaXL1ikvidfJprkb7L9Nx1njcRHHmi3Dsvt7pgqqTEeucQuOrWHPFgzVHA==" }, + "node_modules/@types/ws": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", + "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/xml2js": { "version": "0.4.9", "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.9.tgz", @@ -2463,6 +2517,39 @@ "node": ">= 0.8" } }, + "node_modules/cacheable-lookup": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.0.4.tgz", + "integrity": "sha512-mbcDEZCkv2CZF4G01kr8eBd/5agkt9oCqz75tJMSIsquvRZ2sL6Hi5zGVKi/0OSC9oO1GHfJ2AV0ZIOY9vye0A==", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2666,6 +2753,14 @@ "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", "dev": true }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, "node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -2883,6 +2978,31 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", @@ -2900,6 +3020,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, "node_modules/define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -3938,6 +4066,11 @@ } } }, + "node_modules/form-data-encoder": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", + "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==" + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -4141,6 +4274,70 @@ "node": ">= 4" } }, + "node_modules/got-cjs": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/got-cjs/-/got-cjs-12.0.1.tgz", + "integrity": "sha512-tRcTpee77XR9PNOp2Pu1P/XD67zkdeaBJ8kFz90for9dDTg6tqQKe64ORPbxFnB0mECKB+PQKCTcvJJdhv265g==", + "dependencies": { + "@sindresorhus/is": "^4.2.0", + "@szmarczak/http-timer": "4.0.6", + "@types/cacheable-request": "^6.0.2", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^6.0.4", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "form-data-encoder": "1.7.1", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.9", + "lowercase-keys": "2.0.0", + "p-cancelable": "2.1.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/got-cjs/node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/got-cjs/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/got-cjs/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/got-cjs/node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "engines": { + "node": ">=8" + } + }, "node_modules/graceful-fs": { "version": "4.2.9", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", @@ -4247,6 +4444,11 @@ "entities": "^2.0.0" } }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, "node_modules/http-call": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/http-call/-/http-call-5.3.0.tgz", @@ -4278,6 +4480,18 @@ "node": ">= 0.6" } }, + "node_modules/http2-wrapper": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.10.tgz", + "integrity": "sha512-QHgsdYkieKp+6JbXP25P+tepqiHYd+FVnDwXpxi/BlUcoIB0nsmTOymTNvETuTO+pDuwcSklPE72VR3DqV+Haw==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/hyperlinker": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz", @@ -4794,6 +5008,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -4880,6 +5099,14 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/keyv": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz", + "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", @@ -5206,6 +5433,14 @@ "node": ">=4" } }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -5431,6 +5666,17 @@ "node": ">=10" } }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -5996,6 +6242,17 @@ } ] }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ramda": { "version": "0.26.1", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", @@ -6071,6 +6328,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "node_modules/resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", @@ -6092,6 +6354,22 @@ "node": ">=4" } }, + "node_modules/responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dependencies": { + "lowercase-keys": "^2.0.0" + } + }, + "node_modules/responselike/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, "node_modules/restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -7191,6 +7469,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ws": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz", + "integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xlsx-populate": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/xlsx-populate/-/xlsx-populate-1.21.0.tgz", @@ -8774,6 +9072,11 @@ "fancy-test": "^1.4.10" } }, + "@sindresorhus/is": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.3.0.tgz", + "integrity": "sha512-wwOvh0eO3PiTEivGJWiZ+b946SlMSb4pe+y+Ur/4S87cwo09pYi+FWHHnbrM3W9W7cBYKDqQXcrFYjYUCOJUEQ==" + }, "@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -8799,6 +9102,17 @@ "@types/node": "*" } }, + "@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, "@types/chai": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", @@ -8874,6 +9188,11 @@ "@types/node": "*" } }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, "@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", @@ -8887,6 +9206,14 @@ "dev": true, "optional": true }, + "@types/keyv": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "requires": { + "@types/node": "*" + } + }, "@types/lodash": { "version": "4.14.178", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz", @@ -8936,6 +9263,14 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "requires": { + "@types/node": "*" + } + }, "@types/serve-static": { "version": "1.13.10", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", @@ -8958,6 +9293,14 @@ "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.3.tgz", "integrity": "sha512-dDZH/tXzwjutnuk4UacGgFRwV+JSLaXL1ikvidfJprkb7L9Nx1njcRHHmi3Dsvt7pgqqTEeucQuOrWHPFgzVHA==" }, + "@types/ws": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", + "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", + "requires": { + "@types/node": "*" + } + }, "@types/xml2js": { "version": "0.4.9", "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.9.tgz", @@ -9327,6 +9670,32 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" }, + "cacheable-lookup": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.0.4.tgz", + "integrity": "sha512-mbcDEZCkv2CZF4G01kr8eBd/5agkt9oCqz75tJMSIsquvRZ2sL6Hi5zGVKi/0OSC9oO1GHfJ2AV0ZIOY9vye0A==" + }, + "cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "requires": { + "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" + }, + "dependencies": { + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -9486,6 +9855,14 @@ "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", "dev": true }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, "color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -9656,6 +10033,21 @@ "ms": "2.1.2" } }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", @@ -9670,6 +10062,11 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, "define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -10471,6 +10868,11 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==" }, + "form-data-encoder": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", + "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==" + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -10623,6 +11025,51 @@ } } }, + "got-cjs": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/got-cjs/-/got-cjs-12.0.1.tgz", + "integrity": "sha512-tRcTpee77XR9PNOp2Pu1P/XD67zkdeaBJ8kFz90for9dDTg6tqQKe64ORPbxFnB0mECKB+PQKCTcvJJdhv265g==", + "requires": { + "@sindresorhus/is": "^4.2.0", + "@szmarczak/http-timer": "4.0.6", + "@types/cacheable-request": "^6.0.2", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^6.0.4", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "form-data-encoder": "1.7.1", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.9", + "lowercase-keys": "2.0.0", + "p-cancelable": "2.1.1", + "responselike": "^2.0.0" + }, + "dependencies": { + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" + } + } + }, "graceful-fs": { "version": "4.2.9", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", @@ -10700,6 +11147,11 @@ "entities": "^2.0.0" } }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, "http-call": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/http-call/-/http-call-5.3.0.tgz", @@ -10725,6 +11177,15 @@ "toidentifier": "1.0.1" } }, + "http2-wrapper": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.10.tgz", + "integrity": "sha512-QHgsdYkieKp+6JbXP25P+tepqiHYd+FVnDwXpxi/BlUcoIB0nsmTOymTNvETuTO+pDuwcSklPE72VR3DqV+Haw==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + } + }, "hyperlinker": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz", @@ -11112,6 +11573,11 @@ "esprima": "^4.0.0" } }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -11197,6 +11663,14 @@ } } }, + "keyv": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz", + "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==", + "requires": { + "json-buffer": "3.0.1" + } + }, "kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", @@ -11451,6 +11925,11 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -11634,6 +12113,11 @@ "validate-npm-package-license": "^3.0.1" } }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -12049,6 +12533,11 @@ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, "ramda": { "version": "0.26.1", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", @@ -12106,6 +12595,11 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", @@ -12121,6 +12615,21 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "requires": { + "lowercase-keys": "^2.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -12964,6 +13473,12 @@ "write-file-atomic": "^3.0.0" } }, + "ws": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz", + "integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==", + "requires": {} + }, "xlsx-populate": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/xlsx-populate/-/xlsx-populate-1.21.0.tgz", diff --git a/package.json b/package.json index 34e33a8a0..4ae0f61c0 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@types/node": "^14", "@types/prompt-sync": "^4.1.1", "@types/tmp": "^0.2.2", + "@types/ws": "^8.2.2", "chai": "^4", "csv-parse": "^4.16.0", "express": "^4.17.1", @@ -31,6 +32,7 @@ "flat": "^5.0.2", "genversion": "^3.0.1", "get-installed-path": "^4.0.8", + "got-cjs": "^12.0.1", "htmlparser2": "^6.1.0", "inspecjs": "^2.6.5", "lodash": "^4.17.21", @@ -41,6 +43,7 @@ "tslib": "^1", "typescript": "^3.3", "winston": "^3.4.0", + "ws": "^8.4.2", "xlsx-populate": "^1.21.0", "xml2js": "^0.4.23", "yaml": "^1.10.2" diff --git a/src/commands/scan/scan.ts b/src/commands/scan/scan.ts index 66cc2cefb..8eddf7f92 100644 --- a/src/commands/scan/scan.ts +++ b/src/commands/scan/scan.ts @@ -1,4 +1,6 @@ import {Command, flags} from '@oclif/command' +import got from 'got-cjs' +import ws from 'ws' export default class Scan extends Command { static aliases = ['scan'] @@ -7,10 +9,104 @@ export default class Scan extends Command { static flags = { help: flags.help({char: 'h'}), + profile: flags.integer({char: 'p', description: 'ID of the profile you would like to run'}), + target: flags.string({char: 't', description: 'Standard target URI (e.g ssh://root:password@192.168.1.1)'}), + method: flags.string({char: 'm', default: 'ssh', options: ['ssh', 'winrm'], description: 'Transport method to evaluate profiles'}), + username: flags.string({char: 'u', description: 'Username to authenticate with'}), + password: flags.string({char: 'P', description: 'Password to authenticate with'}) + } + + async makeRequest(endpoint: string, method: 'GET' | 'POST', data?: Record) { + const request = got(`unix:/var/run/docker.sock:${endpoint}`, { + method: method, + json: data + }) + + return request.json() + } + + async getProfiles() { + return got.get('https://raw.githubusercontent.com/mitre/mitre-saf/master/src/assets/data/baselines.json').json().then((profiles: any) => { + const mappedProfiles: Record = {} + profiles.baselines.forEach((profile: any) => { + if(profile.link) { + mappedProfiles[profile.shortName] = profile.link + } + }) + return mappedProfiles + }) + } + + async handleContainerWebsocketConnection(id: string) { + var client = new ws(`ws+unix:///var/run/docker.sock:/containers/${id}/attach/ws?stream=1&stdout=1&stdin=1&logs=1`) + + client.on('open', () => { + console.log(`Connected to container ${id}`) + }); + + client.on('message', (data: Buffer) => { + console.log(data.toString()) + }); + + client.on('error', (error) => { + console.error(error) + }) + + client.on('close', () => { + console.log(`Disconnected from container ${id}`) + }) + } + + async createContainer(image: string, args: string[]): Promise<{Id: string, Warnings: string[]}> { + return this.makeRequest('/containers/create', 'POST', { + Image: image, + Cmd: args + }) as Promise<{Id: string, Warnings: string[]}> + } + + async getContainerInfo(id: string) { + return this.makeRequest(`/v1.24/containers/${id}/json`, "GET") + } + + async startContainer(id: string) { + return this.makeRequest(`/containers/${id}/start`, 'POST') + } + + async getContainerLogs(name: string) { + return this.makeRequest(`/v1.24/containers/${name}/logs`, 'GET') } async run() { - console.log('Visit https://saf.mitre.org/#/validate to explore and run inspec profiles') + const {flags} = this.parse(Scan) + + if (process.getuid() !== 0) { + throw new Error("Please run this command as root so SAF can communicate with the Docker interface") + } + + const availableProfiles = Object.entries(await this.getProfiles()) + + if(flags.profile) { + const profile = availableProfiles[flags.profile - 1] + if(profile) { + const [name, url] = profile + console.log(`Selected profile ${name} (${url})`) + // Create a CINC Auditor container + const containerId = await this.createContainer('cincproject/auditor', ['exec', '--help']).then((containerResponse) => containerResponse.Id) + // Start the container + await this.startContainer(containerId) + // Get the logs from the container + await this.handleContainerWebsocketConnection(containerId) + } else { + console.error(`Invalid profile selected, please choose a number 1-${availableProfiles.length-2}`) + } + } else { + availableProfiles.forEach(([shortName, url], index) => { + console.log(`${index+1}) ${shortName}`) + }) + + return console.log("No profile selected, please choose one of the profiles shown above.") + } + } } From 82cedd535c6522c4c5851f2513fa9d848155786c Mon Sep 17 00:00:00 2001 From: Camden Moors <66680985+camdenmoors@users.noreply.github.com> Date: Tue, 25 Jan 2022 01:53:48 -0500 Subject: [PATCH 2/3] Lint --- .eslintrc | 1 + src/commands/scan/scan.ts | 43 +++++++++++++++++++-------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.eslintrc b/.eslintrc index 0ead13c01..0a117f669 100644 --- a/.eslintrc +++ b/.eslintrc @@ -12,6 +12,7 @@ "unicorn/no-zero-fractions": "off", "unicorn/no-array-for-each": "off", "unicorn/explicit-length-check": "off", + "new-cap": "off", "no-console": "off", "node/no-missing-import": "off", "complexity": "off" diff --git a/src/commands/scan/scan.ts b/src/commands/scan/scan.ts index 8eddf7f92..fe77af6f5 100644 --- a/src/commands/scan/scan.ts +++ b/src/commands/scan/scan.ts @@ -13,15 +13,15 @@ export default class Scan extends Command { target: flags.string({char: 't', description: 'Standard target URI (e.g ssh://root:password@192.168.1.1)'}), method: flags.string({char: 'm', default: 'ssh', options: ['ssh', 'winrm'], description: 'Transport method to evaluate profiles'}), username: flags.string({char: 'u', description: 'Username to authenticate with'}), - password: flags.string({char: 'P', description: 'Password to authenticate with'}) + password: flags.string({char: 'P', description: 'Password to authenticate with'}), } async makeRequest(endpoint: string, method: 'GET' | 'POST', data?: Record) { const request = got(`unix:/var/run/docker.sock:${endpoint}`, { - method: method, - json: data + method: method, + json: data, }) - + return request.json() } @@ -29,7 +29,7 @@ export default class Scan extends Command { return got.get('https://raw.githubusercontent.com/mitre/mitre-saf/master/src/assets/data/baselines.json').json().then((profiles: any) => { const mappedProfiles: Record = {} profiles.baselines.forEach((profile: any) => { - if(profile.link) { + if (profile.link) { mappedProfiles[profile.shortName] = profile.link } }) @@ -38,17 +38,17 @@ export default class Scan extends Command { } async handleContainerWebsocketConnection(id: string) { - var client = new ws(`ws+unix:///var/run/docker.sock:/containers/${id}/attach/ws?stream=1&stdout=1&stdin=1&logs=1`) + const client = new ws(`ws+unix:///var/run/docker.sock:/containers/${id}/attach/ws?stream=1&stdout=1&stdin=1&logs=1`) client.on('open', () => { console.log(`Connected to container ${id}`) - }); + }) client.on('message', (data: Buffer) => { console.log(data.toString()) - }); + }) - client.on('error', (error) => { + client.on('error', error => { console.error(error) }) @@ -57,15 +57,15 @@ export default class Scan extends Command { }) } - async createContainer(image: string, args: string[]): Promise<{Id: string, Warnings: string[]}> { + async createContainer(image: string, args: string[]): Promise<{Id: string; Warnings: string[]}> { return this.makeRequest('/containers/create', 'POST', { - Image: image, - Cmd: args - }) as Promise<{Id: string, Warnings: string[]}> + Image: image, + Cmd: args, + }) as Promise<{Id: string; Warnings: string[]}> } async getContainerInfo(id: string) { - return this.makeRequest(`/v1.24/containers/${id}/json`, "GET") + return this.makeRequest(`/v1.24/containers/${id}/json`, 'GET') } async startContainer(id: string) { @@ -80,33 +80,32 @@ export default class Scan extends Command { const {flags} = this.parse(Scan) if (process.getuid() !== 0) { - throw new Error("Please run this command as root so SAF can communicate with the Docker interface") + throw new Error('Please run this command as root so SAF can communicate with the Docker interface') } const availableProfiles = Object.entries(await this.getProfiles()) - if(flags.profile) { + if (flags.profile) { const profile = availableProfiles[flags.profile - 1] - if(profile) { + if (profile) { const [name, url] = profile console.log(`Selected profile ${name} (${url})`) // Create a CINC Auditor container - const containerId = await this.createContainer('cincproject/auditor', ['exec', '--help']).then((containerResponse) => containerResponse.Id) + const containerId = await this.createContainer('cincproject/auditor', ['exec', '--help']).then(containerResponse => containerResponse.Id) // Start the container await this.startContainer(containerId) // Get the logs from the container await this.handleContainerWebsocketConnection(containerId) } else { - console.error(`Invalid profile selected, please choose a number 1-${availableProfiles.length-2}`) + console.error(`Invalid profile selected, please choose a number 1-${availableProfiles.length - 2}`) } } else { availableProfiles.forEach(([shortName, url], index) => { - console.log(`${index+1}) ${shortName}`) + console.log(`${index + 1}) ${shortName}`) }) - return console.log("No profile selected, please choose one of the profiles shown above.") + return console.log('No profile selected, please choose one of the profiles shown above.') } - } } From 46f0918382c8a204788f59d2d9b69ed971acbeda Mon Sep 17 00:00:00 2001 From: Camden Moors <66680985+camdenmoors@users.noreply.github.com> Date: Tue, 25 Jan 2022 22:54:55 -0500 Subject: [PATCH 3/3] Support --targetURL or --method/--host/--user/--password, delete container after use, promisify websocket connection --- src/commands/scan/scan.ts | 85 +++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 22 deletions(-) diff --git a/src/commands/scan/scan.ts b/src/commands/scan/scan.ts index fe77af6f5..7c1afadc3 100644 --- a/src/commands/scan/scan.ts +++ b/src/commands/scan/scan.ts @@ -10,16 +10,19 @@ export default class Scan extends Command { static flags = { help: flags.help({char: 'h'}), profile: flags.integer({char: 'p', description: 'ID of the profile you would like to run'}), - target: flags.string({char: 't', description: 'Standard target URI (e.g ssh://root:password@192.168.1.1)'}), + targetURL: flags.string({char: 't', description: 'Standard target URI (e.g ssh://root:password@192.168.1.1)'}), method: flags.string({char: 'm', default: 'ssh', options: ['ssh', 'winrm'], description: 'Transport method to evaluate profiles'}), - username: flags.string({char: 'u', description: 'Username to authenticate with'}), - password: flags.string({char: 'P', description: 'Password to authenticate with'}), + host: flags.string({char: 'H', description: 'Target host to scan'}), + username: flags.string({char: 'u', description: 'Username to authenticate with', required: false}), + password: flags.string({char: 'P', description: 'Password to authenticate with', required: false}), + output: flags.string({char: 'o', description: 'Output HDF file', default: 'output.json'}) } - async makeRequest(endpoint: string, method: 'GET' | 'POST', data?: Record) { + async makeRequest(endpoint: string, method: 'GET' | 'POST' | 'DELETE', data?: Record): any { const request = got(`unix:/var/run/docker.sock:${endpoint}`, { method: method, json: data, + throwHttpErrors: false }) return request.json() @@ -38,30 +41,47 @@ export default class Scan extends Command { } async handleContainerWebsocketConnection(id: string) { - const client = new ws(`ws+unix:///var/run/docker.sock:/containers/${id}/attach/ws?stream=1&stdout=1&stdin=1&logs=1`) + return new Promise((resolve, reject) => { + const client = new ws(`ws+unix:///var/run/docker.sock:/containers/${id}/attach/ws?stream=1&stdout=1&stdin=1&logs=1`) - client.on('open', () => { - console.log(`Connected to container ${id}`) - }) + client.on('open', () => { + console.log(`Connected to container ${id}`) + }) - client.on('message', (data: Buffer) => { - console.log(data.toString()) - }) + client.on('message', (data: Buffer) => { + console.log(data.toString()) + }) - client.on('error', error => { - console.error(error) - }) + client.on('error', error => { + reject(error) + }) - client.on('close', () => { - console.log(`Disconnected from container ${id}`) + client.on('close', () => { + resolve(id) + }) }) + } - async createContainer(image: string, args: string[]): Promise<{Id: string; Warnings: string[]}> { + async createContainer(image: string, args: string[]): Promise { return this.makeRequest('/containers/create', 'POST', { Image: image, Cmd: args, - }) as Promise<{Id: string; Warnings: string[]}> + HostConfig: { + Binds: [ + `${process.cwd()}:/share` + ] + } + }).then((fulfilment: {Id: string}) => { + console.log(`Created container ${fulfilment.Id}`) + return fulfilment.Id + }) + } + + async deleteContainer(id: string) { + return this.makeRequest(`/containers/${id}`, 'DELETE').then(() => { + console.log(`Deleted container ${id}`) + }) } async getContainerInfo(id: string) { @@ -69,11 +89,19 @@ export default class Scan extends Command { } async startContainer(id: string) { - return this.makeRequest(`/containers/${id}/start`, 'POST') + return this.makeRequest(`/containers/${id}/start`, 'POST').then(() => { + console.log(`Started container ${id}`) + }) } - async getContainerLogs(name: string) { - return this.makeRequest(`/v1.24/containers/${name}/logs`, 'GET') + buildTargetURL(method: string, host: string, username: string, password: string) { + if (method === 'ssh') { + return ['--target', `${method}://${username}:${password}@${host}`] + } else if (method === 'winrm') { + return ['--backend', 'winrm', '--user', username, '--password', password, '--host', host] + } else { + throw new Error("Invalid transport method") + } } async run() { @@ -91,11 +119,24 @@ export default class Scan extends Command { const [name, url] = profile console.log(`Selected profile ${name} (${url})`) // Create a CINC Auditor container - const containerId = await this.createContainer('cincproject/auditor', ['exec', '--help']).then(containerResponse => containerResponse.Id) + let containerId; + if(flags.targetURL) { + containerId = await this.createContainer('cincproject/auditor', + ["exec", url, "--log-level", "debug", "--reporter", `json:${flags.output}`, "--target", flags.targetURL] + ) + } else if (flags.method && flags.host && flags.username && flags.password) { + containerId = await this.createContainer('cincproject/auditor', + ["exec", url, "--log-level", "debug", "--reporter", `json:${flags.output}`, ...this.buildTargetURL(flags.method, flags.host, flags.username, flags.password)] + ) + } else { + throw new Error("Please provide a target url (--target) or use --method, --host, --username, and --password ") + } // Start the container await this.startContainer(containerId) // Get the logs from the container await this.handleContainerWebsocketConnection(containerId) + // Delete the container + await this.deleteContainer(containerId) } else { console.error(`Invalid profile selected, please choose a number 1-${availableProfiles.length - 2}`) }