diff --git a/package.json b/package.json index 82ee8c122ca..ce92a01848f 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "tools:validate-bundle-names": "TS_NODE_PROJECT=./tools/tsconfig.json node --trace-deprecation --experimental-modules --abort-on-uncaught-exception --loader ts-node/esm --experimental-specifier-resolution=node ./tools/validate-bundle-names.js", "tools:bump-openapi-spec-dep-versions": "TS_NODE_PROJECT=./tools/tsconfig.json node --trace-deprecation --experimental-modules --abort-on-uncaught-exception --loader ts-node/esm --experimental-specifier-resolution=node ./tools/bump-openapi-spec-dep-versions.ts", "tools:get-latest-sem-ver-git-tag": "TS_NODE_PROJECT=./tools/tsconfig.json node --abort-on-uncaught-exception --loader ts-node/esm --experimental-specifier-resolution=node --no-warnings ./tools/get-latest-sem-ver-git-tag.ts", + "tools:generate-sbom": "TS_NODE_PROJECT=tools/tsconfig.json node --experimental-json-modules --trace-deprecation --experimental-modules --abort-on-uncaught-exception --loader ts-node/esm --experimental-specifier-resolution=node ./tools/generate-sbom.ts", "generate-api-server-config": "node ./tools/generate-api-server-config.js", "sync-ts-config": "TS_NODE_PROJECT=tools/tsconfig.json node --experimental-json-modules --loader ts-node/esm ./tools/sync-npm-deps-to-tsc-projects.ts", "start:api-server": "node ./packages/cactus-cmd-api-server/dist/lib/main/typescript/cmd/cactus-api.js --config-file=.config.json", @@ -141,6 +142,7 @@ "karma-electron": "7.0.0", "karma-tap": "4.2.0", "karma-webpack": "5.0.0", + "license-report": "6.4.0", "lint-staged": "11.1.2", "make-dir-cli": "3.0.0", "node-polyfill-webpack-plugin": "1.1.4", diff --git a/tools/generate-sbom.ts b/tools/generate-sbom.ts new file mode 100644 index 00000000000..e84329196a9 --- /dev/null +++ b/tools/generate-sbom.ts @@ -0,0 +1,264 @@ +import { fileURLToPath } from "url"; +import { dirname } from "path"; +import path from "path"; +import { promisify } from "util"; +import { exec, ExecOptions } from "child_process"; +import fs from "fs-extra"; +import { globby, Options as GlobbyOptions } from "globby"; +import { RuntimeError } from "run-time-error"; +import fastSafeStringify from "fast-safe-stringify"; +import { INpmListDependencyV1, npmList } from "./npm-list"; + +const execAsync = promisify(exec); + +async function getManifestFiles(req: { + PROJECT_DIR: string; +}): Promise<{ readonly manifestFilePaths: string[] }> { + const { PROJECT_DIR } = req; + + const MANIFEST_INCLUDE_GLOBS = [ + // FIXME make this compatible with the other (currently commented out) + // manifest files for a complete picture of the dependencies involved. + // + "**/go.mod", + "**/Cargo.toml", + "**/build.gradle*", + "yarn.lock", + "**/package.json", + ]; + + const MANIFEST_EXCLUDE_GLOBS = ["**/node_modules/**"]; + + const globbyOptions: GlobbyOptions = { + cwd: PROJECT_DIR, + absolute: true, + ignore: MANIFEST_EXCLUDE_GLOBS, + }; + const manifestFilePaths = await globby(MANIFEST_INCLUDE_GLOBS, globbyOptions); + return { manifestFilePaths }; +} + +/** + * # Software Bill of Materials Generator Script + * + * How does it work: + * 1. It uses a list of glob patterns to find manifest files defining dependencies. + * For example build.gradle, yarn.lock, etc. (For now only npm package.json files + * are supported unfortunately) + * 2. Once a complete list of these files have been gathered, it iterates through + * their respective directories and runs the SBoM generator tool. + * 3. The results of each execution are appended to a .csv file where there is + * a field called "related to" which will contain the manifest file's relative + * path within the project directory. + */ +const main = async (argv: string[], env: NodeJS.ProcessEnv) => { + if (!argv) { + throw new RuntimeError(`Process argv cannot be falsy.`); + } + if (!env) { + throw new RuntimeError(`Process env cannot be falsy.`); + } + + const TAG = "[tools/generate-sbom.ts] "; + const __filename = fileURLToPath(import.meta.url); + const __dirname = dirname(__filename); + const SCRIPT_DIR = __dirname; + const PROJECT_DIR = path.join(SCRIPT_DIR, "../"); + console.log(`SCRIPT_DIR=${SCRIPT_DIR}`); + console.log(`PROJECT_DIR=${PROJECT_DIR}`); + + const getManifestFilesRes = await getManifestFiles({ PROJECT_DIR }); + const globbedManifestFileCount = getManifestFilesRes.manifestFilePaths.length; + console.log(`Found ${globbedManifestFileCount} package.json files via glob.`); + + const { dependencies } = await npmList({ PROJECT_DIR }); + const manifestFilePaths: Set = new Set(); + Object.entries(dependencies).forEach(([, v]) => { + traverseDeps(v, manifestFilePaths); + }); + + const sbomCacheDir = path.join(PROJECT_DIR, ".cacti-tools", "cache", "sbom"); + await fs.mkdirp(sbomCacheDir); + console.log("Created SBoM cache dir at: ", sbomCacheDir); + + const sbomDir = path.join(PROJECT_DIR, "dist", "sbom"); + await fs.mkdirp(sbomDir); + console.log("Created SBoM dir at: ", sbomDir); + + const dateAndTime = new Date().toJSON().slice(0, 24).replaceAll(":", "-"); + const filename = `cacti_sbom_nodejs_${dateAndTime}.csv`; + const specFileReportPathAbs = path.join(sbomDir, filename); + console.log("Streaming data to SBoM csv file at: ", specFileReportPathAbs); + + const manifestCount = manifestFilePaths.size; + let runtimeMsSum = 0; + let idx = 0; + let csvHeadersEnabled = true; + for (const manifestFilePath of manifestFilePaths) { + idx++; + const start = new Date(); + const pkgDirPath = path.dirname(manifestFilePath); + const dirPath = path.relative(PROJECT_DIR, pkgDirPath); + const manifestRelPath = path.relative(PROJECT_DIR, manifestFilePath); + const req = { + dirPath, + TAG, + csvHeadersEnabled, + manifestFilePath, + PROJECT_DIR, + }; + try { + const res = await generateSBoM(req); + csvHeadersEnabled = false; + const csvContent = res.stdout; // avoid empty lines in .csv file + await fs.appendFile(specFileReportPathAbs, csvContent); + const end = new Date(); + const runtimeMs = end.getTime() - start.getTime(); + runtimeMsSum += runtimeMs; + + const { logMessage } = createDiagnosticsMessage({ + idx, + manifestCount, + manifestRelPath, + runtimeMsSum, + runtimeMs, + }); + console.log(logMessage); + } catch (ex: unknown) { + // If it was a syntax error in the package.json file + // then we just log it as a warning and move on. + if (ex instanceof ManifestParseError) { + console.warn(ex); + } else { + const msg = `Failed to generate SBoM for ${req.manifestFilePath}`; + const throwable = ex instanceof Error ? ex : fastSafeStringify(ex); + throw new RuntimeError(msg, throwable); + } + } + } +}; + +function traverseDeps(root: INpmListDependencyV1, paths: Set): void { + if (root.path) { + paths.add(root.path.concat("/package.json")); + } else { + console.warn(`MISSING PATH => ${JSON.stringify(root).substring(0, 4000)}`); + } + if (!root.dependencies) { + return; + } + Object.entries(root.dependencies).forEach(([, v]) => { + traverseDeps(v, paths); + }); +} + +function createDiagnosticsMessage(req: { + readonly idx: number; + readonly runtimeMs: number; + readonly runtimeMsSum: number; + readonly manifestCount: number; + readonly manifestRelPath: string; +}): { logMessage: string } { + const { idx, manifestCount, runtimeMsSum, runtimeMs, manifestRelPath } = req; + const percentage = ((idx / manifestCount) * 100).toFixed(2); + const progressInfo = `${percentage}%\t${idx}/\t\t${manifestCount}`; + const avgRuntimeMs = runtimeMsSum / idx; + const estRuntimeMin = Math.ceil((avgRuntimeMs * manifestCount) / 60000); + const logMessage = `${progressInfo}\t\testRuntimeMin=${estRuntimeMin}\t${manifestRelPath}\t\t\t\t\truntimeMs=${runtimeMs}ms`; + return { logMessage }; +} + +export async function lernaPkgList(req: { + readonly PROJECT_DIR: string; +}): Promise<{ readonly pkgNames: string[] }> { + const TAG = "[tools/generate-sbom.ts/lernaPkgList()]"; + const shellCmd = `./node_modules/.bin/lerna ls --json --all --no-progress --loglevel=silent`; + const execOpts: ExecOptions = { + cwd: req.PROJECT_DIR, + maxBuffer: 32 * 1024 * 1024, // 32 MB of stdout will be allowed + }; + + try { + const { stderr, stdout } = await execAsync(shellCmd, execOpts); + if (stderr) { + console.error(`${TAG} shell CMD: ${shellCmd}`); + console.error(`${TAG} stderr of the above command: ${stderr}`); + } + const pkgs = JSON.parse(stdout); + const pkgNames = pkgs.map((x: { name: string }) => x.name); + return { pkgNames }; + } catch (ex: unknown) { + const msg = `${TAG} Failed to execute shell CMD: ${shellCmd}`; + const throwable = ex instanceof Error ? ex : fastSafeStringify(ex); + throw new RuntimeError(msg, throwable); + } +} + +export async function generateSBoM(req: { + readonly TAG: string; + readonly csvHeadersEnabled: boolean; + readonly manifestFilePath: string; + readonly dirPath: string; + readonly PROJECT_DIR: string; +}): Promise<{ + readonly manifestFilePath: string; + readonly stderr: string; + readonly stdout: string; +}> { + const { csvHeadersEnabled, TAG, PROJECT_DIR, manifestFilePath } = req; + + const manifestRelPath = path.relative(PROJECT_DIR, manifestFilePath); + const executable = `./node_modules/.bin/license-report`; + + const { pkgNames } = await lernaPkgList({ PROJECT_DIR }); + + const csvFields = [ + "department", + "relatedTo", + "name", + "licenseType", + "link", + "remoteVersion", + "installedVersion", + "definedVersion", + "author", + ]; + + const cmdArgs = [ + "--output=csv", + `--fields=${csvFields.join(" --fields=")} `, + "--exclude=" + pkgNames.join(" --exclude="), + csvHeadersEnabled ? " --csvHeaders " : "", + `--relatedTo.value=${manifestRelPath}`, + `--department.value='Hyperledger Cacti'`, + `--package=${manifestFilePath}`, + ].join(" "); + + const shellCmd = `${executable} ${cmdArgs}`; + + const execOpts: ExecOptions = { + cwd: req.PROJECT_DIR, + maxBuffer: 2 * 1024 * 1024, // 2 MB of stdout will be allowed + }; + + try { + const { stderr, stdout } = await execAsync(shellCmd, execOpts); + if (stderr) { + console.error(`${TAG} shell CMD: ${shellCmd}`); + console.error(`${TAG} stderr of the above command: ${stderr}`); + } + return { manifestFilePath, stderr, stdout }; + } catch (ex: unknown) { + const msg = `${TAG} Failed to execute shell CMD: ${shellCmd}`; + if (ex instanceof Error && ex.message.includes("SyntaxError: ")) { + throw new ManifestParseError(msg, ex); + } else { + const throwable = ex instanceof Error ? ex : fastSafeStringify(ex); + throw new RuntimeError(msg, throwable); + } + } +} + +export class ManifestParseError extends RuntimeError {} + +main(process.argv, process.env); diff --git a/tools/npm-list.ts b/tools/npm-list.ts new file mode 100644 index 00000000000..9bcb0758e72 --- /dev/null +++ b/tools/npm-list.ts @@ -0,0 +1,87 @@ +import { ExecOptions, exec } from "child_process"; +import { promisify } from "util"; + +import fastSafeStringify from "fast-safe-stringify"; +import { RuntimeError } from "run-time-error"; +import { hasKey } from "./has-key"; + +const execAsync = promisify(exec); + +export interface INpmListRequestV1 { + readonly PROJECT_DIR: string; +} + +export interface INpmListResponseV1 { + readonly dependencies: Record; +} + +export interface INpmListRepositoryV1 { + readonly type: string; + readonly url: string; + readonly directory?: string; +} + +export interface INpmListDependencyV1 { + readonly version: string; + readonly resolved: string; + readonly overridden: boolean; + readonly name: string; + readonly description: string; + readonly author: string | { readonly name: string; readonly email: string }; + readonly homepage: string; + readonly license: string; + readonly repository: INpmListRepositoryV1 | string; + + readonly _id: string; + readonly extraneous: boolean; + readonly path: string; + + readonly dependencies?: Record; +} + +export async function npmList( + req: INpmListRequestV1, +): Promise { + const TAG = "[tools/generate-sbom.ts#npmList()]"; + const shellCmd = `npm ls --all --json --long --include-workspace-root --loglevel=silent`; + + const { PROJECT_DIR } = req; + + const execOpts: ExecOptions = { + cwd: PROJECT_DIR, + maxBuffer: 256 * 1024 * 1024, + }; + + try { + const { stderr, stdout } = await execAsync(shellCmd, execOpts); + if (stderr) { + console.error(`${TAG} shell CMD: ${shellCmd}`); + console.error(`${TAG} stderr of the above command: ${stderr}`); + } + return JSON.parse(stdout); + } catch (ex: unknown) { + // We have to detect if npm is giving a non-zero exit code only because + // it found some extraneous dependencies (in which case it's output of + // the list of dependencies is still a valid JSON document that is still + // 100% valid for our intents and purposes) + const canHandle = + ex instanceof Error && + hasKey(ex, "code") && + hasKey(ex, "signal") && + hasKey(ex, "stderr") && + hasKey(ex, "stdout") && + ex.code === 1 && + ex.signal === null && + ex.stderr === "" && + typeof ex.stdout === "string" && + ex.stdout.length > 0; + + if (canHandle) { + return JSON.parse(ex.stdout as string); + } else { + const msg = `${TAG} Failed to execute shell CMD: ${shellCmd}`; + const throwable = ex instanceof Error ? ex : fastSafeStringify(ex); + throw new RuntimeError(msg, throwable); + } + } +} diff --git a/tools/tsconfig.json b/tools/tsconfig.json index e86507449cd..34ef4a4d145 100644 --- a/tools/tsconfig.json +++ b/tools/tsconfig.json @@ -70,6 +70,6 @@ /* Advanced Options */ "skipLibCheck": true, /* Skip type checking of declaration files. */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "forceConsistentCasingInFileNames": true } } diff --git a/yarn.lock b/yarn.lock index 8ad127e2c4f..daae9112c48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4676,6 +4676,11 @@ resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== +"@kessler/tableify@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@kessler/tableify/-/tableify-1.0.2.tgz#44fb4df25a3d3e4bb8b286e80c31f02b961152ac" + integrity sha512-e4psVV9Fe2eBfS9xK2rzQ9lE5xS4tARm7EJzDb6sVZy3F+EMyHJ67i0NdBVR9BRyQx7YhogMCbB6R1QwXuBxMg== + "@kwsites/file-exists@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz#ad1efcac13e1987d8dbaf235ef3be5b0d96faa99" @@ -5770,6 +5775,11 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== +"@sindresorhus/is@^5.2.0": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.5.2.tgz#cbfd816d556b941f65d32cfc6d708359b826e7ea" + integrity sha512-8ZMK+V6YpeZFfW6hU9uAeWVuq8v3t7BaG276gIO+kVqnAcLrHCXdFUOf7kgouyfAarkZtuavIqY3RsXTsTWviw== + "@sinonjs/commons@^1", "@sinonjs/commons@^1.0.2", "@sinonjs/commons@^1.3.0": version "1.8.6" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" @@ -5842,20 +5852,10 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== -"@socket.io/component-emitter@~3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" - integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== - -"@stencil/core@^2.14.2": - version "2.15.2" - resolved "https://registry.yarnpkg.com/@stencil/core/-/core-2.15.2.tgz#73a18050714a9edc488e6a2ea3380f5a91a04690" - integrity sha512-D6dv2KAXlWt9mjC28q0s6anghQgXRn0k93suOf+4pqsv1Uq19zNJXpYL68N5GxMSiNZyMPTU4Tt2NCbut7DVGg== - -"@stencil/core@~2.12.0": - version "2.12.1" - resolved "https://registry.yarnpkg.com/@stencil/core/-/core-2.12.1.tgz#92364d3e57337b8d36dd9b70cfbed2833929f22d" - integrity sha512-u24TZ+FEvjnZt5ZgIkLjLpUNsO6Ml3mUZqwmqk81w6RWWz75hgB5p4RFI5rvuErFeh2xvMIGo+pNdG24XUBz1A== +"@stencil/core@^2.18.0": + version "2.22.3" + resolved "https://registry.yarnpkg.com/@stencil/core/-/core-2.22.3.tgz#83987e20bba855c450f6d6780e3a20192603f13f" + integrity sha512-kmVA0M/HojwsfkeHsifvHVIYe4l5tin7J5+DLgtl8h6WWfiMClND5K3ifCXXI2ETDNKiEk21p6jql3Fx9o2rng== "@szmarczak/http-timer@^1.1.2": version "1.1.2" @@ -6556,7 +6556,7 @@ dependencies: "@types/node" "*" -"@types/http-cache-semantics@*": +"@types/http-cache-semantics@*", "@types/http-cache-semantics@^4.0.1": 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== @@ -9951,6 +9951,24 @@ cacheable-lookup@^6.0.4: resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz#0330a543471c61faa4e9035db583aad753b36385" integrity sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww== +cacheable-lookup@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" + integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== + +cacheable-request@^10.2.8: + version "10.2.12" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.12.tgz#05b97a3199d1ee65c360eb45c5af6191faa3ab6b" + integrity sha512-qtWGB5kn2OLjx47pYUkWicyOpK1vy9XZhq8yRTXOy+KAmjjESSRLx6SiExnnaGGUP1NM6/vmygMu0fGylNh9tw== + dependencies: + "@types/http-cache-semantics" "^4.0.1" + get-stream "^6.0.1" + http-cache-semantics "^4.1.1" + keyv "^4.5.2" + mimic-response "^4.0.0" + normalize-url "^8.0.0" + responselike "^3.0.0" + cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -12699,11 +12717,6 @@ engine.io-parser@~5.0.3: dependencies: "@socket.io/base64-arraybuffer" "~1.0.2" -engine.io-parser@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.1.0.tgz#d593d6372d7f79212df48f807b8cace1ea1cb1b8" - integrity sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w== - engine.io@~6.1.0: version "6.1.3" resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.1.3.tgz#f156293d011d99a3df5691ac29d63737c3302e6f" @@ -12736,23 +12749,7 @@ engine.io@~6.2.1: engine.io-parser "~5.0.3" ws "~8.2.3" -engine.io@~6.5.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.5.0.tgz#02b9d9941c0d3ab2d46628e98ac3331dd533dff0" - integrity sha512-UlfoK1iD62Hkedw2TmuHdhDsZCGaAyp+LZ/AvnImjYBeWagA3qIEETum90d6shMeFZiDuGT66zVCdx1wKYKGGg== - dependencies: - "@types/cookie" "^0.4.1" - "@types/cors" "^2.8.12" - "@types/node" ">=10.0.0" - accepts "~1.3.4" - base64id "2.0.0" - cookie "~0.4.1" - cors "~2.8.5" - debug "~4.3.1" - engine.io-parser "~5.1.0" - ws "~8.11.0" - -enhanced-resolve@^5.0.0, enhanced-resolve@^5.10.0, enhanced-resolve@^5.9.2, enhanced-resolve@^5.9.3: +enhanced-resolve@^5.0.0, enhanced-resolve@^5.10.0: version "5.12.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== @@ -12792,6 +12789,11 @@ envinfo@^7.7.3: resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== +eol@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/eol/-/eol-0.9.1.tgz#f701912f504074be35c6117a5c4ade49cd547acd" + integrity sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg== + err-code@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" @@ -14882,6 +14884,11 @@ form-data-encoder@1.7.1: resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.1.tgz#ac80660e4f87ee0d3d3c3638b7da8278ddb8ec96" integrity sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg== +form-data-encoder@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" + integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== + form-data@4.0.0, form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -15318,6 +15325,11 @@ get-pkg-repo@^4.0.0: through2 "^2.0.0" yargs "^16.2.0" +get-stdin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + integrity sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA== + get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" @@ -15851,6 +15863,23 @@ got@^11.8.2: p-cancelable "^2.0.0" responselike "^2.0.0" +got@^12.6.0: + version "12.6.1" + resolved "https://registry.yarnpkg.com/got/-/got-12.6.1.tgz#8869560d1383353204b5a9435f782df9c091f549" + integrity sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.8" + decompress-response "^6.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" + got@^6.7.1: version "6.7.1" resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -19874,6 +19903,13 @@ keyv@^4.0.0: dependencies: json-buffer "3.0.1" +keyv@^4.5.2: + version "4.5.3" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.3.tgz#00873d2b046df737963157bd04f294ca818c9c25" + integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug== + dependencies: + json-buffer "3.0.1" + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -20196,6 +20232,21 @@ libtap@^1.4.0: tcompare "^5.0.6" trivial-deferred "^1.0.1" +license-report@6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/license-report/-/license-report-6.4.0.tgz#599989c4bee15d95fb1ee3f880b528d240458533" + integrity sha512-JjY8aQRSu+Kx8BLhEH6ltylWDD66OkA875aFtGDJ4qILGRgIsFwfNx63bOrD6UGi7CNF9X/dd8I7l/PF4Kexjg== + dependencies: + "@kessler/tableify" "^1.0.2" + debug "^4.3.4" + eol "^0.9.1" + got "^12.6.0" + rc "^1.2.8" + semver "^7.3.8" + tablemark "^3.0.0" + text-table "^0.2.0" + visit-values "^2.0.0" + license-webpack-plugin@4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz#1e18442ed20b754b82f1adeff42249b81d11aec6" @@ -20688,6 +20739,13 @@ lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" integrity sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA== +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -21219,6 +21277,11 @@ mimic-response@^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== +mimic-response@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" + integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== + min-document@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" @@ -22009,6 +22072,14 @@ no-case@^2.2.0, no-case@^2.3.2: dependencies: lower-case "^1.1.1" +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + node-abi@^2.7.0: version "2.30.1" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.30.1.tgz#c437d4b1fe0e285aaf290d45b45d4d7afedac4cf" @@ -22372,6 +22443,11 @@ normalize-url@^6.0.1: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== +normalize-url@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.0.tgz#593dbd284f743e8dcf6a5ddf8fadff149c82701a" + integrity sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw== + npm-bundled@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" @@ -25655,6 +25731,13 @@ responselike@^2.0.0: dependencies: lowercase-keys "^2.0.0" +responselike@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" + integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== + dependencies: + lowercase-keys "^3.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -26235,6 +26318,15 @@ sentence-case@^2.1.0: no-case "^2.2.0" upper-case-first "^1.1.2" +sentence-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" + integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" + serialize-error@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" @@ -26682,17 +26774,10 @@ socket.io-adapter@~2.4.0: resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6" integrity "sha1-tQpKns3QDDTUyMgIIk2qGnhhUqY= sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==" -socket.io-adapter@~2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12" - integrity sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA== - dependencies: - ws "~8.11.0" - -socket.io-client@4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.1.3.tgz#236daa642a9f229932e00b7221e843bf74232a62" - integrity sha512-hISFn6PDpgDifVUiNklLHVPTMv1LAk8poHArfIUdXa+gKgbr0MZbAlquDFqCqsF30yBqa+jg42wgos2FK50BHA== +socket.io-client@4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.4.tgz#d3cde8a06a6250041ba7390f08d2468ccebc5ac9" + integrity sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g== dependencies: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.2" @@ -26742,15 +26827,19 @@ socket.io-parser@~4.2.1: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.1" -socket.io-parser@~4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" - integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== +socket.io@4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.4.tgz#a4513f06e87451c17013b8d13fdfaf8da5a86a90" + integrity sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ== dependencies: - "@socket.io/component-emitter" "~3.1.0" - debug "~4.3.1" + accepts "~1.3.4" + base64id "~2.0.0" + debug "~4.3.2" + engine.io "~6.2.1" + socket.io-adapter "~2.4.0" + socket.io-parser "~4.2.1" -socket.io@4.4.1, socket.io@^4.2.0: +socket.io@^4.2.0: version "4.4.1" resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.4.1.tgz#cd6de29e277a161d176832bb24f64ee045c56ab8" integrity sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg== @@ -26762,31 +26851,6 @@ socket.io@4.4.1, socket.io@^4.2.0: socket.io-adapter "~2.3.3" socket.io-parser "~4.0.4" -socket.io@4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.0.tgz#ae21460d5aef23b152d38de64d7c1798cd2d23fc" - integrity sha512-eOpu7oCNiPGBHn9Falg0cAGivp6TpDI3Yt596fbsf+vln8kRLFWxXKrecFrybn/xNYVn9HcdJNAkYToCmTjsyg== - dependencies: - accepts "~1.3.4" - base64id "~2.0.0" - cors "~2.8.5" - debug "~4.3.2" - engine.io "~6.5.0" - socket.io-adapter "~2.5.2" - socket.io-parser "~4.2.4" - -socket.io@^4.4.1: - version "4.5.0" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.0.tgz#78ae2e84784c29267086a416620c18ef95b37186" - integrity "sha1-eK4uhHhMKSZwhqQWYgwY75WzcYY= sha512-slTYqU2jCgMjXwresG8grhUi/cC6GjzmcfqArzaH3BN/9I/42eZk9yamNvZJdBfTubkjEdKAKs12NEztId+bUA==" - dependencies: - accepts "~1.3.4" - base64id "~2.0.0" - debug "~4.3.2" - engine.io "~6.2.0" - socket.io-adapter "~2.4.0" - socket.io-parser "~4.0.4" - socketio-wildcard@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/socketio-wildcard/-/socketio-wildcard-2.0.0.tgz#2466e832276b19163563bee772388747f912475b" @@ -27100,6 +27164,14 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +split-text-to-chunks@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/split-text-to-chunks/-/split-text-to-chunks-1.0.0.tgz#9b9bd2b8530e18b09697b1b8ca4485d31608eeb7" + integrity sha512-HLtEwXK/T4l7QZSJ/kOSsZC0o5e2Xg3GzKKFxm0ZexJXw0Bo4CaEl39l7MCSRHk9EOOL5jT8JIDjmhTtcoe6lQ== + dependencies: + get-stdin "^5.0.1" + minimist "^1.2.0" + split2@^3.0.0: version "3.2.2" resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" @@ -27771,6 +27843,14 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" +tablemark@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tablemark/-/tablemark-3.0.0.tgz#65708e4d8fb08a826b0e7e60fcab9b77f3a964d8" + integrity sha512-7N05gRK7t6B4g8AtedUsKoKtPjplmUjOPr/V4kVB+7U3yGiB3WvKqMTTQzVCZyhfZUXgQFp9YyN9ZgC52uCPKw== + dependencies: + sentence-case "^3.0.4" + split-text-to-chunks "^1.0.0" + tap-mocha-reporter@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/tap-mocha-reporter/-/tap-mocha-reporter-5.0.3.tgz#3e261b2a43092ba8bc0cb67a89b33e283decee05" @@ -28596,6 +28676,11 @@ tslib@^1.10.0, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.0.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" + integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== + tslib@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" @@ -29172,6 +29257,13 @@ upper-case-first@^1.1.0, upper-case-first@^1.1.2: dependencies: upper-case "^1.1.1" +upper-case-first@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" + integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== + dependencies: + tslib "^2.0.3" + upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" @@ -29453,6 +29545,11 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +visit-values@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/visit-values/-/visit-values-2.0.0.tgz#12b18b52e0238d1a541281d27591548309cf3de2" + integrity sha512-vLFU70y3D915d611GnHYeHkEmq6ZZETzTH4P1hM6I9E3lBwH2VeBBEESe/bGCY+gAyK0qqLFn5bNFpui/GKmww== + vm-browserify@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" @@ -31588,11 +31685,6 @@ ws@^8.4.2: resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== -ws@~8.11.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" - integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== - xdg-basedir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"